Bosle Clément
02/22/2026, 11:37 AMwebsockets.exceptions.InvalidStatus: server rejected WebSocket connection: HTTP 403 when self hosting my prefect server using docker compose : prefect.server.utilities.subscriptions - WebSocket connection rejected: 'prefect' subprotocol required when auth is configuredMarvin
02/22/2026, 11:37 AMMarvin
02/22/2026, 11:40 AMPREFECT_SERVER_API_AUTH_STRING), all WebSocket connections must:
- Include the Sec-WebSocket-Protocol: prefect subprotocol on the initial handshake
- Perform a small auth handshake once connected
- The official Prefect UI and Python clients do send this subprotocol automatically. When this error appears, it’s almost always because a reverse proxy (e.g., nginx/Traefik) between the client and your server is not forwarding the Sec-WebSocket-Protocol header.
How to fix
1) If you’re using a reverse proxy, ensure it preserves the WebSocket headers, especially Sec-WebSocket-Protocol.
- nginx example (critical lines annotated):
location / {
proxy_pass <http://prefect_server>; # your upstream
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# CRITICAL: make sure these WS headers are forwarded
proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
# Optional to avoid timeouts on long-lived WS
proxy_read_timeout 7d;
proxy_send_timeout 7d;
}
- Traefik generally preserves WS headers by default. Make sure you are not using a headers middleware that overwrites or drops Sec-WebSocket-Protocol, and that you are not terminating/rewriting the WS route in a way that strips it.
2) Make sure both sides have matching auth strings
- Server: set in your server container
PREFECT_SERVER_API_AUTH_STRING="user:password"
- Clients (agents, workers, any Python clients): set
PREFECT_API_AUTH_STRING="user:password"
These must match exactly.
3) Quick isolation tests
- Bypass the proxy temporarily: point PREFECT_API_URL at the server container’s host:port directly and try again. If it works direct but fails via proxy, it’s the header forwarding.
- Use a WS client to verify the subprotocol reaches the server:
wscat -c <wss://your-domain.example.com/api/>... -H "Sec-WebSocket-Protocol: prefect"
If the server accepts with auth enabled, the subprotocol made it through.
References (source)
- Server check that enforces the subprotocol when auth is enabled:
subscriptions.py
- Python clients sending the “prefect” subprotocol:
events/clients.py
- Server-side API auth setting:
settings/models/server/api.py
If you can share:
- Your docker-compose for the server
- Any nginx/Traefik (or other proxy) config in front of Prefect
- Whether you set PREFECT_SERVER_API_AUTH_STRING and where your clients run
…I can spot-check and suggest exact edits for your setup.