secure

HTTP security headers for Python web applications, centered on one object: Secure .

secure exists to keep header policy out of ad hoc view code. Instead of copying header strings into routes, middleware, and framework-specific hooks, you configure one Secure instance and apply it consistently.

Hand-written header code tends to drift. Headers get missed, defaults vary between apps, and sync or async framework details leak into otherwise simple code. secure gives you a small public API, opinionated presets, and typed builders when you need to go beyond the defaults.

Install

secure requires Python 3.10+ and has no external dependencies.

uv add secure

pip install secure

Quick start

Start with Secure.with_default_headers() . It uses Preset.BALANCED , the recommended default for most applications.

from secure import Secure class Response : def __init__ ( self ): self . headers = {} response = Response () Secure . with_default_headers (). set_headers ( response )

Secure applies headers to response objects that expose either:

response.set_header(name, value)

response.headers[name] = value

The quick start uses the response.headers[name] = value form.

Use set_headers() for synchronous response objects. Use set_headers_async() in async code or when the response object may use async setters.

Presets

Most applications should start with BALANCED .

from secure import Preset , Secure balanced = Secure . from_preset ( Preset . BALANCED ) basic = Secure . from_preset ( Preset . BASIC ) strict = Secure . from_preset ( Preset . STRICT )

Preset.BALANCED : recommended default. Modern baseline with CSP, HSTS, referrer policy, permissions policy, and common browser protections.

: recommended default. Modern baseline with CSP, HSTS, referrer policy, permissions policy, and common browser protections. Preset.BASIC : compatibility-oriented. Adds legacy and interoperability headers that some deployments still expect.

: compatibility-oriented. Adds legacy and interoperability headers that some deployments still expect. Preset.STRICT : hardened profile. Tightens CSP, disables caching, and denies framing.

Choose BALANCED unless you have a specific reason to prefer BASIC or STRICT .

Middleware

If your framework supports app-wide middleware, prefer that over setting headers one response at a time.

WSGI

from flask import Flask from secure import Secure from secure . middleware import SecureWSGIMiddleware app = Flask ( __name__ ) secure_headers = Secure . with_default_headers () app . wsgi_app = SecureWSGIMiddleware ( app . wsgi_app , secure = secure_headers )

ASGI

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 )

Use SecureWSGIMiddleware when you can wrap a WSGI app directly. Use SecureASGIMiddleware when you want app-wide coverage in an ASGI stack such as FastAPI, Starlette, or Shiny.

Advanced usage

Most applications can stop at a preset. When you need to tune a specific header, keep Secure as the entry point and pass builder objects into it.

Custom policy

from secure import ContentSecurityPolicy , Secure , StrictTransportSecurity secure_headers = Secure ( csp = ( ContentSecurityPolicy () . default_src ( "'self'" ) . img_src ( "'self'" , "https://images.example.com" ) . script_src ( "'self'" , "https://cdn.example.com" ) ), hsts = StrictTransportSecurity (). max_age ( 63072000 ). include_subdomains (), )

Optional validation

The validation pipeline is optional. Use it when headers are being composed dynamically and you want stricter checks before emission.

from secure import Secure secure_headers = ( Secure . with_default_headers () . allowlist_headers () . deduplicate_headers () . validate_and_normalize_headers () )

If you need manual emission for an unsupported response contract, iterate over secure_headers.header_items() .

Framework examples

See docs/frameworks.md for the full matrix. The most common patterns are:

FastAPI

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 )

Flask

from flask import Flask from secure import Secure app = Flask ( __name__ ) secure_headers = Secure . with_default_headers () @ app . after_request def add_security_headers ( response ): secure_headers . set_headers ( response ) return response

Starlette

from secure import Secure from secure . middleware import SecureASGIMiddleware from starlette . applications import Starlette app = Starlette () secure_headers = Secure . with_default_headers () app . add_middleware ( SecureASGIMiddleware , secure = secure_headers )