--- description: How to configure Content Security Policy in Django using the built-in SECURE_CSP settings, and send violation reports to Report URI. --- # 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](https://report-uri.com/account/setup/) page: ```python 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: ```python TEMPLATES = [{ "BACKEND": "django.template.backends.django.DjangoTemplates", "OPTIONS": { "context_processors": [ "django.template.context_processors.csp", ], }, }] ``` Then use it on any inline script or style: ```html ``` ## 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](https://docs.djangoproject.com/en/6.0/howto/csp/) ## Start Monitoring with Report URI **Already using us for CSP?** Find your report-uri value on [Setup](https://report-uri.com/account/setup/), or view your [CSP reports](https://report-uri.com/account/reports/csp/). **New to Report URI?** Create an account and grab your report-uri value from the [Setup](https://report-uri.com/account/setup/) page. [Start your free trial](https://report-uri.com/register/?plan=starter2025)