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)Run the test suite locally with pytest:
pip install -e ".[dev]"
pytest -vContributions, issues, and feature requests are welcome! 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.