Setting standard HTTP security headers—such as Content-Security-Policy (CSP), Strict-Transport-Security (HSTS), and X-Frame-Options—is standard practice before shipping an API to production. They protect your users and infrastructure against common web vulnerabilities like clickjacking, MIME-type sniffing, cross-site scripting (XSS), and cross-origin resource leaks.
However, in FastAPI, developers routinely hit a frustrating roadblock the moment they attempt to enforce a strict Content Security Policy:
FastAPI's interactive documentation (/docs and /redoc) breaks immediately.
The interactive docs rely heavily on inline script execution, stylesheets, and remote assets to render Swagger UI and ReDoc properly on page load. When you add a generic, restrictive CSP middleware, the browser enforces the directives strictly, blocks those assets, and leaves you with a blank page. Developers typically end up wasting hours tuning directives by trial and error, or worse, abandoning CSP entirely.
To solve this problem cleanly, I built fastapi-security-headers.
Why This Implementation?
Many existing security header packages for Python web frameworks either wrap requests with high-level Starlette abstractions, buffer full response bodies into memory, or introduce unnecessary third-party dependencies into your dependency tree.
fastapi-security-headers was designed to be as minimal, direct, and efficient as possible:
-
Pure ASGI: Operates directly at the ASGI specification level. It intercepts the
http.response.startmessage and injects headers directly into the stream without buffering payloads or wrapping request/response objects. -
Pre-encoded Bytes: Headers are processed and encoded into raw
tuple[bytes, bytes]structures once during application initialization. At runtime, injecting headers into a response involves virtually zero compute overhead. -
Zero External Dependencies: Built strictly using Python's standard library (
dataclasses,typing). It will never bloat yourpoetry.lockorrequirements.txt. - Docs-Aware Presets: Ships with tested presets that enforce strict CSP policies while granting the precise exemptions needed for Swagger UI and ReDoc to work out of the box.
-
Safe for Streams and WebSockets: Automatically avoids interfering with
StreamingResponse, Server-Sent Events (SSE), or WebSocket connections. - Route-Aware Precedence: Honors explicit headers set by individual route handlers, only filling in missing security policies rather than blindly overwriting endpoint behavior.
Installation
Install the package via pip:
pip install fastapi-security-headers
Quickstart: Securing Your API and Docs
The fastest way to secure an API while keeping the documentation intact is using the built-in swagger_friendly preset:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, Presets
app = FastAPI(
title="Secure Production API",
docs_url="/docs",
redoc_url="/redoc"
)
# Apply baseline security headers + Swagger/ReDoc compatible CSP
app.add_middleware(
SecurityHeadersMiddleware,
config=Presets.swagger_friendly()
)
@app.get("/")
async def root():
return {"status": "healthy", "security": "enforced"}
@app.get("/items/{item_id}")
async def get_item(item_id: int):
return {"id": item_id, "name": f"Item {item_id}"}
With this single middleware configuration:
- Every standard API endpoint receives strict transport and isolation headers.
- Visiting
/docsor/redocloads all necessary scripts, styles, and assets without generating CSP violations in the browser console.
The Default Baseline Headers
When using fastapi-security-headers, every response is protected with the following baseline headers:
| HTTP Header | Default Value | Security Impact |
|---|---|---|
X-Content-Type-Options |
nosniff |
MIME-Sniffing: Prevents browsers from guessing content types, stopping executable payloads disguised as non-executable types like images or JSON. |
X-Frame-Options |
DENY |
Clickjacking: Disallows any domain from rendering your site inside an <iframe>, preventing UI redressing attacks. |
X-XSS-Protection |
0 |
Auditor Exploits: Disables legacy browser XSS filters that have been demonstrated to introduce new vulnerabilities (per modern OWASP guidelines). |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
SSL Stripping / MitM: Instructs browsers to communicate exclusively over HTTPS for the next 12 months, including all subdomains. |
Referrer-Policy |
strict-origin-when-cross-origin |
Data Leakage: Sends the full URL only for same-origin requests; strips query strings and paths when navigating across origins. |
Permissions-Policy |
geolocation=(), microphone=(), camera=() |
Feature Abuse: Explicitly restricts client browsers from invoking unused device hardware APIs. |
Cross-Origin-Opener-Policy |
same-origin |
Spectre / Process Isolation: Isolates your browsing context from cross-origin windows, preventing malicious document references. |
Cross-Origin-Resource-Policy |
same-origin |
Cross-Origin Reads: Prevents unauthorized origins from loading your API responses as resources. |
Built-In Presets
Different workloads have different requirements. You can choose between pre-configured presets depending on what your service exposes:
1. Swagger-Friendly Preset (Presets.swagger_friendly())
Designed for services that serve interactive documentation. It configures a Content Security Policy that permits assets required by Swagger UI and ReDoc while keeping all other resource vectors locked down:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, Presets
app = FastAPI()
app.add_middleware(
SecurityHeadersMiddleware,
config=Presets.swagger_friendly()
)
2. Headless API Preset (Presets.api())
Ideal for pure JSON microservices, internal services, or mobile backends that do not expose any HTML UI:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, Presets
app = FastAPI(docs_url=None, redoc_url=None)
# Restricts default-src to 'none' across all resource types
app.add_middleware(
SecurityHeadersMiddleware,
config=Presets.api()
)
This enforces Content-Security-Policy: default-src 'none', disallowing any browser execution of scripts, styles, frames, or plugins.
3. Strict Preset (Presets.strict())
Intended for high-compliance applications (such as financial, enterprise, or healthcare software subject to PCI-DSS, SOC 2, or HIPAA):
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, Presets
app = FastAPI()
# Enforces 2-year HSTS with preload flag and strict framing constraints
app.add_middleware(
SecurityHeadersMiddleware,
config=Presets.strict()
)
Custom Configuration
If you have specific architectural requirements or need to define custom policies, you can instantiate SecurityConfig directly:
from fastapi import FastAPI
from fastapi_security_headers import SecurityHeadersMiddleware, SecurityConfig
app = FastAPI()
custom_config = SecurityConfig(
enable_hsts=True,
hsts_max_age=63072000, # 2 years
hsts_include_subdomains=True,
hsts_preload=True,
content_security_policy=(
"default-src 'self'; "
"img-src 'self' data: https://images.example.com; "
"script-src 'self'; "
"frame-ancestors 'none';"
),
custom_headers={
"X-Permitted-Cross-Domain-Policies": "none",
"Clear-Site-Data": '"cache", "cookies", "storage"'
}
)
app.add_middleware(
SecurityHeadersMiddleware,
config=custom_config
)
Verifying Headers
You can verify that the headers are properly applied using curl against your local or staging server:
curl -I http://localhost:8000/
Example response:
HTTP/1.1 200 OK
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 0
strict-transport-security: max-age=31536000; includeSubDomains
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
cross-origin-opener-policy: same-origin
cross-origin-resource-policy: same-origin
content-security-policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-inline'; style-src 'self' https://cdn.jsdelivr.net 'unsafe-inline'; img-src 'self' data: https://fastapi.tiangolo.com;
You can also submit your public URL to securityheaders.com to audit your overall compliance grade.
Feedback & Source Code
The library is completely open source under the MIT License.
- GitHub Repository: aletgdev/fastapi-security-headers
- PyPI Package: pypi.org/project/fastapi-security-headers
If you encounter any edge cases with custom documentation routes or want to suggest new presets, feel free to open an issue or start a discussion on GitHub.
Top comments (0)