自定义 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,否则请求会被浏览器拦截。