nats-io/nats-server · error
websocket: invalid header %q not allowed
Error message
websocket: invalid header %q not allowed
What it means
The websocket options block defines custom headers to add to handshake responses, but one of them is in the forbidden set (host, content-length, connection, upgrade, nats-no-masking). These headers are controlled by the HTTP/WebSocket protocol layer and cannot be overridden, so the validator fails startup.
Source
Thrown at server/websocket.go:1187
if wo.JWTCookie != _EMPTY_ {
if len(o.TrustedOperators) == 0 && len(o.TrustedKeys) == 0 {
return fmt.Errorf("trusted operators or trusted keys configuration is required for JWT authentication via cookie %q", wo.JWTCookie)
}
}
if err := validatePinnedCerts(wo.TLSPinnedCerts); err != nil {
return fmt.Errorf("websocket: %v", err)
}
// Check for invalid headers here.
for key := range wo.Headers {
k := strings.ToLower(key)
switch k {
case "host",
"content-length",
"connection",
"upgrade",
"nats-no-masking":
return fmt.Errorf("websocket: invalid header %q not allowed", key)
}
if strings.HasPrefix(k, "sec-websocket-") {
return fmt.Errorf("websocket: invalid header %q, \"Sec-WebSocket-\" prefix not allowed", key)
}
}
return nil
}
// Creates or updates the existing map
func (s *Server) wsSetOriginOptions(o *WebsocketOpts) {
ws := &s.websocket
ws.mu.Lock()
defer ws.mu.Unlock()
// Copy over the option's same origin boolean
ws.sameOrigin = o.SameOrigin
// Reset the map. Will help for config reload if/when we support it.View on GitHub (pinned to 3a66a489d2)
Solutions
- Remove the offending header key from the websocket.headers map
- Set Connection/Upgrade/Host behavior at the reverse proxy instead of in nats-server config
- Use only application-safe custom headers (e.g. X-Forwarded-For style custom values are allowed)
Example fix
// before
websocket { headers: { "Connection": "keep-alive" } }
// after
websocket { headers: { "X-Custom-Header": "value" } } Defensive patterns
Strategy: validation
Validate before calling
for key := range opts.Websocket.Headers {
switch strings.ToLower(key) {
case "host", "content-length", "connection", "upgrade", "nats-no-masking":
return fmt.Errorf("reserved header %q not configurable", key)
}
} Prevention
- Only add application-specific headers in websocket.headers
- Handle hop-by-hop headers (Connection, Upgrade) at the proxy layer
When it happens
Trigger: websocket { headers: { "Connection": "keep-alive" } } or similarly setting Host/Content-Length/Upgrade/Nats-No-Masking in the websocket headers map.
Common situations: Operators trying to add CORS or proxy-related headers and accidentally including hop-by-hop headers like Connection; copying header sets from reverse-proxy configs.
Related errors
- websocket: invalid header %q, "Sec-WebSocket-" prefix not al
- remote leaf node URL %q cannot be used in FIPS-140 mode when
- websocket authentication username not compatible with presen
- websocket authentication token not compatible with presence
- trusted operators or trusted keys configuration is required
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/c0eb98110be23348.
Report an issue: GitHub.