表单提交时无法通过原生设置自定义请求头,必须用javascript(如fetch)发起请求并手动添加x-csrf-token头;需从读取token,阻止默认提交后构造带该头的post请求,且后端须在cors中明确允许该头。

表单提交时如何在请求头里带 X-CSRF-Token
浏览器原生 <form></form> 提交无法设置自定义请求头,所以直接往 headers 里塞 X-CSRF-Token 是无效的。必须改用 JavaScript 发起提交,比如 fetch 或 XMLHttpRequest。
常见错误是把 Token 存在 hidden input 里就以为“已带上”,其实那只是传了参数,不是请求头——后端中间件(如 Express 的 csurf、Laravel 的 VerifyCsrfToken)通常只检查请求头或特定字段名(如 _token),不认任意 hidden 字段。
- 从 meta 标签或 JS 变量中读取 Token 值(推荐放在
<meta name="csrf-token" content="xxx">) - 阻止表单默认提交:
event.preventDefault() - 用
fetch()构造 POST 请求,手动设置headers: { 'X-CSRF-Token': token } - 注意
Content-Type:若后端依赖application/x-www-form-urlencoded解析,需用URLSearchParams构建 body;若接受 JSON,则用JSON.stringify
const form = document.querySelector('form');
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new URLSearchParams(new FormData(form));
await fetch(form.action, {
method: 'POST',
headers: { 'X-CSRF-Token': token },
body: data
});
});
后端为什么收不到 X-CSRF-Token?
不是前端没发,而是预检(OPTIONS)失败或请求被浏览器拦截。CORS 场景下,如果后端没明确允许该 header,浏览器会直接拒绝发送。
- 确保后端响应中包含
Access-Control-Allow-Headers: X-CSRF-Token - 若用 Nginx 转发,确认没过滤掉自定义 header(
underscores_in_headers off是默认值,但某些配置会开启并丢弃含下划线的 header;建议用短横线命名如X-Csrf-Token) - Express + cors 中间件需显式配置:
allowedHeaders: ['X-Csrf-Token'] - 开发时检查 Network 面板:若请求方法显示为
OPTIONS且状态码是 403/502,基本就是 header 白名单问题
fetch 提交 vs FormData 直传 Token 字段的区别
二者都能过验证,但机制不同:前者靠请求头触发中间件校验逻辑,后者靠表单字段名(如 Laravel 的 _token、Django 的 csrfmiddlewaretoken)匹配校验规则。
- 用字段方式更兼容老后端,无需改 CORS 配置,但 Token 会出现在请求体明文里(虽在 HTTPS 下安全,但日志或调试中易暴露)
- 用 header 方式更“干净”,适合 API 场景,但要求前后端约定 header 名且后端正确解析(例如 Rails 默认只认
X-CSRF-Token,不认X-XSRF-Token) - Spring Security 默认检查 header
X-XSRF-TOKEN和 cookieXSRF-TOKEN是否一致,不能混用 Laravel 的命名
Token 过期或重复使用导致 403 怎么办
CSRF Token 一般是一次性或短期有效(如 2 小时),刷新页面未更新 Token、多标签页共用同一 Token、或异步请求未及时续期,都会触发 403。
- 登录/关键操作后,后端应返回新 Token,前端需同步更新
<meta>或全局变量 - 避免在 SPA 中长期缓存 Token;每次表单渲染前,可先 GET
/api/csrf-token拿最新值(注意该接口本身要豁免 CSRF 校验) - 不要在多个
fetch并发提交时共享同一个 Token 变量——如果 A 请求耗时长,B 请求可能用的是已失效的旧值 - 后端报错信息如果是
TokenMismatchException(Laravel)或InvalidCsrfTokenException(Spring),说明 Token 校验环节已运行,问题出在生成、传递或时效上,不是跨域或 header 遗漏
Token 机制本质是“绑定用户会话 + 一次一换”,所有绕过刷新、复用、硬编码的行为,都会在某个并发或超时节点上露出马脚。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











