Install & Compatibility
Where this runs
tested against v3.0.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.910 runs
installs and imports cleanly · install 0.0s · import 0.329s · 20.9MB
glibcpy 3.10–3.910 runs
installs and imports cleanly · install 2.0s · import 0.304s · 21MB
19MB installed
● package 19MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
CSRFMiddleware
✓ from starlette_csrf import CSRFMiddleware
This quickstart demonstrates how to integrate `CSRFMiddleware` into a FastAPI application. A `GET` request will automatically receive a `csrftoken` cookie and expose the token in `request.state.csrftoken`. For `POST` requests, the client is expected to include this token in an `x-csrftoken` header for successful validation. The example shows how to embed this in an HTML form.
import os
import uvicorn
from fastapi import FastAPI, Request, Response, Form
from starlette.middleware import Middleware
from starlette.routing import Route
from starlette.responses import HTMLResponse
from starlette_csrf import CSRFMiddleware
# Ensure you have a strong secret key
SECRET_KEY = os.environ.get('STARLETTE_CSRF_SECRET', 'a-very-secret-key-that-you-should-change-in-production')
app = FastAPI(
middleware=[
Middleware(CSRFMiddleware, secret=SECRET_KEY)
]
)
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
# The CSRF token is automatically set in a cookie on GET requests
# and can be accessed via request.state.csrftoken for templates.
# In a real application, you'd embed this in your HTML forms.
token = request.state.csrftoken if hasattr(request.state, 'csrftoken') else 'No token (GET request initial load)'
return f'''
<html>
<head>
<title>CSRF Test</title>
</head>
<body>
<h1>Welcome!</h1>
<p>CSRF Token in state (for display only): {token}</p>
<form method="post" action="/submit">
<input type="text" name="item" placeholder="Enter item">
<!-- In a real frontend, you'd get this from a cookie or initial GET response -->
<input type="hidden" name="x-csrftoken" value="{{request.state.csrftoken}}">
<button type="submit">Submit</button>
</form>
<script>
// For AJAX requests, you'd extract the csrftoken cookie and send it in the header
// Example (conceptual, requires frontend JS to read cookie):
// const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken=')).split('=')[1];
// fetch('/submit', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// 'x-csrftoken': csrfToken
// },
// body: 'item=ajax_test'
// });
</script>
</body>
</html>
'''
@app.post("/submit")
async def submit_item(item: str = Form(...), response: Response = None):
# The middleware automatically validates the token from the 'x-csrftoken' header
# If validation fails, it returns a 403 Forbidden before this handler is called.
return {"message": f"Item '{item}' received successfully!"}
if __name__ == "__main__":
# To run: uvicorn your_app_file_name:app --reload
# Then open http://127.0.0.1:8000
uvicorn.run(app, host="127.0.0.1", port=8000)
Errors
Common errors & fixes
403 Forbidden error on POST requests (or other unsafe methods).
The CSRF token expected in the 'x-csrftoken' header (or the configured header name) does not match the token in the 'csrftoken' cookie, or the header is missing entirely.
fixEnsure your frontend (HTML form or JavaScript AJAX request) correctly retrieves the `csrftoken` cookie value and sends it in the `x-csrftoken` HTTP header for all unsafe requests (POST, PUT, DELETE, PATCH). For HTML forms, you can inject `request.state.csrftoken` into a hidden input field. For AJAX, read the cookie and set the header manually.
WebSocket connections fail with `AssertionError` or other unexpected errors when `CSRFMiddleware` is applied globally.
The `CSRFMiddleware` is designed for HTTP requests and may not handle WebSocket scopes correctly, leading to internal errors if it processes a WebSocket connection.
fixExempt WebSocket routes from CSRF protection by adding them to the `exempt_urls` argument of the `CSRFMiddleware` using a regular expression pattern. Alternatively, wrap the middleware to conditionally apply it only to HTTP scopes, as suggested in some community discussions.
Upgrade
Version history
3.0.0latest on PyPI · released Jun 27, 2023
Audit
Dependencies
starletterequiredCore framework dependency. Compatible with `starlette >=0.14.2` historically, but generally works with recent versions.
itsdangerousrequiredUsed for cryptographic signing of the CSRF token. Compatible with `itsdangerous >=2.0.1,<3.0.0` historically, but generally works with recent versions.