{"record":{"id":"d9852b1ecfcd8093","repo":"nats-io/nats-server","slug":"websocket-invalid-header-q-sec-websocket-pre","errorCode":null,"errorMessage":"websocket: invalid header %q, \"Sec-WebSocket-\" prefix not allowed","messagePattern":"websocket: invalid header %q, \"Sec-WebSocket-\" prefix not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/websocket.go","lineNumber":1191,"sourceCode":"\t}\n\tif err := validatePinnedCerts(wo.TLSPinnedCerts); err != nil {\n\t\treturn fmt.Errorf(\"websocket: %v\", err)\n\t}\n\n\t// Check for invalid headers here.\n\tfor key := range wo.Headers {\n\t\tk := strings.ToLower(key)\n\t\tswitch k {\n\t\tcase \"host\",\n\t\t\t\"content-length\",\n\t\t\t\"connection\",\n\t\t\t\"upgrade\",\n\t\t\t\"nats-no-masking\":\n\t\t\treturn fmt.Errorf(\"websocket: invalid header %q not allowed\", key)\n\t\t}\n\n\t\tif strings.HasPrefix(k, \"sec-websocket-\") {\n\t\t\treturn fmt.Errorf(\"websocket: invalid header %q, \\\"Sec-WebSocket-\\\" prefix not allowed\", key)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Creates or updates the existing map\nfunc (s *Server) wsSetOriginOptions(o *WebsocketOpts) {\n\tws := &s.websocket\n\tws.mu.Lock()\n\tdefer ws.mu.Unlock()\n\t// Copy over the option's same origin boolean\n\tws.sameOrigin = o.SameOrigin\n\t// Reset the map. Will help for config reload if/when we support it.\n\tws.allowedOrigins = nil\n\tif o.AllowedOrigins == nil {\n\t\treturn\n\t}","sourceCodeStart":1173,"sourceCodeEnd":1209,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/websocket.go#L1173-L1209","documentation":"Custom websocket headers may not use the Sec-WebSocket-* prefix because those headers are negotiated by the WebSocket handshake protocol itself (Sec-WebSocket-Key, Sec-WebSocket-Accept, extensions, protocol, version). Allowing overrides would corrupt the handshake, so validation rejects any key starting with sec-websocket- (case-insensitive).","triggerScenarios":"websocket { headers: { \"Sec-WebSocket-Protocol\": \"chat\" } } or any header whose lowercase form begins with \"sec-websocket-\".","commonSituations":"Trying to force a subprotocol or WebSocket version via config headers instead of client-side options; copying request headers from a browser capture into server config.","solutions":["Remove the Sec-WebSocket-* header from websocket.headers","Configure subprotocols on the client library (e.g. WebSocket constructor protocols argument), not server headers","Rename the header to a non-reserved name if it is genuinely custom"],"exampleFix":"// before\nwebsocket { headers: { \"Sec-WebSocket-Protocol\": \"chat\" } }\n// after\nwebsocket { headers: { \"X-App-Protocol\": \"chat\" } }","handlingStrategy":"validation","validationCode":"for key := range opts.Websocket.Headers {\n  if strings.HasPrefix(strings.ToLower(key), \"sec-websocket-\") {\n    return fmt.Errorf(\"header %q uses reserved Sec-WebSocket- prefix\", key)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never mirror browser request headers into server handshake config","Set subprotocols/versions on the client, not via server headers"],"tags":["websocket","headers","config-validation"],"backgroundTag":"reserved-http-header","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}