Built-in CSP Support in Django 6.0

Django 6.0 adds native Content Security Policy (CSP) header support. No need to install django-csp anymore: configuration is done directly in settings.py and a middleware automatically handles headers and nonces for inline scripts.

Configuration

Enable the CSP middleware and configure the directives in settings.py. Django automatically generates a unique nonce per request to authorize trusted inline scripts.

python
# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.csp.CSPMiddleware',  # new
    ...
]

CONTENT_SECURITY_POLICY = {
    'default-src': ["'self'"],
    'script-src': ["'self'", "'nonce'"],  # auto-managed nonce
    'style-src': ["'self'", "https://fonts.googleapis.com"],
    'img-src': ["'self'", "data:"],
    'font-src': ["'self'", "https://fonts.gstatic.com"],
    'connect-src': ["'self'"],
}

# Report-only mode for testing without blocking:
CONTENT_SECURITY_POLICY_REPORT_ONLY = True
CONTENT_SECURITY_POLICY_REPORT_URI = '/csp-report/'

Inline Scripts with Nonce

In templates, the {% csp_nonce %} tag inserts the request's unique nonce. Only scripts carrying this nonce are allowed to execute.

htmldjango
{# templates/base.html #}
{% load csp %}
<!DOCTYPE html>
<html>
<head>
  <title>My Secure Site</title>
  {# Inline script authorized by nonce #}
  <script nonce="{% csp_nonce %}">
    console.log('This script is allowed by CSP');
  </script>
</head>
<body>
  {# A script without a nonce will be blocked by the browser #}
  {{ content }}
</body>
</html>

{# Automatically generated HTTP header:
   Content-Security-Policy: default-src 'self';
   script-src 'self' 'nonce-abc123def456'; ... #}

Sources