The missing security headers middleware for FastAPI. Protect your API against XSS, clickjacking, MIME sniffing, and OWASP Top 10 web vulnerabilities with secure-by-default configurations and zero overhead.
- π Zero Performance Overhead (Pure ASGI): Bypasses heavy Starlette wrappers. Injects headers directly into ASGI raw message streams without body buffering.
- π¦ Zero Dependencies: Uses only Python's standard library (
dataclasses,typing). Zero bloat in your dependency tree. - β‘ Pre-compiled Bytes: Header tuples are encoded to
(bytes, bytes)once at application startup, running in nanoseconds per request. - ποΈ Out-of-the-box Presets: Ready-made configurations for APIs, high-compliance environments, and mixed apps.
- π Swagger / ReDoc Friendly: Includes a dedicated preset that won't break your interactive
/docsdocumentation. - π Route-Aware: Intelligently respects custom headers set by individual route handlers unless explicitly configured to override.
- π WebSockets & Streaming Safe: Passes WebSockets and large streaming responses (
StreamingResponse) seamlessly.
pip install fastapi-security-headersAdd the middleware to your FastAPI application in just 2 lines of code:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware
app = FastAPI()
# Enable OWASP recommended security headers with secure defaults
app.add_middleware(SecurityHeadersMiddleware)
@app.get("/")
async def root():
return {"message": "Protected by fastapi-security-headers"}By default, fastapi-security-headers turns an F score on security scanners into an A+:
| HTTP Header | Default Value | Attack Vector Mitigated |
|---|---|---|
X-Content-Type-Options |
nosniff |
MIME-Sniffing: Prevents browsers from guessing content types, blocking malicious scripts disguised as images or JSON. |
X-Frame-Options |
DENY |
Clickjacking: Prevents external sites from embedding your API inside hidden <iframe> overlays. |
X-XSS-Protection |
0 |
Audit Vulnerabilities: Disables legacy buggy XSS filters as recommended by OWASP. |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
SSL Stripping / MitM: Enforces HTTPS for all future visits over the next 365 days. |
Referrer-Policy |
strict-origin-when-cross-origin |
Data Leakage: Prevents leaking sensitive URL query parameters to third-party domains. |
Permissions-Policy |
geolocation=(), microphone=(), camera=() |
Feature Abuse: Disables unused device APIs (GPS, camera, microphone). |
Cross-Origin-Opener-Policy |
same-origin |
Side-Channel Attacks: Isolates browsing context against Spectre-style attacks. |
Cross-Origin-Resource-Policy |
same-origin |
Cross-Origin Reads: Blocks external origins from reading your API responses. |
Enforces strict Content Security Policy (CSP) while allowing necessary CDNs (cdn.jsdelivr.net) and assets for Swagger UI (/docs) and ReDoc (/redoc).
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, Presets
app = FastAPI()
app.add_middleware(SecurityHeadersMiddleware, config=Presets.swagger_friendly())Hardened specifically for pure JSON microservices. Disallows all frames, scripts, and media loading:
app.add_middleware(SecurityHeadersMiddleware, config=Presets.api())Maximum security posture for financial, health, and enterprise apps. Enforces 2-year HSTS with preload, require-corp, and strict CSP:
app.add_middleware(SecurityHeadersMiddleware, config=Presets.strict())Balanced baseline for general web applications.
You can fully customize headers or disable any specific header by setting it to None:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, SecurityHeadersConfig, HSTSConfig
config = SecurityHeadersConfig(
# Customize HSTS (e.g. disable on localhost or enable preload)
strict_transport_security=HSTSConfig(max_age=63072000, include_subdomains=True, preload=True),
# Allow iframes from the same origin
x_frame_options="SAMEORIGIN",
# Add custom enterprise security headers
custom_headers={
"X-Permitted-Cross-Domain-Policies": "none",
}
)
app = FastAPI()
app.add_middleware(SecurityHeadersMiddleware, config=config)By default (override=False), if a specific endpoint returns its own custom header, the middleware respects it and does not duplicate it:
from fastapi.responses import JSONResponse
@app.get("/embeddable-widget")
async def widget():
# This endpoint specifically permits embedding
return JSONResponse(
content={"data": "widget"},
headers={"x-frame-options": "SAMEORIGIN"}
)To force middleware headers across all endpoints regardless of route return values, set override=True:
app.add_middleware(SecurityHeadersMiddleware, override=True)To strip Server and X-Powered-By headers emitted by application routes (to mitigate server fingerprinting), set strip_server_headers=True:
app.add_middleware(SecurityHeadersMiddleware, strip_server_headers=True)When running behind Uvicorn, pure ASGI middleware runs after Uvicorn appends its server-level default headers (such as Server: uvicorn). Therefore, strip_server_headers=True will filter out headers from the application layer, but Uvicorn's own wire-level Server header will still be sent unless disabled directly in Uvicorn:
Programmatically:
# Run uvicorn programmatically with server_header=False
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000, server_header=False)Or via the CLI flag:
# Pass --no-server-header flag in CLI
uvicorn app:app --no-server-headerRun the test suite locally with pytest:
pip install -e ".[dev]"
pytest -vContributions, issues, and feature requests are welcome! See CONTRIBUTING.md for setup, testing, and pull request guidelines.
New here? Look for issues labeled good first issue or help wanted, and feel free to check the issues page.
Alejandro Tacoronte GonzΓ‘lez
- GitHub: @aletgdev
- LinkedIn: Alejandro Tacoronte
- Portfolio: portfolio.alejandrotg.es
If this project helps you secure your FastAPI applications, consider buying a coffee! β
This project is licensed under the MIT License - see the LICENSE file for details.