django-csp

Content-Security-Policy header management for Django, with per-view overrides and nonce support.

Library
PyPI
v4.0
624stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity0
Maintenance20
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
86/100Excellent
Architecture82
Code Quality88
Innovation85
Learning Curve90

django-csp is a lightweight Django middleware library, maintained by Mozilla, that generates and attaches Content-Security-Policy (CSP) and CSP-Report-Only response headers based on settings defined in CONTENT_SECURITY_POLICY and CONTENT_SECURITY_POLICY_REPORT_ONLY. It has been protecting Django sites against XSS and data-injection attacks since 2010, and version 4.0 introduced a settings-driven configuration format alongside CSP keyword constants like SELF, NONE, and STRICT_DYNAMIC.

Beyond a global policy, django-csp exposes per-view decorators (@csp, @csp_update, @csp_replace, @csp_exempt) for adjusting or exempting a policy on individual views, a lazily-generated per-request nonce accessible as request.csp_nonce for allow-listing inline scripts without unsafe-inline, and a RateLimitedCSPMiddleware for throttling how often violation reports get sent to a report-uri. Template tags and a Jinja2 extension make it straightforward to emit correctly nonced <script> tags from templates.

What You Get

  • Global CSP/CSP-Report-Only header generation driven entirely by Django settings
  • Per-view decorators (@csp, @csp_update, @csp_replace, @csp_exempt) for adjusting or exempting policy on individual views
  • A request.csp_nonce lazy nonce plus a context processor and template tag/Jinja2 extension for correctly nonced inline scripts
  • RateLimitedCSPMiddleware to throttle the percentage of requests that send violation reports to report-uri

Common Use Cases

  • Locking down a Django app’s script/style/frame sources to mitigate XSS
  • Rolling out a stricter policy safely via CONTENT_SECURITY_POLICY_REPORT_ONLY before enforcing it
  • Allow-listing specific inline scripts with per-request nonces instead of unsafe-inline
  • Exempting or loosening the policy for specific views such as third-party embeds or admin tools

Under The Hood

Architecture django-csp is organized as a small, flat Django app (the csp package) rather than a layered system: csp/middleware.py holds the single CSPMiddleware (a MiddlewareMixin subclass) that attaches a lazily-generated nonce to the request in process_request and, in process_response, calls into csp/utils.py’s pure build_policy() function twice (once for the enforced policy, once for report-only) to render header strings sourced from PolicyParts — a small dataclass assembled from Django settings and any per-response _csp_config/_csp_update/_csp_replace attributes set by the decorators in csp/decorators.py. This is the core abstraction: policy state travels on the HttpResponse object itself as private attributes rather than through a request context, so decorators can mutate policy per-view without global state, and RateLimitedCSPMiddleware in csp/contrib/rate_limiting.py composes on top by subclassing CSPMiddleware and overriding get_policy_parts() to strip report-uri/report-to for a random subset of requests. Template rendering (csp/templatetags/csp.py’s script tag and csp/extensions/__init__.py’s Jinja2 NoncedScript extension) both funnel through the same build_script_tag() helper in utils.py. If build_policy()’s directive-merging precedence changed, every downstream feature — decorators, middleware, rate limiting — would need re-verification since they all depend on its exact config/update/replace semantics.

Tech Stack The only runtime dependencies are django>=4.2 and packaging (used in csp/checks.py to gate a Django system check by installed version), with an optional extra for jinja2>=2.9.6 powering the Jinja2 template extension. The package targets Python 3.9-3.13 plus PyPy, and Django 4.2 through 5.2, uses a pyproject.toml-only setuptools build, and ships fully type-annotated code (a py.typed marker is present) checked with mypy --strict via the django-stubs plugin. Tooling is Ruff for linting and formatting, tox/tox-gh-actions to run the test matrix across Python/Django/PyPy combinations, and pre-commit hooks enforced before merge. Documentation is built with Sphinx and hosted on Read the Docs, and releases publish to PyPI via a GitHub Actions job gated on a published GitHub release.

Code Quality Testing uses pytest with pytest-django and pytest-cov, plus pytest-ruff wired into pyproject.toml’s addopts so lint/format checks run as part of the test suite itself; the csp/tests/ directory has one test module per source file, using Django’s RequestFactory and override_settings extensively to cover header-generation edge cases like set-typed directive values and simultaneous enforced/report-only policies. Error handling is explicit and typed — a dedicated CSPNonceError exception guards against reading request.csp_nonce after the response has already been written — and csp/checks.py registers real Django system checks that surface actionable warnings, including an auto-generated migration snippet, when a project is still using the pre-4.0 CSP_* settings format. Every module uses from __future__ import annotations plus full type hints, naming is consistent snake_case throughout, and CI runs the full matrix of Python/PyPy versions on every push and PR.

API Design django-csp’s main technical distinctions are its per-response, decorator-driven policy model — storing pending policy changes as private attributes on the HttpResponse object so per-view decorators compose cleanly with global middleware settings — and its treatment of the CSP nonce as a CheckableLazyObject, a SimpleLazyObject subclass whose __bool__ reports whether the nonce was actually generated, letting the middleware detect after the fact whether request.csp_nonce was ever read and skip adding an unused nonce to the header, while raising CSPNonceError if it’s accessed too late to be included. RateLimitedCSPMiddleware’s statistical sampling of violation reports is a small but genuinely useful primitive for running CSP in report-only mode at scale, where full reporting on a busy site can flood a report-uri endpoint. None of this is groundbreaking research — it follows patterns established by similar CSP middlewares in other frameworks — but the settings-migration system checks that auto-generate a new-format config from old CSP_* settings are an unusually developer-friendly touch for a library undergoing a breaking config-format change.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search