{"record":{"id":"009b89a3c3ce9a96","repo":"fatedier/frp","slug":"non-tls-connection-received-on-a-tlsonly-server","errorCode":null,"errorMessage":"non-TLS connection received on a TlsOnly server","messagePattern":"non-TLS connection received on a TlsOnly server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/net/tls.go","lineNumber":51,"sourceCode":"\tvar n int\n\t_ = c.SetReadDeadline(time.Now().Add(timeout))\n\tn, err = r.Read(buf)\n\t_ = c.SetReadDeadline(time.Time{})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tswitch {\n\tcase n == 1 && int(buf[0]) == FRPTLSHeadByte:\n\t\tout = tls.Server(c, tlsConfig)\n\t\tisTLS = true\n\t\tcustom = true\n\tcase n == 1 && int(buf[0]) == 0x16:\n\t\tout = tls.Server(sc, tlsConfig)\n\t\tisTLS = true\n\tdefault:\n\t\tif tlsOnly {\n\t\t\terr = fmt.Errorf(\"non-TLS connection received on a TlsOnly server\")\n\t\t\treturn\n\t\t}\n\t\tout = sc\n\t}\n\treturn\n}\n","sourceCodeStart":33,"sourceCodeEnd":58,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/util/net/tls.go#L33-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# frpc.toml — before\n[transport]\ntls.enable = false\n\n# after (required when frps sets tlsOnly = true)\n[transport]\ntls.enable = true","handlingStrategy":"validation","validationCode":"# client-side preflight: assert TLS is on before pointing frpc at a tlsOnly frps\ngrep -q '^tlsEnable = true\\|^\\[transport\\]' frpc.ini || echo \"WARNING: TLS not enabled; frps may run with tlsOnly\"","typeGuard":null,"tryCatchPattern":"conn, isTLS, _, err := netpkg.CheckAndEnableTLSServerConnWithTimeout(c, tlsCfg, true, timeout)\nif err != nil {\n    if strings.Contains(err.Error(), \"non-TLS connection\") {\n        c.Close() // reject and log the peer address for cleanup\n        log.Warnf(\"plaintext conn rejected from %s\", c.RemoteAddr())\n    }\n    return\n}","preventionTips":["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."],"tags":["tls","security","configuration","protocol-detection"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}