原生 form 不支持自定义请求头,必须用 fetch 或 XMLHttpRequest 替代;需拦截 submit、用 FormData 提取数据、正确设置 headers 和 credentials,并确保后端 CORS 配置匹配。

原生 form 根本不支持自定义请求头
浏览器对 form.submit() 的行为做了硬性限制:它只允许发出“简单请求”,Authorization、X-Request-ID、X-API-Key 等自定义头一律被忽略。你写 form.headers = {...}、加 data-headers 属性、甚至用 meta 标签声明,全无效——DOM 没这个 API,浏览器也不解析。
常见错误现象包括:
- 控制台报
Refused to set unsafe header "Content-Type"(尤其在手动设multipart/form-data时) - 页面刷新了,但带 token 的请求没发出去(忘了
event.preventDefault()) - DevTools Network 面板里看不到自定义头,但后端却收不到认证信息
必须用 fetch() 或 XMLHttpRequest 替代提交
这是唯一可靠路径。核心是拦截原生提交、构造可控请求。关键点不是“怎么写 fetch”,而是“怎么不破坏表单语义”:
- 保留
<form>结构和所有name属性,确保无障碍支持和无 JS 降级能力 - 监听
submit事件,第一时间调用event.preventDefault() - 用
new FormData(form)提取字段(自动处理文件、多选、空值等),别手拼 JSON 或 URL 编码 -
fetch(url, { method: 'POST', body: formData, headers: { 'Authorization': 'Bearer ' + token } })—— 注意:不要 在headers里写Content-Type,浏览器会自动加boundary,手动设就崩
fetch 的 headers 写法和坑点
headers 只接受 plain object 或 Headers 实例,传字符串或数组直接报错。但更隐蔽的坑在细节:
立即学习“前端免费学习笔记(深入)”;
- 敏感头如
Origin、Referer、Cookie、Content-Length会被浏览器强制屏蔽或覆盖,写了也白写 - 自定义头名含下划线(如
X-Client-ID)没问题,但大小写不敏感;用Headers构造器比对象字面量更稳(自动归一化) - 只要用了非白名单头(比如
Authorization),浏览器必先发一次OPTIONS预检——后端必须响应Access-Control-Allow-Headers: Authorization,且不能写*(除非同时配Access-Control-Allow-Credentials: false) - 如果要带 Cookie,
fetch得加credentials: 'include',否则后端收不到 session
后端 CORS 配置不能省
前端改完,后端不配合照样失败。最常漏的是这三项:
-
Access-Control-Allow-Origin:不能只写*(尤其带凭证时),得明确指定源或动态回写请求头中的Origin -
Access-Control-Allow-Headers:必须显式列出你用的头,如Authorization, X-Request-ID, Content-Type;*在带凭证时无效 -
Access-Control-Allow-Credentials:只要前端设了credentials: 'include',这项就必须为true,且Allow-Origin不能是*
文件上传这类场景还要额外确认后端是否支持 multipart/form-data 解析(比如 Express 要装 multer,Spring Boot 要开 spring.servlet.multipart 配置)——Header 对了,body 解不出来,一样 400。



















