HTML5跨域上传依赖XMLHttpRequest Level 2或fetch配合服务端CORS配置;服务端必须返回Access-Control-Allow-Origin(非*)、Allow-Credentials:true等响应头,前端需显式设置credentials: 'include'或xhr.withCredentials = true。
HTML5 本身不提供跨域上传能力,真正起作用的是 XMLHttpRequest(尤其是 XMLHttpRequest Level 2)配合服务端的 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),必须显式暴露默认情况下,fetch 和 XMLHttpRequest 都不会携带 Cookie 或认证凭据。跨域上传若依赖会话(如登录态),必须手动启用。
使用 fetch 示例:
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('https://www./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./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”状态里——这种失败没有报错信息,最容易被忽略。