AJAX 请求报 CSRF token missing 是因 Django 默认要求非安全方法请求携带有效 CSRF Token,而原生 JS 不自动注入;需设 CSRF_COOKIE_HTTPONLY=False 并从 cookie 读取 csrftoken 放入 X-CSRFToken 请求头。

为什么 AJAX 请求总报 CSRF token missing
因为 Django 默认要求所有非 GET/HEAD/OPTIONS 的请求(包括 POST、PUT、DELETE)必须携带有效的 CSRF Token,而原生 fetch 或 XMLHttpRequest 不会自动带上它——哪怕页面已渲染了 {% csrf_token %}。
这不是后端故意设障,而是安全机制:防止跨站伪造请求。但很多人只在表单里加了 {% csrf_token %},就以为“全局生效”了,结果 AJAX 一发就崩。
- 常见错误现象:
403 Forbidden+ 响应体含CSRF token missing or incorrect - 只在模板中写
{% csrf_token %}不够,它只生成一个隐藏 input,不自动注入到 JS 请求头里 - 如果用了
django.middleware.csrf.CsrfViewMiddleware(默认开启),所有非安全方法请求都会校验
如何让 AJAX 携带 CSRF Token(Django 4.2+ 推荐方式)
Django 从 4.2 开始把 CSRF Token 存在 csrftoken Cookie 里,并允许前端直接读取它塞进请求头。这是最轻量、兼容性最好的方案。
- 确保
settings.py中CSRF_COOKIE_HTTPONLY = False(默认是True,必须关掉,否则 JS 读不到) - 确保
CSRF_COOKIE_SAMESITE设为'Lax'或'None'(若需跨站请求,得配CSRF_COOKIE_SECURE=True) - 前端用
document.cookie提取csrftoken,再塞进headers:
const csrftoken = document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1];
fetch('/api/submit/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify({name: 'test'})
});
jQuery 用户别硬套 $.ajaxSetup,小心坑
老项目还在用 jQuery?别直接在 $.ajaxSetup 里写死 X-CSRFToken,因为 Cookie 可能过期或未加载完成,导致后续所有请求都挂。
- 每次请求前动态取值更稳妥,比如封装一个
getCookie(name)函数 -
$.ajaxSetup({ beforeSend })里取不到最新 Cookie 是常见问题——页面刚加载完,CSRF Cookie 还没写入 - 如果用了
django.contrib.staticfiles,确认CSRF_COOKIE_PATH和静态资源路径没冲突(比如设成/admin/就会导致前台 JS 读不到)
API 场景下要不要关 CSRF?看清楚你的认证方式
如果你的接口走的是 TokenAuthentication 或 SessionAuthentication 配合登录态,那不能关 CSRF——关了等于裸奔。
- 只有纯无状态 API(如 JWT,且完全不用 Django session)才考虑用
@csrf_exempt,但仅限个别视图,别全关 -
APIView类里加authentication_classes = [TokenAuthentication]不代表自动跳过 CSRF,仍要校验,除非显式加@method_decorator(csrf_exempt) - 用
djangorestframework-simplejwt时,只要没用SessionAuthentication,通常可以安全地对 API 视图禁用 CSRF(但仍建议优先走 Token Header 方案)
真正容易被忽略的点:CSRF Token 不是“一次生成永久有效”,它随 session 变化,也受 CSRF_COOKIE_AGE 控制;前端缓存了旧 token 却没刷新,就会突然报错——别只测登录成功那一刻。










