HTML5跨域上传依赖XMLHttpRequest Level 2或fetch配合服务端CORS配置;服务端必须返回Access-Control-Allow-Origin(非*)、Allow-Credentials:true等响应头,前端需显式设置credentials: 'include'或xhr.withCredentials = true。

HTML5 本身不提供跨域上传能力,真正起作用的是 XMLHttpRequest(尤其是 XMLHttpRequest Level 2)配合服务端的 CORS 配置。浏览器禁止前端主动发起跨域请求是安全机制,绕不开服务端协作。
服务端必须返回正确的 CORS 响应头
即使你用 fetch 或 XMLHttpRequest 发送了带凭证的上传请求,只要服务端没配好响应头,浏览器就会直接拦截预检(OPTIONS)或实际请求。
关键响应头至少包括:
-
Access-Control-Allow-Origin:不能为*(如果带credentials),需明确指定源,如https://your-app.com -
Access-Control-Allow-Credentials:设为true(若需传 Cookie 或 Authorization) -
Access-Control-Allow-Methods:至少包含POST,上传通常还涉及OPTIONS -
Access-Control-Allow-Headers:如Content-Type、X-Requested-With等你实际发送的请求头 -
Access-Control-Expose-Headers(可选):若需读取自定义响应头(如X-Upload-Id),必须显式暴露
前端 JS 上传代码要显式开启 credentials
默认情况下,fetch 和 XMLHttpRequest 都不会携带 Cookie 或认证凭据。跨域上传若依赖会话(如登录态),必须手动启用。
立即学习“前端免费学习笔记(深入)”;
使用 fetch 示例:
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('https://www.php.cn/link/f142c6067e6345134c6728f299cf4c1e', {
method: 'POST',
credentials: 'include', // ← 关键:带上 Cookie
body: formData
})
.then(r => r.json())
.catch(err => console.error(err));
使用 XMLHttpRequest 示例:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.php.cn/link/f142c6067e6345134c6728f299cf4c1e', true);
xhr.withCredentials = true; // ← 关键:等价于 fetch 的 credentials: 'include'
xhr.upload.onprogress = e => { /* 进度处理 */ };
xhr.onload = () => { /* 成功回调 */ };
xhr.send(formData);注意:FormData 会自动设置正确的 Content-Type(含 boundary),不要手动覆盖,否则服务端可能无法解析。
避免常见踩坑点
这些错误会导致上传静默失败或卡在预检阶段:
- 服务端对
OPTIONS请求返回 404 或 500,而不是 200 + 正确 CORS 头 - 前端设了
credentials: 'include',但服务端Access-Control-Allow-Origin仍是*→ 浏览器直接拒绝 - 上传大文件时未处理
xhr.upload.onprogress,用户无感知;同时服务端超时(如 Nginx 默认 60s)未调大client_max_body_size和proxy_read_timeout - 用
axios时忘记加withCredentials: true,或在实例配置里漏掉(它不继承全局默认) - 本地开发时前端跑在
http://localhost:3000,后端 API 是http://localhost:8000—— 这仍是跨域,CORS 同样生效
跨域上传真正的难点不在前端写几行 JS,而在于前后端对 CORS 行为的理解是否一致。一个没返回 Access-Control-Allow-Headers 的 OPTIONS 响应,或一个漏掉 withCredentials 的请求,都会让整个流程停在控制台 Network 面板的灰色“canceled”状态里——这种失败没有报错信息,最容易被忽略。











