{"record":{"id":"354f815444c0fd98","repo":"netbirdio/netbird","slug":"listen-port-is-required-for-tls-services","errorCode":null,"errorMessage":"listen_port is required for TLS services","messagePattern":"listen_port is required for TLS services","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"management/internals/modules/reverseproxy/service/service.go","lineNumber":944,"sourceCode":"\t}\n\tif len(s.Targets) != 1 {\n\t\treturn errors.New(\"TCP/UDP services must have exactly one target\")\n\t}\n\tif s.Mode == ModeUDP && s.Targets[0].ProxyProtocol {\n\t\treturn errors.New(\"proxy_protocol is not supported for UDP services\")\n\t}\n\treturn s.validateL4Target(s.Targets[0])\n}\n\nfunc (s *Service) validateTLSMode() error {\n\tif s.Domain == \"\" {\n\t\treturn errors.New(\"domain is required for TLS services (used for SNI matching)\")\n\t}\n\tif s.isAuthEnabled() {\n\t\treturn errors.New(\"auth is not supported for TLS services\")\n\t}\n\tif s.ListenPort == 0 {\n\t\treturn errors.New(\"listen_port is required for TLS services\")\n\t}\n\tif len(s.Targets) != 1 {\n\t\treturn errors.New(\"TLS services must have exactly one target\")\n\t}\n\treturn s.validateL4Target(s.Targets[0])\n}\n\nfunc (s *Service) validateHTTPTargets() error {\n\tfor i, target := range s.Targets {\n\t\tswitch target.TargetType {\n\t\tcase TargetTypePeer, TargetTypeHost, TargetTypeDomain:\n\t\t\t// Host is normally overwritten by replaceHostByLookup with the\n\t\t\t// resolved peer IP / resource address; operator-supplied values\n\t\t\t// are honored only when DirectUpstream is set. Validate the\n\t\t\t// override here so misconfigured hosts fail fast at API time.\n\t\t\tif err := validateDirectUpstreamHost(i, target); err != nil {\n\t\t\t\treturn err\n\t\t\t}","sourceCodeStart":926,"sourceCodeEnd":962,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/management/internals/modules/reverseproxy/service/service.go#L926-L962","documentation":"Returned by validateTLSMode when a tls service has ListenPort == 0. Unlike HTTP (shared hostname-routed listeners) and unlike tcp/udp (where the port allocation is implied by the mode), a TLS service must declare the dedicated port it listens on so the proxy can accept raw TLS and dispatch by SNI. It is the inverse of error 40, which rejects a listen_port on http services.","triggerScenarios":"Creating a tls service with no listen_port in the payload; an API client that maps its generic 'port' field to the target port but never to listen_port; switching mode from http to tls on a service that legitimately had no listen port.","commonSituations":"Assuming TLS shares the standard 443 listener via SNI so no port is needed - this implementation requires an explicit dedicated port. Confusion between target.port (upstream) and listen_port (proxy listener). In the peer-expose request the fields are Port and ListenPort, and only ListenPort overrides the listener for L4.","solutions":["Set listen_port to the port the proxy should accept TLS on, e.g. 4433 or 853.","Double-check you set listen_port (service-level listener), not target.port (upstream port).","If you wanted hostname routing without a dedicated port, use mode http with an https target_protocol instead."],"exampleFix":"// before\n{ \"mode\": \"tls\", \"domain\": \"db.netbird.example.com\",\n  \"targets\": [ { \"target_id\": \"peer-a\", \"port\": 5432 } ] }\n\n// after\n{ \"mode\": \"tls\", \"domain\": \"db.netbird.example.com\", \"listen_port\": 4433,\n  \"targets\": [ { \"target_id\": \"peer-a\", \"port\": 5432 } ] }","handlingStrategy":"validation","validationCode":"func checkTLSListenPort(mode string, listenPort uint16) error {\n\tif mode == \"tls\" && listenPort == 0 {\n\t\treturn errors.New(\"listen_port is required for tls services\")\n\t}\n\treturn nil\n}","typeGuard":"func hasTLSListenPort(mode string, listenPort uint16) bool {\n\treturn mode != \"tls\" || listenPort != 0\n}","tryCatchPattern":"if err := svc.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"listen_port is required for TLS\") {\n\t\treturn respondBadRequest(errors.New(\"set an explicit proxy listener port, e.g. 4433\"))\n\t}\n\treturn respondBadRequest(err)\n}","preventionTips":["Name fields clearly in clients: listener port vs upstream target port.","Do not assume SNI sharing on 443 - this implementation wants a dedicated listen_port for tls.","Check port availability on the proxy host before choosing a value."],"tags":["netbird","reverse-proxy","validation","tls","port","configuration","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}