{"record":{"id":"9cda1592160e8466","repo":"gorilla/websocket","slug":"http-statustext-status","errorCode":null,"errorMessage":"http.StatusText(status)","messagePattern":"http\\.StatusText\\(status\\)","errorType":"http","errorClass":"HandshakeError","httpStatus":null,"severity":"error","filePath":"server.go","lineNumber":82,"sourceCode":"\t//\n\t// A CheckOrigin function should carefully validate the request origin to\n\t// prevent cross-site request forgery.\n\tCheckOrigin func(r *http.Request) bool\n\n\t// EnableCompression specify if the server should attempt to negotiate per\n\t// message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n}\n\nfunc (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {\n\terr := HandshakeError{reason}\n\tif u.Error != nil {\n\t\tu.Error(w, r, status, err)\n\t} else {\n\t\tw.Header().Set(\"Sec-Websocket-Version\", \"13\")\n\t\thttp.Error(w, http.StatusText(status), status)\n\t}\n\treturn nil, err\n}\n\n// checkSameOrigin returns true if the origin is not set or is equal to the request host.\nfunc checkSameOrigin(r *http.Request) bool {\n\torigin := r.Header[\"Origin\"]\n\tif len(origin) == 0 {\n\t\treturn true\n\t}\n\tu, err := url.Parse(origin[0])\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn equalASCIIFold(u.Host, r.Host)\n}\n\nfunc (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/gorilla/websocket/blob/e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f/server.go#L64-L100","documentation":"returnError is the Upgrader's error path during a failed WebSocket handshake. It writes an HTTP error response (with Sec-Websocket-Version: 13 header) whose body is http.StatusText(status), and returns a HandshakeError carrying the same reason to the caller. The developer sees this when an incoming request fails upgrade validation — the reason string is typically an HTTP status text such as 'Method Not Allowed', 'Bad Request', or 'Unauthorized'.","triggerScenarios":"Upgrade() rejects a request: non-GET method, missing/invalid Upgrade or Connection headers, missing Sec-WebSocket-Key, unsupported Sec-WebSocket-Version, CheckOrigin returning false, or Subprotocol mismatch — each maps to a status whose StatusText becomes the error reason.","commonSituations":"Hitting the WS endpoint with a plain browser GET or curl without Upgrade headers; misconfigured reverse proxy/load balancer stripping Upgrade and Connection headers; CheckOrigin rejecting a legitimate cross-origin frontend (different host during development); client sending Sec-WebSocket-Version other than 13; accessing via wrong scheme without TLS termination.","solutions":["Read the HandshakeError reason / returned status to see which validation failed (method, headers, version, origin, subprotocol)","Ensure the client performs a real WS upgrade (use a WebSocket client, not plain fetch/curl without headers: Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key)","If behind nginx/HAProxy, forward upgrade headers: proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \"upgrade\"","Relax or fix CheckOrigin to accept your frontend's Origin, instead of relying on the default same-origin check","Ensure the client requests version 13 and, if using subprotocols, that the server lists a matching one"],"exampleFix":"// before (default same-origin check rejects cross-origin client)\nvar upgrader = websocket.Upgrader{}\nconn, err := upgrader.Upgrade(w, r, nil) // err: \"websocket: request origin not allowed by Upgrader.CheckOrigin\"\n// after: accept known origins\nvar upgrader = websocket.Upgrader{\n    CheckOrigin: func(r *http.Request) bool {\n        origin := r.Header.Get(\"Origin\")\n        return origin == \"https://app.example.com\" || origin == \"\"\n    },\n}\nconn, err := upgrader.Upgrade(w, r, nil)","handlingStrategy":"try-catch","validationCode":"func canUpgrade(r *http.Request) error {\n    if r.Method != http.MethodGet {\n        return fmt.Errorf(\"need GET, got %s\", r.Method)\n    }\n    if !strings.EqualFold(r.Header.Get(\"Upgrade\"), \"websocket\") {\n        return errors.New(\"missing/invalid Upgrade header\")\n    }\n    if !tokenListContains(r.Header.Get(\"Connection\"), \"upgrade\") {\n        return errors.New(\"missing Connection: Upgrade\")\n    }\n    if r.Header.Get(\"Sec-Websocket-Key\") == \"\" {\n        return errors.New(\"missing Sec-WebSocket-Key\")\n    }\n    if r.Header.Get(\"Sec-Websocket-Version\") != \"13\" {\n        return errors.New(\"unsupported websocket version\")\n    }\n    return nil\n}","typeGuard":"func isHandshakeError(err error) bool {\n    _, ok := err.(websocket.HandshakeError)\n    return ok\n}","tryCatchPattern":"conn, err := upgrader.Upgrade(w, r, nil)\nif err != nil {\n    if isHandshakeError(err) {\n        // Upgrade already wrote the HTTP error response; just log and return\n        log.Printf(\"handshake rejected from %s: %v\", r.RemoteAddr, err)\n        return\n    }\n    http.Error(w, \"internal error\", http.StatusInternalServerError)\n    return\n}","preventionTips":["Always check errors from Upgrade and do not write another response afterwards — it already wrote one","Set an explicit CheckOrigin matching your deployed origins instead of relying on same-origin defaults","Configure reverse proxies (nginx/HAProxy/ALB) to forward Upgrade/Connection headers","Test the endpoint with a real WebSocket client (wscat, browser) not plain HTTP requests","If you set Upgrader.Error, make sure it writes a response so clients are not left hanging"],"tags":["websocket","handshake","http","upgrade","server"],"backgroundTag":"websocket-handshake-rejected","analyzedSha":"e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f","analyzedAt":"2026-08-31T12:40:58.222Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}