HTTP status code · Client errors (4xx)
426 Upgrade Required
426 Upgrade Required means the server refuses to handle the request over the current protocol but will after the client switches to the one listed in the Upgrade header. The most common cause is opening a WebSocket URL with a normal HTTP request, or a proxy that drops the Upgrade headers.
| Class | 4xx, Client errors |
|---|---|
| Defined in | RFC 9110 §15.5.22 |
| Cacheable by default | Only with explicit Cache-Control or Expires |
| Safe to retry | Yes, over the protocol named in the Upgrade header |
| Relevant headers |
|
What 426 means
RFC 9110, section 15.5.22, requires a 426 response to include an Upgrade header listing the acceptable protocols, and its own example asks for HTTP/3.0. In real traffic the protocol is almost always websocket: the Node.js ws library, for instance, answers a plain HTTP request to its standalone server with 426.
A WebSocket connection starts as an HTTP/1.1 GET with Upgrade: websocket and Connection: Upgrade. If you paste the endpoint into a browser tab, or a reverse proxy forwards the request without those two headers, the server sees ordinary HTTP and replies 426.
Common causes
If you are visiting the site
- You opened a WebSocket address (often something like /ws or /socket) directly in the browser address bar.
- A corporate proxy or antivirus strips the Upgrade header from outgoing requests.
If you run the server
- nginx in front of the app proxies /ws without proxy_http_version 1.1 and the Upgrade and Connection headers, so the backend never sees the handshake.
- A load balancer or platform with WebSocket support disabled for that route.
- A health check or monitoring probe that sends plain GET to the WebSocket port.
How to fix it
If you are visiting the site
- Open the page that uses the socket, not the socket URL itself; the page opens the connection for you.
- If a web app fails only on one network, try another network or disable the filtering proxy.
If you run the server
- In nginx, add proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; to the WebSocket location.
- Point health checks at a normal HTTP route instead of the socket endpoint.
- Test the handshake directly: curl -i -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" https://example.com/ws should return 101.
How to send 426
app.get('/ws', (req, res) => {
res.set('Upgrade', 'websocket');
res.set('Connection', 'Upgrade');
res.status(426).json({ error: 'This endpoint only accepts WebSocket connections' });
});// app/ws/route.ts
export async function GET() {
return Response.json(
{ error: 'This endpoint only accepts WebSocket connections' },
{ status: 426, headers: { 'Upgrade': 'websocket', 'Connection': 'Upgrade' } }
);
}mux.HandleFunc("GET /ws", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Upgrade", "websocket")
w.Header().Set("Connection", "Upgrade")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUpgradeRequired) // 426
w.Write([]byte(`{"error":"This endpoint only accepts WebSocket connections"}`))
})from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/ws")
def websocket_only():
raise HTTPException(status_code=426, detail="This endpoint only accepts WebSocket connections", headers={"Upgrade": "websocket", "Connection": "Upgrade"})location /ws {
if ($http_upgrade !~* ^websocket$) {
add_header Upgrade websocket always;
return 426;
}
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}Commonly confused with
- 426 vs 101
- 101 Switching Protocols is the success answer to an upgrade request; 426 is the refusal you get when you did not ask for the upgrade.
- 426 vs 505
- 505 rejects the HTTP version in the request line; 426 accepts the request but wants a different protocol named in Upgrade.
Frequently asked questions
- Why do I get 426 Upgrade Required from a WebSocket server?
- The request reached the server without the Upgrade: websocket and Connection: Upgrade headers, so it looked like plain HTTP. Either something opened the URL as a web page, or a proxy in between removed those headers.
- Can 426 be used to force HTTPS?
- RFC 2817 described upgrading HTTP to TLS with Upgrade and 426, but browsers never implemented it. To move visitors to HTTPS, use a 301 or 308 redirect and an HSTS header.
- What header must a 426 response include?
- Upgrade, listing the protocol or protocols the server will accept, for example Upgrade: websocket. RFC 9110 makes it mandatory, and it usually goes with Connection: Upgrade.
Last reviewed by Arielton Oberek.