Skip to content
MDView as Markdown

Django

Django 6.0 brought CSP into core. Before that you needed django-csp; now the middleware, the settings and the template nonce all ship with the framework, and the report-only variant is a first-class setting rather than an afterthought.

What Django gives you

  • django.middleware.csp.ContentSecurityPolicyMiddleware, added to MIDDLEWARE.
  • Two mutually exclusive settings: SECURE_CSP to enforce, and SECURE_CSP_REPORT_ONLY to report without blocking.
  • Policies written as a dictionary of directives, using constants from django.utils.csp rather than hand-assembled strings.
  • CSP.NONCE in script-src or style-src, with the middleware generating a unique nonce per request.
  • The django.template.context_processors.csp context processor, exposing {{ csp_nonce }} to templates.

Setting the header

Start in report-only mode. Your URL comes from the Setup page:

from django.utils.csp import CSP
SECURE_CSP_REPORT_ONLY = {
    "default-src": [CSP.SELF],
    "script-src": [CSP.SELF, CSP.NONCE],
    "report-uri": "https://abc123.report-uri.com/r/d/csp/reportOnly",
}
MIDDLEWARE = [
    "django.middleware.csp.ContentSecurityPolicyMiddleware",
    # ...
]

When you are ready to enforce, move the same dictionary to SECURE_CSP and switch the endpoint to the enforce one so the two data sets stay separate in your account.

Adding a nonce

Register the context processor:

TEMPLATES = [{
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "OPTIONS": {
        "context_processors": [
            "django.template.context_processors.csp",
        ],
    },
}]

Then use it on any inline script or style:

<script nonce="{{ csp_nonce }}">
  // allowed by the policy
</script>

Things to watch for

  • SECURE_CSP and SECURE_CSP_REPORT_ONLY are mutually exclusive. If you set both expecting a report-only shadow policy alongside an enforced one, you will not get it - run the report-only phase first, then switch.
  • Do not cache the HTML and the header together. A cached response serves a stale nonce to every visitor, which defeats the point entirely.
  • On Django 5.2 and earlier there is no built-in support; django-csp remains the route until you upgrade.

Official documentation

Django - How to use Django's Content Security Policy

Start Monitoring with Report URI

Already using us for CSP? Find your report-uri value on Setup, or view your CSP reports.

New to Report URI? Create an account and grab your report-uri value from the Setup page.

Start your free trial