FastAPI Depends()doesn't work with cookies in WebSocket routes.- Cookies are not automatically read in WebSocket contexts because of ASGI limits.
- You can manually read cookies and headers from
websocket.headers. - Cookie-based authentication for WebSockets needs you to strictly follow Secure and SameSite rules.
- You must manually change standard HTTP middleware and
Depends()patterns to make WebSockets secure.
FastAPI Cookie Not Working in WebSockets?
If your FastAPI cookie suddenly returns None in a FastAPI WebSocket route, you're not the only one confused. Many developers find this happens when they try to use authentication or session code that works well in regular HTTP routes. But then, it quietly fails in WebSockets. FastAPI handles WebSockets in a different way, and this changes how Depends() and cookie access work. Here, we will explain why this happens and show you how to fix it with clear methods that work for live systems.
FastAPI HTTP vs WebSocket Contexts
FastAPI uses the ASGI specification. This spec explains how async web apps talk to servers. For regular HTTP routes, FastAPI uses an HTTPConnection. This lets the Request object give you full access to cookies, headers, query parameters, and form data. This complete Request makes FastAPI features like Depends(), Cookie(), Header(), and Body() work smoothly.
But FastAPI WebSocket routes are quite different. A WebSocket connection starts with an HTTP handshake. Once the connection becomes a WebSocket, how it works changes. The WebSocket object in FastAPI follows the ASGI design and gives less information:
- It does not have built-in cookie parsing.
- It does not pass the
Requestobject. Depends()cannot get data fromCookie()orHeader()fields.
This core difference is why methods that work well in HTTP do not work in WebSockets.
Why Depends(Cookie(...)) Returns None in WebSockets
When you use a FastAPI HTTP route like this:
from fastapi import Cookie, Depends, HTTPException
def get_user_id(session_id: str = Cookie(default=None)):
if not session_id:
raise HTTPException(status_code=403, detail="Not authenticated")
return session_id
This dependency works on its own. This is because FastAPI connects the Request context and reads the cookie before anything else.
But try to use the same dependency in a WebSocket route:
from fastapi import Cookie, WebSocket, Depends
async def get_user_id(session_id: str = Cookie(default=None)):
return session_id
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, user_id: str = Depends(get_user_id)):
...
The user_id here will always be None. This happens no matter what cookies are sent. And this is because the Depends() system is not set up to pull cookie values from the WebSocket object. FastAPI just skips this dependency injection when it cannot find the needed parameters.
This is not a bug in the code. Instead, it is a limit of the ASGI WebSocket scope and how it's designed. Once the connection upgrades, the WebSocket does not have all the helpful features of the HTTP layer.
Reading Cookies from WebSocket.headers Manually
To get around these limits, you can manually read cookies from websocket.headers. The headers are still there because the first handshake request includes them.
Here’s a helper function you can use again:
from http.cookies import SimpleCookie
from fastapi import WebSocket
def get_cookie_from_ws(websocket: WebSocket, cookie_name: str) -> str | None:
cookie_header = websocket.headers.get("cookie")
if not cookie_header:
return None
cookie = SimpleCookie()
cookie.load(cookie_header)
return cookie.get(cookie_name).value if cookie_name in cookie else None
This code does this safely:
- It looks for the
cookieheader. - It reads it using Python’s
http.cookies.SimpleCookie. - And then it gives back the cookie value you want, or
None.
This method gives you a solid and secure way to get cookies. You can also use it for many cookie values, JWT reading, or CSRF defense.
Use it like this:
@app.websocket("/ws")
async def websocket_auth(websocket: WebSocket):
await websocket.accept()
session_id = get_cookie_from_ws(websocket, "session_id")
if not session_id:
await websocket.close(code=1008) # Policy Violation
return
await websocket.send_text(f"Session OK: {session_id}")
Cookie-Based vs Token-Based Auth for WebSockets
WebSocket authentication usually comes in two main types. Each has good and bad points:
1. Cookie-Based Authentication
This choice is good if you already use cookies for login or session management:
@app.websocket("/ws")
async def cookie_auth_ws(websocket: WebSocket):
await websocket.accept()
session_id = get_cookie_from_ws(websocket, "session_id")
if not session_id or not valid_session(session_id):
await websocket.close(code=1008)
return
await websocket.send_text("Authenticated by cookie.")
Good points:
- No changes needed for users who save sessions in their browser.
- Works with existing login systems.
Bad points:
- Cookies only get sent if the domain, path, and secure flags match.
- Needs WSS and correct Cross-Origin settings.
2. Token in Query Parameter or Header
Many real-time apps choose to add a token by hand. This can be in the query string or header during the handshake:
new WebSocket("wss://yourapp.com/ws?token=abcd1234");
@app.websocket("/ws")
async def token_auth_ws(websocket: WebSocket):
token = websocket.query_params.get("token")
if not token or not validate_token(token):
await websocket.close(code=1008)
return
await websocket.accept()
await websocket.send_text("Authenticated via token.")
Good points:
- Simple to check and handle.
- Works with any framework.
Bad points:
- A little less secure if URLs are saved in logs.
- Tokens might need to be short-lived and changed often.
Manually Handling Dependencies in WebSocket Routes
FastAPI Depends does not fully support dependency injection in WebSocket routes. So, you can make the same logic work using separate helper functions.
Here’s a way to do manual injection that you can use again:
async def authenticate_session(websocket: WebSocket):
session_id = get_cookie_from_ws(websocket, "session_id")
if not session_id or not valid_session(session_id):
await websocket.close(code=1008)
@app.websocket("/ws")
async def websocket_with_auth(websocket: WebSocket):
await websocket.accept()
await authenticate_session(websocket)
await websocket.send_text("Connected!")
This method keeps many of the good things from using Depends() in a structured way:
- Your code is in smaller, separate parts.
- Your authentication logic is in one place.
- It is easy to test.
You can also use decorators to handle authorization logic without repeating yourself.
Be Careful: Middleware and WebSockets
Regular FastAPI Middleware only works for HTTP requests, not WebSocket connections.
Why is this? Middleware in FastAPI uses Starlette’s BaseHTTPMiddleware. This processes requests before any route code runs. But once a WebSocket changes from HTTP, this middleware stops being involved.
This means:
- Changing cookies or sessions in HTTP middleware will not affect WebSockets.
- You must write CORS, rate-limiting, or audit middleware again for WebSocket paths.
Here are some ways to handle this:
- Use custom ASGI middleware (this is more complex).
- Put your route logic inside your own decorator.
- Or, check headers inside the WebSocket route, as we talked about.
Note this small but very important difference in how things are set up when you run large apps.
Secure WebSocket Cookies: HTTPS and WSS Best Practices
Security for cookies is very important with FastAPI WebSocket. Browsers make sure several rules are followed about which cookies are sent and when:
-
Secure Attribute: Cookies need the
Secureflag to be sent overwss://. If they don't have it, cookies are blocked when your app is live. -
SameSite Policy:
SameSite=LaxorStrictcan stop cookies from working in cross-site WebSocket setups.- Use
SameSite=NoneandSecurefor cross-origin setups.
-
Subdomains:
- Cookies set on a main domain (
.example.com) are not certain to be sent tows.sub.example.comunless the path and domain are the same.
- Cookies set on a main domain (
-
HttpOnly Flag:
- Cookies with the
HttpOnlymark cannot be reached by JavaScript. But they are still sent with WebSocket upgrade requests.
- Cookies with the
Key good practices:
- Run your app on
https://and your WebSocket onwss://. - Set your session cookies like this:
Set-Cookie: session_id=ABC123; HttpOnly; Secure; SameSite=None; Path=/
Helping the Frontend Send Cookies Over WebSocket
Know how browsers act when using the WebSocket API:
- All cookies that match the domain, path, and flag rules are sent with the first GET request (the handshake).
- You cannot make cookies be included, like with
withCredentialsinfetch(). - So, having the right server settings and secure cookies is very important.
Frontend developers need to:
- Use full URLs in WebSocket setup.
- Make sure cookie and WebSocket main domains match.
- Always serve over HTTPS/WSS when the app is live.
Example:
const ws = new WebSocket("wss://api.example.com/ws");
// matching cookies are sent automatically if policy allows
How to Check for Cookie Problems in FastAPI WebSocket
Cookie problems can be known for being quiet and hard to find. Try these steps:
-
Client DevTools: Look at the “Network” → WS tab. See which cookies are sent in
Request Headers. -
Server Logging:
print(dict(websocket.headers)) -
Curl/WebSocket Clients:
curl -i --header "Cookie: session_id=test123" https://example.com/ws -
Python:
import websockets headers = {'Cookie': 'session_id=test123'} async with websockets.connect("wss://example.com/ws", extra_headers=headers): ...
Good tools for checking help you find Secure flag issues, SameSite policy differences, or missing domains.
Final Tips for FastAPI Depends and WebSocket
Here are some final lessons:
- ❌
FastAPI Depends()will not get cookies or headers in WebSocket routes. - ✅ Instead, use manual reading and specific helper functions.
- 🔐 Always check cookie-based sessions or tokens before you accept a WebSocket connection.
- 🧱
Middlewaredoes not work; use ASGI middleware or check inside the route. - 🚪 Close sockets that are not allowed with
code=1008to stop wrong use.
A Working Example of Secure WebSocket Auth in FastAPI
Here is a complete method that works to check user identity using cookies over WebSocket:
from fastapi import FastAPI, WebSocket
from http.cookies import SimpleCookie
app = FastAPI()
def get_cookie(websocket: WebSocket, name: str):
cookie_header = websocket.headers.get('cookie')
if cookie_header:
cookie = SimpleCookie()
cookie.load(cookie_header)
return cookie.get(name).value if cookie.get(name) else None
def valid_session(session_id: str) -> bool:
# Replace with your session validation logic
return session_id == "abc123" # Example only
@app.websocket("/ws")
async def protected_ws(websocket: WebSocket):
await websocket.accept()
session = get_cookie(websocket, "session_id")
if not session or not valid_session(session):
await websocket.close(code=1008)
return
await websocket.send_text("Welcome!")
This method makes sure of:
- Secure cookie access.
- A manual checking process.
- Correct cleanup if something goes wrong.
FastAPI Is Powerful—If You Understand How It Works
FastAPI gives simple ways to think about HTTP requests. But WebSockets don't quite fit that idea. If you understand the ASGI spec and how it separates HTTP and WebSocket scopes, you can build strong real-time apps. These apps will clearly include FastAPI cookie authentication and follow the FastAPI depends pattern—by hand.
Change how you set things up, use helpers that have been tested, and you will get the full power of WebSockets in FastAPI.
References
- FastAPI Documentation. (n.d.). WebSocket. Retrieved from https://fastapi.tiangolo.com/advanced/websockets/
- ASGI Specification. (2021). WebSockets lifespan and limitations. Retrieved from https://asgi.readthedocs.io/en/latest/
- Mozilla Developer Network. (2023). WebSockets and cookies. Retrieved from https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
- Python Docs. (v3.10). http.cookies — Cookie handling for HTTP clients and servers. Retrieved from https://docs.python.org/3/library/http.cookies.html