Add modern security headers to any Python web app in minutes.
Works with FastAPI, Flask, Django, Starlette, and Shiny for Python. No dependencies.
Quick links: Quick start · Headers · Middleware
Security headers are one of the simplest ways to improve a web application's security, but they are often applied inconsistently across frameworks and deployments.
secure provides a single, modern, well-typed API for configuring and applying HTTP security headers in Python.
- Good defaults that are safe to adopt
- A small, explicit API
- Sync and async support
- Framework-agnostic design
import secure- Apply essential security headers in a few lines
- Share one configuration across multiple frameworks
- Start with presets, then customize
- Keep header logic out of your handlers
- Use one library for FastAPI, Starlette, Flask, Django, Shiny for Python, and more
If you want a strong security baseline without adding a large dependency, secure is designed for that use case.
uv add secure
# or
pip install secureimport secure
secure_headers = secure.Secure.with_default_headers()
# Apply to any response object with headers support
secure_headers.set_headers(response)
# or
await secure_headers.set_headers_async(response)Secure.with_default_headers() maps to Preset.BALANCED, the recommended default.
Works with FastAPI, Flask, Django, Starlette, and Shiny for Python in minutes.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests
Strict-Transport-Security: max-age=31536000; includeSubDomains
Permissions-Policy: geolocation=(), microphone=(), camera=()
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGINThis baseline reflects modern browser guidance from MDN and OWASP. It reduces cross-origin risk, prevents MIME sniffing, and provides a conservative Content Security Policy you can extend.
The default CSP is designed to work for most applications while avoiding unsafe script execution. Interactive apps or third-party integrations may require additional CSP configuration depending on how scripts, styles, or external resources are used.
from secure import Preset, Secure
Secure.from_preset(Preset.BALANCED)
Secure.from_preset(Preset.BASIC)
Secure.from_preset(Preset.STRICT)- BALANCED: recommended default
- BASIC: Helmet-style compatibility
- STRICT: tighter CSP and isolation
Start with BALANCED and move stricter only when needed.
secure provides both ASGI and WSGI middleware.
- Works with FastAPI, Starlette, Shiny, Flask, Django, and others
- Overwrites headers by default
- Allows controlled duplication via
multi_ok
from fastapi import FastAPI
from secure import Secure
from secure.middleware import SecureASGIMiddleware
app = FastAPI()
secure_headers = Secure.with_default_headers()
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)from secure import Secure
class SecureHeadersMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.secure = Secure.with_default_headers()
def __call__(self, request):
response = self.get_response(request)
self.secure.set_headers(response)
return responseapp.add_middleware(SecureASGIMiddleware, secure=secure_headers)@app.after_request
def add_security_headers(response):
secure_headers.set_headers(response)
return responsefrom shiny import App
from secure.middleware import SecureASGIMiddleware
app = SecureASGIMiddleware(App(), secure=secure_headers)Interactive apps may require additional CSP configuration depending on how scripts, styles, or external resources are used.
from secure.headers import ContentSecurityPolicy
csp = (
ContentSecurityPolicy()
.default_src("'self'")
.script_src("'self'", "cdn.example.com")
)from secure.headers import PermissionsPolicy
permissions = (
PermissionsPolicy()
.geolocation("'self'")
.camera("'none'")
)Optional pipeline for stricter control:
secure_headers = (
Secure.with_default_headers()
.allowlist_headers(...)
.deduplicate_headers(...)
.validate_and_normalize_headers(...)
)Use this when you need strict validation, predictable output, or want to fail fast on unsafe headers.
FastAPI, Starlette, Flask, Django, Shiny for Python, aiohttp, Sanic, Pyramid, Tornado, and more.
See the full framework integration guides.
- Python 3.10+
- No external dependencies
Read the full documentation.
MIT License
Issues and pull requests are welcome.
Built using guidance from MDN and OWASP secure headers recommendations.
