{"record":{"id":"e9af304b891fae8a","repo":"gorilla/websocket","slug":"websocket-duplicate-header-not-allowed","errorCode":null,"errorMessage":"websocket: duplicate header not allowed: ","messagePattern":"websocket: duplicate header not allowed: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client.go","lineNumber":245,"sourceCode":"\treq.Header[\"Connection\"] = []string{\"Upgrade\"}\n\treq.Header[\"Sec-WebSocket-Key\"] = []string{challengeKey}\n\treq.Header[\"Sec-WebSocket-Version\"] = []string{\"13\"}\n\tif len(d.Subprotocols) > 0 {\n\t\treq.Header[\"Sec-WebSocket-Protocol\"] = []string{strings.Join(d.Subprotocols, \", \")}\n\t}\n\tfor k, vs := range requestHeader {\n\t\tswitch {\n\t\tcase k == \"Host\":\n\t\t\tif len(vs) > 0 {\n\t\t\t\treq.Host = vs[0]\n\t\t\t}\n\t\tcase k == \"Upgrade\" ||\n\t\t\tk == \"Connection\" ||\n\t\t\tk == \"Sec-Websocket-Key\" ||\n\t\t\tk == \"Sec-Websocket-Version\" ||\n\t\t\tk == \"Sec-Websocket-Extensions\" ||\n\t\t\t(k == \"Sec-Websocket-Protocol\" && len(d.Subprotocols) > 0):\n\t\t\treturn nil, nil, errors.New(\"websocket: duplicate header not allowed: \" + k)\n\t\tcase k == \"Sec-Websocket-Protocol\":\n\t\t\treq.Header[\"Sec-WebSocket-Protocol\"] = vs\n\t\tdefault:\n\t\t\treq.Header[k] = vs\n\t\t}\n\t}\n\n\tif d.EnableCompression {\n\t\treq.Header[\"Sec-WebSocket-Extensions\"] = []string{\"permessage-deflate; server_no_context_takeover; client_no_context_takeover\"}\n\t}\n\n\tif d.HandshakeTimeout != 0 {\n\t\tvar cancel func()\n\t\tctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)\n\t\tdefer cancel()\n\t}\n\n\tvar proxyURL *url.URL","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/gorilla/websocket/blob/e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f/client.go#L227-L263","documentation":"DialContext rejects user-supplied request headers that would conflict with the headers the library must set itself for the WebSocket handshake: Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Extensions, and Sec-WebSocket-Protocol (when Subprotocols is configured on the dialer). Supplying any of these as a duplicate is rejected with this error naming the offending header key.","triggerScenarios":"Passing one of the protected headers in requestHeader to Dial/DialContext — e.g. requestHeader.Set(\"Sec-WebSocket-Protocol\", \"chat\") while dialer.Subprotocols is non-empty, or manually setting \"Connection: Upgrade\" or \"Sec-WebSocket-Version: 13\".","commonSituations":"Porting code from raw net/http clients where those headers were set manually, copying browser examples that set Upgrade/Connection headers, and specifying Sec-WebSocket-Protocol both via dialer.Subprotocols and via request header.","solutions":["Remove the protected header from requestHeader and configure it via the dialer instead (e.g. d.Subprotocols for Sec-WebSocket-Protocol)","Set Sec-WebSocket-Protocol ONLY through Dialer.Subprotocols when Subprotocols is used","If you need extensions/keys, let the library generate them — do not set Sec-WebSocket-Extensions, Key, Version, Upgrade, or Connection manually","Keep application-specific headers (Authorization, Cookie, Origin) in requestHeader — those are allowed"],"exampleFix":"// before\ndialer := websocket.Dialer{Subprotocols: []string{\"chat\"}}\nheader := http.Header{}\nheader.Set(\"Sec-WebSocket-Protocol\", \"chat\")\nconn, _, err := dialer.Dial(url, header)\n// after\ndialer := websocket.Dialer{Subprotocols: []string{\"chat\"}}\nconn, _, err := dialer.Dial(url, nil)","handlingStrategy":"validation","validationCode":"var forbidden = map[string]bool{\n    \"Upgrade\": true, \"Connection\": true,\n    \"Sec-Websocket-Key\": true, \"Sec-Websocket-Version\": true,\n    \"Sec-Websocket-Extensions\": true,\n}\nfor k := range header {\n    if forbidden[http.CanonicalHeaderKey(k)] || k == \"Sec-Websocket-Protocol\" && len(dialer.Subprotocols) > 0 {\n        return fmt.Errorf(\"header %q is managed by the websocket dialer\", k)\n    }\n}","typeGuard":null,"tryCatchPattern":"conn, resp, err := dialer.DialContext(ctx, url, header)\nif err != nil && strings.HasPrefix(err.Error(), \"websocket: duplicate header not allowed\") {\n    return fmt.Errorf(\"remove the protected header from requestHeader: %w\", err)\n}","preventionTips":["Set subprotocols only via Dialer.Subprotocols, never both ways","Audit header maps built for net/http before reusing them for websocket dials","Let the library own Upgrade, Connection, Key, Version, Extensions headers"],"tags":["websocket","headers","configuration"],"backgroundTag":"duplicate-header-not-allowed","analyzedSha":"e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f","analyzedAt":"2026-08-31T12:40:58.222Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}