Django 4.2 不内置 CSP,必须用 django-csp;配置时 CSP_DEFAULT_SRC 必须为元组或列表,EXCLUDE_URL_PREFIXES 必须为 list/tuple,nonce 需密码学安全且每次响应唯一。

直接上结论:Django 4.2 本身不内置 CSP 功能,必须用 django-csp 或 Django 5.0+ 的原生 ContentSecurityPolicyMiddleware;你如果还在用 4.2,就别折腾自定义中间件,老实用 django-csp,但得避开它那几个“一写错就全站裸奔”的坑。
django-csp 安装与基础配置必须用 pip install django-csp
别信某些教程说“Django 自带 CSP”,那是 5.0 才加的。4.2 只能靠第三方包:pip install django-csp。装完后在 settings.py 里加中间件和配置:
-
MIDDLEWARE里插入'csp.middleware.CSPMiddleware'(位置建议在SecurityMiddleware之后、SessionMiddleware之前) - 定义
CSP_DEFAULT_SRC或完整CONTENT_SECURITY_POLICY字典 - 别漏掉
INSTALLED_APPS += ['csp'],否则manage.py check会报csp.E001
CSP_DEFAULT_SRC 写成字符串会导致全站策略失效
常见错误是这样写:
CSP_DEFAULT_SRC = "https://cdn.example.com"
Python 会把字符串当可迭代对象拆成字符列表,结果等价于 ["h", "t", "t", "p", ...],而浏览器只认合法源字符串,最终该指令被丢弃——等于没配。正确写法只有两种:
立即学习“Python免费学习笔记(深入)”;
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 单源:用元组或列表包裹,如
CSP_DEFAULT_SRC = ("https://cdn.example.com",) - 多源:明确写成列表,如
CSP_DEFAULT_SRC = ["'self'", "https://cdn.example.com"] - 注意:
'self'必须带单引号,且必须是字符串字面量或csp.constants.SELF,不能写成SELF(未导入)或self(Python 关键字)
EXCLUDE_URL_PREFIXES 必须是 list/tuple,写字符串会触发 csp.E002 报错
想排除后台不加 CSP 头?别这么写:
CONTENT_SECURITY_POLICY = { "EXCLUDE_URL_PREFIXES": "/admin" }
这会让 "/admin" 被拆成 ['/', 'a', 'd', 'm', 'i', 'n'],所有以 / 开头的请求(也就是全部)都被排除,CSP 彻底失效。django-csp 会在 manage.py check 时直接抛出 csp.E002 错误。正确姿势:
- 必须是 list 或 tuple:
"EXCLUDE_URL_PREFIXES": ["/admin", "/api/health"] - 路径末尾不加斜杠(
"/admin/"≠"/admin"),匹配逻辑是前缀匹配 - 如果要用正则排除,得换方案——
django-csp不支持正则,得自己写中间件覆盖CSPMiddleware.process_request
nonce 支持需手动注入,django-htmx 等库依赖它但不自动提供
Django 4.2 没有 get_csp_nonce() 这种函数,django-htmx 的 {{ htmx_script(nonce=csp_nonce) }} 标签只是“被动接收”,nonce 值得你自己生成并传入模板上下文:
- 在视图里用
from django.utils.crypto import get_random_string生成:csp_nonce = get_random_string(16) - 把
csp_nonce加进context,再传给模板 - 对应 CSP 指令必须包含
'nonce-{csp_nonce}',例如:CSP_SCRIPT_SRC = ["'self'", "'nonce-{csp_nonce}'"](注意这里要格式化字符串或用 f-string 拼接) - 别用
random.choice或时间戳——nonce 必须密码学安全,否则形同虚设
最易被忽略的是:nonce 是一次性的,每个响应都该不同;但很多人图省事全局复用一个值,这会让攻击者拿到一次 nonce 就能绕过所有脚本限制。

















