自定义 Header + CORS 简介
自定义 Header 是指在 HTTP 请求或响应中添加非标准的头部字段。CORS(跨域资源共享) 是浏览器的一种安全机制,用于控制跨域请求的权限。两者经常一起出现,因为自定义 Header 会触发 CORS 预检请求。
什么是自定义 Header
标准 Header vs 自定义 Header
| 类型 | 示例 | 说明 |
|---|---|---|
| 标准 Header | Content-Type, Authorization, Accept |
HTTP 协议定义的通用头部 |
| 自定义 Header | X-Request-ID, X-Auth-Token, App-Version |
以 X- 开头或自定义的非标准头部 |
常见自定义 Header 场景
# 请求头
X-Request-ID: abc123 # 请求追踪ID
X-Client-Version: 2.1.0 # 客户端版本号
X-Device-Type: mobile # 设备类型
X-Signature: a1b2c3d4 # 请求签名
# 响应头
X-RateLimit-Remaining: 99 # 剩余请求次数
X-Response-Time: 45ms # 服务器处理时间
X-Debug-Info: cache-hit # 调试信息
自定义 Header 与 CORS 的关系
简单请求 vs 预检请求
浏览器将跨域请求分为两类:
| 请求类型 | 条件 | 是否会触发预检 |
|---|---|---|
| 简单请求 | 仅使用 GET/POST/HEAD,Content-Type 为 text/plain/multipart/form-data/application/x-www-form-urlencoded,不使用自定义 Header |
❌ 不会 |
| 预检请求 | 使用了自定义 Header、非简单方法(PUT/DELETE)、特殊 Content-Type | ✅ 会 |
重点:只要请求中包含自定义 Header,浏览器就会先发一个 OPTIONS 预检请求。
完整流程示例
前端发送带自定义 Header 的请求
// Vue 中使用 axios
axios.get('https://api.example.com/users', {
headers: {
'X-Request-ID': 'abc123', // 自定义 Header
'X-Client-Version': '2.1.0', // 自定义 Header
'Authorization': 'Bearer token' // 标准 Header(不会触发预检)
}
})
浏览器自动发送的预检请求
OPTIONS https://api.example.com/users
Origin: https://myapp.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-request-id, x-client-version
服务器需要返回的响应头
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Request-ID, X-Client-Version
Access-Control-Max-Age: 86400
服务器配置示例
Node.js(Express)
app.use((req, res, next) => {
// 允许的域名
res.header('Access-Control-Allow-Origin', 'https://myapp.com')
// 允许的方法
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
// 允许的自定义 Header(关键!)
res.header('Access-Control-Allow-Headers', 'X-Request-ID, X-Client-Version')
// 预检请求直接返回
if (req.method === 'OPTIONS') {
return res.sendStatus(204)
}
next()
})
Nginx
location /api/ {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE";
add_header Access-Control-Allow-Headers "X-Request-ID, X-Client-Version";
if ($request_method = 'OPTIONS') {
return 204;
}
}
关键 CORS 响应头详解
| 响应头 | 作用 | 示例 |
|---|---|---|
Access-Control-Allow-Origin |
允许哪些域名访问 | * 或 https://myapp.com |
Access-Control-Allow-Methods |
允许哪些 HTTP 方法 | GET, POST, PUT |
Access-Control-Allow-Headers |
允许哪些自定义 Header | X-Request-ID, X-Auth-Token |
Access-Control-Expose-Headers |
允许前端 JS 读取哪些响应头 | X-RateLimit-Remaining |
Access-Control-Max-Age |
预检结果缓存时间(秒) | 86400(24小时) |
Access-Control-Allow-Credentials |
是否允许携带 Cookie | true |
常见问题与解决
问题1:请求发不出去,控制台报 CORS 错误
Access to XMLHttpRequest at 'https://api.example.com/data'
from origin 'https://myapp.com' has been blocked by CORS policy:
Request header field x-custom-header is not allowed by
Access-Control-Allow-Headers in preflight response.解决方法:在服务器 Access-Control-Allow-Headers 中添加该自定义 Header。
问题2:前端读取不到自定义响应头
// 前端代码
const rateLimit = response.headers['x-ratelimit-remaining']
console.log(rateLimit) // null
解决方法:服务器需要添加 Access-Control-Expose-Headers:
Access-Control-Expose-Headers: X-RateLimit-Remaining, X-Response-Time
问题3:携带 Cookie 的跨域请求
axios.get('https://api.example.com/data', {
withCredentials: true, // 携带 Cookie
headers: {
'X-CSRF-Token': 'abc123'
}
})
服务器需要额外配置:
Access-Control-Allow-Origin: https://myapp.com // 不能使用 *
Access-Control-Allow-Credentials: true
最佳实践
| 建议 | 说明 |
|---|---|
| 避免滥用自定义 Header | 能用标准 Header 解决的尽量用标准 Header |
| 统一命名规范 | 自定义 Header 建议统一前缀,如 X-App-、X-Trace- |
| 最小化暴露 | Access-Control-Allow-Headers 只列出实际需要的自定义 Header |
| 设置合理的 Max-Age | 减少预检请求频率,提高性能(建议 1 小时以上) |
生产环境不要用 * |
明确指定允许的域名,避免安全隐患 |
小结
自定义 Header 是 HTTP 的”自创字段”,但一旦使用就会触发浏览器的 CORS 预检机制。 需要在服务器端明确声明 Access-Control-Allow-Headers 来放行这些自定义 Header,否则请求会被浏览器拦截。
