netbirdio/netbird · error
skip_tls_verify is not supported for L4 services
Error message
skip_tls_verify is not supported for L4 services
What it means
Returned by validateL4Target when target.options.skip_tls_verify is true. That flag instructs the HTTP proxy's TLS client to not verify the upstream certificate when the proxy itself speaks HTTPS to the backend; in tcp/udp/tls modes the proxy never terminates or originates TLS - it relays encrypted bytes - so there is no handshake it could skip verification on. Self-signed upstream certificates are therefore irrelevant (and safe) for L4 services.
Source
Thrown at management/internals/modules/reverseproxy/service/service.go:1066
return errors.New("target host is required for subnet targets")
}
case TargetTypeCluster:
// target_id carries the cluster address; the proxy resolves
// the upstream at request time.
default:
return fmt.Errorf("invalid target_type %q for L4 service", target.TargetType)
}
if target.Path != nil && *target.Path != "" && *target.Path != "/" {
return errors.New("path is not supported for L4 services")
}
if target.Options.SessionIdleTimeout < 0 {
return errors.New("session_idle_timeout must be positive for L4 services")
}
if target.Options.RequestTimeout < 0 {
return errors.New("request_timeout must be positive for L4 services")
}
if target.Options.SkipTLSVerify {
return errors.New("skip_tls_verify is not supported for L4 services")
}
if target.Options.PathRewrite != "" {
return errors.New("path_rewrite is not supported for L4 services")
}
if len(target.Options.CustomHeaders) > 0 {
return errors.New("custom_headers is not supported for L4 services")
}
return nil
}
// Service mode constants.
const (
ModeHTTP = "http"
ModeTCP = "tcp"
ModeUDP = "udp"
ModeTLS = "tls"
)
View on GitHub (pinned to 93e97f4bf1)
Solutions
- Set skip_tls_verify to false (or omit it) on the L4 target - end-to-end TLS means clients do the verifying.
- If the proxy must terminate TLS and skip upstream verification, use mode http with target_protocol https.
- Install the CA on the connecting clients instead if the passthrough cert is private.
Example fix
// before
{ "mode": "tls", "targets": [ { "target_id": "peer-a", "port": 443,
"options": { "skip_tls_verify": true } } ] }
// after
{ "mode": "tls", "targets": [ { "target_id": "peer-a", "port": 443 } ] } Defensive patterns
Strategy: validation
Validate before calling
func checkL4NoSkipTLSVerify(o TargetOptions) error {
if o.SkipTLSVerify {
return errors.New("skip_tls_verify is http-only; remove it from L4 targets")
}
return nil
} Type guard
func isL4SkipTLSVerifyClean(o TargetOptions) bool {
return !o.SkipTLSVerify
} Try / catch
if err := svc.Validate(); err != nil {
if strings.Contains(err.Error(), "skip_tls_verify is not supported for L4") {
return respondBadRequest(errors.New("passthrough TLS is end-to-end; clients verify the cert"))
}
return respondBadRequest(err)
} Prevention
- Understand which side terminates TLS: proxy (http mode) vs client (tls/tcp passthrough).
- Only copy skip_tls_verify onto targets whose protocol the proxy itself speaks.
- For private CAs on passthrough, distribute the CA to clients instead of touching this flag.
When it happens
Trigger: An L4 target whose options block carries skip_tls_verify: true, typically copied from an HTTPS HTTP-mode target that had a self-signed cert; flipping a service from http (https target_protocol) to tls passthrough without clearing the flag.
Common situations: 'My upstream uses a self-signed cert, better set skip_tls_verify' applied to a passthrough where the client, not the proxy, validates the cert. Template reuse across modes. Tooling that copies the whole options block verbatim.
Related errors
- auth is not supported for TLS services
- TLS services must have exactly one target
- path_rewrite is not supported for L4 services
- custom_headers is not supported for L4 services
- domain is required for TCP/UDP services (used for cluster de
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/ffb662e3b121ae7c.
Report an issue: GitHub.