secure keeps the same Secure object across frameworks. What changes is how you attach it.
Some sections below are first-class integrations with clear framework-level hooks or middleware. Others are intentionally minimal fallback examples where support is thinner or framework APIs vary by version.
- Use
set_headers()when the response object is synchronous and you are already inside a response hook, middleware callback, or view. - Use
set_headers_async()in async middleware, hooks, or handlers when you want one helper that works safely across async response objects. - Use
SecureWSGIMiddlewarewhen you want app-wide coverage and can wrap a WSGI application directly. - Use
SecureASGIMiddlewarewhen you want app-wide coverage in an ASGI stack such as FastAPI, Starlette, or Shiny.
Prefer middleware when your framework makes it easy and you want app-wide coverage. Use per-response setters when you are integrating into an existing hook, view, or minimal handler path.
Uvicorn adds Server: uvicorn by default. If you want secure to control the Server header, disable Uvicorn's default header with --no-server-header or server_header=False.
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000, server_header=False)- aiohttp
- Bottle
- CherryPy
- Dash
- Django
- Falcon
- FastAPI
- Flask
- Masonite
- Morepath
- Pyramid
- Quart
- Responder
- Sanic
- Shiny
- Starlette
- Tornado
- TurboGears
- Custom frameworks
Async framework with first-class middleware support.
from aiohttp import web
from secure import Secure
secure_headers = Secure.with_default_headers()
@web.middleware
async def add_security_headers(request, handler):
response = await handler(request)
await secure_headers.set_headers_async(response)
return response
app = web.Application(middlewares=[add_security_headers])from aiohttp import web
from secure import Secure
secure_headers = Secure.with_default_headers()
async def home(request):
response = web.Response(text="Hello, world")
await secure_headers.set_headers_async(response)
return responseSmall WSGI framework with request hooks.
from bottle import Bottle, response
from secure import Secure
app = Bottle()
secure_headers = Secure.with_default_headers()
@app.hook("after_request")
def add_security_headers():
secure_headers.set_headers(response)from bottle import Bottle, response
from secure import Secure
app = Bottle()
secure_headers = Secure.with_default_headers()
@app.route("/")
def home():
secure_headers.set_headers(response)
return "Hello, world"Minimal fallback example. CherryPy exposes the response object in the handler, so handler-level mutation is the practical integration point.
import cherrypy
from secure import Secure
secure_headers = Secure.with_default_headers()
class App:
@cherrypy.expose
def index(self):
secure_headers.set_headers(cherrypy.response)
return b"Hello, world"
cherrypy.quickstart(App())Dash runs on top of Flask, so the usual Flask integration patterns apply.
import dash
from dash import html
from secure import Secure
app = dash.Dash(__name__)
server = app.server
secure_headers = Secure.with_default_headers()
app.layout = html.Div("Hello Dash!")
@server.after_request
def add_security_headers(response):
secure_headers.set_headers(response)
return responseimport dash
from dash import html
from secure import Secure
from secure.middleware import SecureWSGIMiddleware
app = dash.Dash(__name__)
server = app.server
secure_headers = Secure.with_default_headers()
app.layout = html.Div("Hello Dash!")
server.wsgi_app = SecureWSGIMiddleware(server.wsgi_app, secure=secure_headers)Django is usually best integrated through Django middleware rather than raw WSGI wrapping.
Register the middleware class in your Django MIDDLEWARE setting.
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 responsefrom django.http import HttpResponse
from secure import Secure
secure_headers = Secure.with_default_headers()
def home(request):
response = HttpResponse("Hello, world")
secure_headers.set_headers(response)
return responseFalcon exposes a clean response middleware hook.
import falcon
from secure import Secure
secure_headers = Secure.with_default_headers()
class SecureMiddleware:
def process_response(self, req, resp, resource, req_succeeded):
secure_headers.set_headers(resp)
app = falcon.App(middleware=[SecureMiddleware()])import falcon
from secure import Secure
secure_headers = Secure.with_default_headers()
class HelloWorldResource:
def on_get(self, req, resp):
resp.text = "Hello, world"
secure_headers.set_headers(resp)
app = falcon.App()
app.add_route("/", HelloWorldResource())ASGI framework. Middleware is the clearest default.
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 fastapi import FastAPI
from secure import Secure
app = FastAPI()
secure_headers = Secure.with_default_headers()
@app.middleware("http")
async def add_security_headers(request, call_next):
response = await call_next(request)
await secure_headers.set_headers_async(response)
return responsefrom fastapi import FastAPI, Response
from secure import Secure
app = FastAPI()
secure_headers = Secure.with_default_headers()
@app.get("/")
def home(response: Response):
secure_headers.set_headers(response)
return {"hello": "world"}WSGI framework with a straightforward response hook.
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 responsefrom 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)Minimal fallback example. Masonite routing and response APIs vary by version, so apply Secure to the response object you actually return.
from secure import Secure
secure_headers = Secure.with_default_headers()
def home(response):
rendered = response.json({"hello": "world"})
secure_headers.set_headers(rendered)
return renderedMinimal fallback example. Morepath does not expose a conventional middleware layer for this, so view-level mutation is the practical integration point.
import morepath
from secure import Secure
secure_headers = Secure.with_default_headers()
class App(morepath.App):
pass
@App.path(path="")
class Root:
pass
@App.view(model=Root)
def home(self, request):
response = morepath.Response("Hello, world")
secure_headers.set_headers(response)
return responsePyramid applications commonly use tweens for cross-cutting response changes.
Register the tween in your Configurator with config.add_tween("yourpackage.security.add_security_headers").
from secure import Secure
secure_headers = Secure.with_default_headers()
def add_security_headers(handler, registry):
def tween(request):
response = handler(request)
secure_headers.set_headers(response)
return response
return tweenfrom pyramid.response import Response
from secure import Secure
secure_headers = Secure.with_default_headers()
def home(request):
response = Response("Hello, world")
secure_headers.set_headers(response)
return responseAsync Flask-compatible framework.
from quart import Quart
from secure import Secure
app = Quart(__name__)
secure_headers = Secure.with_default_headers()
@app.after_request
async def add_security_headers(response):
await secure_headers.set_headers_async(response)
return responsefrom quart import Quart, Response
from secure import Secure
app = Quart(__name__)
secure_headers = Secure.with_default_headers()
@app.route("/")
async def home():
response = Response("Hello, world")
await secure_headers.set_headers_async(response)
return responseMinimal fallback example. Route handlers typically own the response, so route-level mutation is the practical integration point.
import responder
from secure import Secure
api = responder.API()
secure_headers = Secure.with_default_headers()
@api.route("/")
async def home(req, resp):
resp.text = "Hello, world"
await secure_headers.set_headers_async(resp)Sanic exposes response middleware for app-wide coverage.
from sanic import Sanic
from secure import Secure
app = Sanic("secure-app")
secure_headers = Secure.with_default_headers()
@app.middleware("response")
async def add_security_headers(request, response):
await secure_headers.set_headers_async(response)
return responseUse route-level setters when you only need a small integration or do not want extra app wiring in tests.
from sanic import Sanic, response
from secure import Secure
app = Sanic("secure-app")
secure_headers = Secure.with_default_headers()
@app.get("/")
async def home(request):
resp = response.text("Hello, world")
await secure_headers.set_headers_async(resp)
return respShiny applications are ASGI apps, so ASGI middleware is the cleanest and most direct path.
from secure import Secure
from secure.middleware import SecureASGIMiddleware
from shiny import App, ui
secure_headers = Secure.with_default_headers()
app_ui = ui.page_fluid("Hello Shiny!")
def server(input, output, session):
pass
app = App(app_ui, server)
app = SecureASGIMiddleware(app, secure=secure_headers)ASGI framework. Use ASGI middleware unless you only need route-level control.
from secure import Secure
from secure.middleware import SecureASGIMiddleware
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
secure_headers = Secure.with_default_headers()
async def home(request):
return PlainTextResponse("Hello, world")
app = Starlette(routes=[Route("/", home)])
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)from secure import Secure
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route
secure_headers = Secure.with_default_headers()
async def home(request):
response = Response("Hello, world")
await secure_headers.set_headers_async(response)
return response
app = Starlette(routes=[Route("/", home)])Minimal fallback example. Tornado usually applies headers inside request handlers, so handler-level mutation is the practical integration point.
import tornado.web
from secure import Secure
secure_headers = Secure.with_default_headers()
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
secure_headers.set_headers(self)
app = tornado.web.Application([(r"/", MainHandler)])Minimal fallback example. If you do not already have a framework-level hook in place, controller-level mutation is the practical integration point.
from tg import Response, TGController, expose
from secure import Secure
secure_headers = Secure.with_default_headers()
class RootController(TGController):
@expose()
def index(self):
response = Response("Hello, world")
secure_headers.set_headers(response)
return response
root = RootController()If your framework is not listed here, the integration rule is still simple: configure one Secure instance, then apply it to the response as late as possible before it is sent.
from secure import Secure
secure_headers = Secure.with_default_headers()
def add_security_headers(response):
secure_headers.set_headers(response)
return responsefrom secure import Secure
secure_headers = Secure.with_default_headers()
for name, value in secure_headers.header_items():
response.headers[name] = value