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

  1. Remove the offending header key from the websocket.headers map
  2. Set Connection/Upgrade/Host behavior at the reverse proxy instead of in nats-server config
  3. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/c0eb98110be23348. Report an issue: GitHub.