fatedier/frp · error
non-TLS connection received on a TlsOnly server
Error message
non-TLS connection received on a TlsOnly server
What it means
CheckAndEnableTLSServerConnWithTimeout peeks the first byte of each inbound connection to classify it: 0x17 (frp's custom TLS marker byte) and 0x16 (standard TLS ClientHello) select TLS, anything else is plaintext. When tlsOnly is true (frps tlsOnly = true in the transport config) and the byte matches neither, the connection is rejected with this error instead of being served as plaintext.
Source
Thrown at pkg/util/net/tls.go:51
var n int
_ = c.SetReadDeadline(time.Now().Add(timeout))
n, err = r.Read(buf)
_ = c.SetReadDeadline(time.Time{})
if err != nil {
return
}
switch {
case n == 1 && int(buf[0]) == FRPTLSHeadByte:
out = tls.Server(c, tlsConfig)
isTLS = true
custom = true
case n == 1 && int(buf[0]) == 0x16:
out = tls.Server(sc, tlsConfig)
isTLS = true
default:
if tlsOnly {
err = fmt.Errorf("non-TLS connection received on a TlsOnly server")
return
}
out = sc
}
return
}
View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Enable TLS on every client: transport.tls.enable = true in frpc.toml (modern frpc enables TLS by default).
- Verify no legacy frpc (< v0.50 era defaults) or probe is hitting the bind port — check frps logs for the rejected remote address.
- If you must accept plaintext during migration, temporarily set frps tlsOnly = false, then re-enable after all clients are migrated.
- Point health checks at the dashboard/web port instead of the frp bind port.
Example fix
# frpc.toml — before [transport] tls.enable = false # after (required when frps sets tlsOnly = true) [transport] tls.enable = true
Defensive patterns
Strategy: validation
Validate before calling
# client-side preflight: assert TLS is on before pointing frpc at a tlsOnly frps grep -q '^tlsEnable = true\|^\[transport\]' frpc.ini || echo "WARNING: TLS not enabled; frps may run with tlsOnly"
Try / catch
conn, isTLS, _, err := netpkg.CheckAndEnableTLSServerConnWithTimeout(c, tlsCfg, true, timeout)
if err != nil {
if strings.Contains(err.Error(), "non-TLS connection") {
c.Close() // reject and log the peer address for cleanup
log.Warnf("plaintext conn rejected from %s", c.RemoteAddr())
}
return
} Prevention
- Enable transport.tls.enable = true on every frpc before turning on frps tlsOnly.
- Use modern frpc versions where TLS is the default.
- Point health checks and scanners at non-bind ports; monitor frps logs to find offending peers.
When it happens
Trigger: A plain-TCP frpc (transport.tls.enable = false) connecting to an frps configured with tlsOnly; a health check or port scanner sending arbitrary bytes; an old frpc version that does not support TLS; protocol confusion from connecting an SSH/HTTP client to the frp bind port.
Common situations: Hardening frps with tlsOnly=true while some clients still have TLS disabled; mixed fleet with old frpc versions; monitoring probes hitting the bind port with a bare TCP handshake.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- gen TLS config error: %v
- failed to parse CA certificate from file %q: no valid PEM ce
- ErrHealthCheckType
- unit not support
- type is required
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/009b89a3c3ce9a96.
Report an issue: GitHub.