NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #2942 most downloaded on PyPI
A lightweight package that adds security headers for Python web frameworks.
Last release 5 months ago
22 Apr 2026
Release timing varies
gaps range from 9 days to 3.4 years
Some releases are documented
notes for 6 of 15 stable releases
1 version withdrawn
withdrawn after publishing
8 years old
17 releases · first in 2018
secure v2.0.1 introduces a cleaner public API, modern preset defaults, first-class ASGI and WSGI middleware, and safer header handling across supporte
secure v2.0.1 introduces a cleaner public API, modern preset defaults, first-class ASGI and WSGI middleware, and safer header handling across supported Python web frameworks.
Compared with v1.0.1, v2 keeps the core Secure, with_default_headers(), from_preset(), set_headers(), and set_headers_async() APIs, but changes defaults, adds middleware and normalization helpers, and makes Secure.headers stricter and effectively read-only. Notably, neither v1 nor v2 exposes secure.__version__ as a public attribute, so version checks should use package metadata rather than module attributes.
Preset.BALANCED as the recommended defaultSecureASGIMiddleware and SecureWSGIMiddlewareCross-Origin-Resource-PolicyX-DNS-Prefetch-ControlX-Permitted-Cross-Domain-Policiesv2 adds framework-agnostic middleware in secure.middleware:
SecureASGIMiddleware(app, *, secure=None, multi_ok=None)SecureWSGIMiddleware(app, *, secure=None, multi_ok=None)This gives ASGI and WSGI applications a cleaner integration path than manually mutating each response.
v2 adds new helpers on Secure:
allowlist_headers()deduplicate_headers()validate_and_normalize_headers()header_items()These make it easier to inspect, normalize, and safely emit headers, especially in applications that may need duplicate-aware handling for multi-valued headers.
v2 adds:
CrossOriginResourcePolicyXDnsPrefetchControlXPermittedCrossDomainPoliciesMULTI_OKCOMMA_JOIN_OKDEFAULT_ALLOWED_HEADERSv2 tracks headers_list mutations correctly. In v1, once .headers had been read, later mutations to headers_list could leave .headers stale because it was backed by a cached property.
Secure.headers is stricterIn v1, Secure.headers was a cached dict[str, str], and duplicate header names silently collapsed to the last value.
In v2:
Secure.headers is an immutable mappingValueErrorIf your application intentionally or accidentally creates duplicate header names, do not rely on .headers as the source of truth. Use header_items() for ordered inspection, or call deduplicate_headers() before applying headers.
Secure.with_default_headers() no longer means the same thing it meant in v1.
with_default_headers() included Cache-Control: no-storewith_default_headers() maps to Preset.BALANCED, which does not include that same default cache behaviorIf you relied on the v1 default cache policy, add it explicitly in v2.
v1 included Preset.BASIC and Preset.STRICT.
v2 adds Preset.BALANCED, and Secure.with_default_headers() now maps to that new preset.
This also means Preset.BASIC in v2 is not the old BASIC. Compared with v1 BASIC, v2 BASIC adds or changes:
includeSubDomainsX-DNS-Prefetch-ControlX-Permitted-Cross-Domain-PoliciesOrigin-Agent-ClusterX-Download-OptionsX-XSS-Protectionand drops v1 BASIC defaults such as:
ServerCache-ControlPreset.STRICT also changed:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadpreload by defaultno-store to no-store, max-age=0If you need v1-style HSTS preload behavior, configure it explicitly in v2.
There is no secure.framework.fastapi helper in either version.
For FastAPI and other ASGI frameworks, the recommended upgrade path is to move from per-response mutation to SecureASGIMiddleware.
SecureASGIMiddlewareSecureWSGIMiddlewareallowlist_headers(...)deduplicate_headers(...)validate_and_normalize_headers(...)header_items()CrossOriginResourcePolicyXDnsPrefetchControlXPermittedCrossDomainPoliciesMULTI_OKCOMMA_JOIN_OKDEFAULT_ALLOWED_HEADERSSecure.with_default_headers() now returns the balanced presetReview your defaults
with_default_headers() in v2 matches v1Audit any code that reads Secure.headers
header_items() or call deduplicate_headers() firstMove ASGI and WSGI apps to middleware
set_headers_async() or set_headers() calls with SecureASGIMiddleware or SecureWSGIMiddlewareSecure instance if needed before wiring it into middlewarefrom fastapi import FastAPI, Request
from secure import Secure
app = FastAPI()
secure_headers = Secure.with_default_headers()
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
response = await call_next(request)
await secure_headers.set_headers_async(response)
return responsefrom fastapi import FastAPI
from secure import Secure
from secure.middleware import SecureASGIMiddleware
app = FastAPI()
secure_headers = (
Secure.with_default_headers()
.deduplicate_headers(action="last")
.validate_and_normalize_headers()
)
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)with_default_headers() means the same defaults as v1Secure.headers and update duplicate-header handlingSecure instancev2.0.1 keeps the core Secure API intact while making the library safer and easier to integrate in modern Python web applications. The biggest changes are the new preset model, middleware-first integration for ASGI and WSGI frameworks, and stricter handling of duplicate and normalized headers.
One column per quarter.
This is the first stable v2 release. Version 2.0.0 was burned and should be skipped when tagging or publishing.
v2 focuses on a cleaner public API, a redesigned preset model, first-class ASGI/WSGI middleware, and stricter, safer header handling.
Secure.headers is now strict and read-only
headers was a cached dict[str, str] and silently collapsed duplicate namesValueErrorheader_items() for multi-valued output or deduplicate_headers() to resolve duplicatesDefault headers have changed
Secure.with_default_headers() now maps to Preset.BALANCEDCache-Control: no-store; v2 does notPresets redesigned
Preset.BALANCED (recommended default)Preset.BASIC updated for Helmet.js parity and no longer matches v1 BASICPreset.STRICT no longer enables HSTS preload by defaultFastAPI / ASGI integration model changed in practice
set_headers / set_headers_async)Middleware
SecureASGIMiddlewareSecureWSGIMiddlewaresecure.middleware moduleHeader pipeline helpers
allowlist_headers(...)deduplicate_headers(...)validate_and_normalize_headers(...)header_items() for ordered (name, value) outputNew header builders and constants
CrossOriginResourcePolicyXDnsPrefetchControlXPermittedCrossDomainPoliciesMULTI_OK, COMMA_JOIN_OK, DEFAULT_ALLOWED_HEADERSOnInvalidPolicy, OnUnexpectedPolicy, DeduplicateActionSecure.with_default_headers() now returns the balanced presetheaders_list mutations are now reflected correctly (no stale cached state)Do not assume v1 defaults
Audit any usage of Secure.headers
header_items() or deduplicate_headers() when duplicates are possibleMove to middleware for ASGI/WSGI apps
set_headers_async() calls with SecureASGIMiddleware or SecureWSGIMiddlewareExplicitly configure behavior that changed
Cache-Control if you relied on v1 defaultssecure.__version__; use package metadata for version checksNothing published for this version
A release-candidate for secure v2.0.0 focused on a cleaner public API, modern presets, first-class ASGI/WSGI middleware , and safer header application
A release-candidate for secure v2.0.0 focused on a cleaner public API, modern presets, first-class ASGI/WSGI middleware, and safer header application/validation across frameworks.
Preset.BALANCEDPreset.BALANCED, now the recommended default.Secure.with_default_headers() now equals Secure.from_preset(Preset.BALANCED).Preset.BASIC targets Helmet.js default parity.Preset.STRICT no longer enables HSTS preload by default (opt-in separately).Secure.headers is now strict about duplicates
ValueError.header_items() for multi-valued emission, or resolve duplicates via deduplicate_headers() / validate_and_normalize_headers().SecureASGIMiddleware (intercepts ASGI http.response.start)SecureWSGIMiddleware (wraps WSGI start_response)secure.middleware re-exports both; supports multi_ok for safely appending multi-valued headers (e.g. CSP)Secure
allowlist_headers(...) (raise / drop / warn)deduplicate_headers(...) (raise, first, last, concat) with COMMA_JOIN_OK and MULTI_OKvalidate_and_normalize_headers(...) (RFC 7230 token validation, CR/LF hardening, optional obs-text, immutable normalized override)header_items() for ordered (name, value) output without enforcing uniquenessMULTI_OK, COMMA_JOIN_OK, DEFAULT_ALLOWED_HEADERSOnInvalidPolicy, OnUnexpectedPolicy, DeduplicateActionCross-Origin-Resource-PolicyX-DNS-Prefetch-ControlX-Permitted-Cross-Domain-PoliciesCODE_OF_CONDUCT.md, CONTRIBUTING.mdmulti_ok semanticsBALANCED / BASIC / STRICT) and documented default header setHeaderSetError, AttributeError, RuntimeError, pipeline ValueError)Origin-Agent-Cluster, X-Download-Options, X-XSS-Protection: 0 for Helmet-parityresponse.headers.set(...) (Werkzeug-style)HeaderSetErrorpyproject.toml modernized (metadata cleanup, setuptools floor bump, Ruff configuration)with_default_headers() behavior, review the new presets and choose:
Preset.BALANCED (default, recommended)Preset.BASIC (Helmet-parity compatibility)Preset.STRICT (hardened; no preload by default)header_items() and/or configure middleware multi_ok.See the migration guide: docs/migration.md.
Full Changelog: v1.0.1...v2.0.0rc1
This release focuses on improving the performance of the Secure.set_headers method by reducing redundant type checks. The changes optimize the efficie
This release focuses on improving the performance of the Secure.set_headers method by reducing redundant type checks. The changes optimize the efficiency when setting multiple headers, especially in frameworks that support both synchronous and asynchronous methods.
Secure.set_headers and Secure.set_headers_async. This optimizes the process by checking the response type once before looping through headers, enhancing performance for applications with multiple headers. #26A big thank you to @davidwtbuxton for raising the issue and helping us improve the project.
To upgrade to v1.0.1, simply run:
pip install --upgrade secureSecure.set_headers by reducing redundant type checks. (#26)API Redesign: The library has undergone a full API redesign, and some previous methods have been deprecated or refactored. Be sure to review the docum…
We’re excited to announce the release of secure.py v1.0.0! This is a major update that completely redesigns the library with modern Python support and significant improvements in usability, security, and performance.
Full API Overhaul: The entire library has been redesigned for Python 3.10+ with a more Pythonic API, leveraging type hints and modern language features like union operators (|) and cached_property.
Improved Framework Support: Enhanced integration for popular web frameworks like FastAPI, Flask, Django, Sanic, Starlette, and more, with improved support for asynchronous frameworks.
Middleware Examples: We've added middleware-based integration examples for supported frameworks, making it easier to apply security headers across your application.
Enhanced Security Defaults: Updated default security headers for stronger protection, including refined Content-Security-Policy (CSP) configurations with nonce and strict-dynamic directives.
Better Type Annotations: The entire codebase now includes better type hints and annotations for an improved developer experience.
API Redesign: The library has undergone a full API redesign, and some previous methods have been deprecated or refactored. Be sure to review the documentation before upgrading.
Python 3.10+ Required: This release drops support for older versions of Python. Ensure you are running Python 3.10 or later before upgrading.
Server headers in Uvicorn-based frameworks, with examples on how to prevent default Uvicorn headers.We look forward to your feedback! 🚀
secure.py library with modern Python (3.10+) support.|) and cached_property.Change Feature-Policy to Permissions-Policy
Breaking Changes
Changelog:
Feature-Policy to Permissions-Policy (#10)Remove trailing semicolon from Feature Policy
Merry Christmas! 🎅
Feature.Values.All to Feature.Values.All_ (shadowed built-in name 'all')SameSite.LAX / SameSite.STRICT)SecureHeaders and SecureCookie.SameSite.LAX / SameSite.STRICT.Feature.Values.All to Feature.Values.All_ to avoid conflict with the built-in all.Add policy builder SecurePolicies (policies.py)
SecurePolicies (policies.py)Expires header for legacy browser supportmax-age directive to Cache-control headerXXS argument to XXPSecurePolicies in policies.py.Expires header for legacy browser support.max-age directive to Cache-Control header.XXS argument to XXP.set-cookie to use Flask's native method.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →