必须在nginx中配置access-control-expose-headers暴露x-total-count,否则js无法读取;该头仅控制浏览器是否允许脚本访问响应头,不干预服务端发送行为;需在location块中与proxy_pass同级配置,并配合options预检处理及origin动态匹配。

要让浏览器前端 JavaScript 正确读取服务器返回的 X-Total-Count 响应头,必须在 Nginx 的 CORS 配置中显式将其列入 Access-Control-Expose-Headers,否则该头对 JS 不可见——即使后端实际返回了它,response.headers.get('X-Total-Count') 也会返回 null。
必须暴露该响应头
Access-Control-Expose-Headers 是唯一控制“哪些响应头能被前端 JS 读取”的机制。它不决定服务端是否发送该头,只决定浏览器是否允许脚本访问。
- 该字段值为逗号分隔的字符串,中间不能有空格,例如:
"X-Total-Count,X-Request-ID" - 只需列出你前端需要读取的自定义响应头,无需包含标准头(如
Content-Type) - 配置位置建议放在
location块内,与proxy_pass同级,确保所有代理响应都生效
配套处理 OPTIONS 预检请求
如果前端请求携带了自定义请求头(比如 X-App-Version)或使用了非简单方法(如 PUT),浏览器会先发 OPTIONS 预检。此时 Nginx 必须返回完整响应头,否则主请求会被拦截。
- 在
location块中用if ($request_method = 'OPTIONS') { return 204; }拦截预检 - 在
return 204前添加必要头:Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Age -
不要在预检响应中加
Access-Control-Allow-Credentials或Access-Control-Expose-Headers—— 它们只用于实际请求的响应
注意 Origin 和 Credentials 的一致性
若前端设置了 credentials: true(例如带 Cookie 或 Authorization),则 Access-Control-Allow-Origin 不能为 *,且必须与 X-Total-Count 暴露逻辑共存于同一响应中。
- 推荐用
map指令动态匹配可信源,例如:map $http_origin $cors_origin { "~^https?://(app|admin)\.example\.com$" $http_origin; default ""; } - 仅当
$cors_origin非空时才设置Access-Control-Allow-Origin $cors_origin和Access-Control-Allow-Credentials true -
Access-Control-Expose-Headers可独立设置,不受Origin动态影响,只要写在同一个location下即可生效
完整示例片段
以下是一个安全、可复用的配置片段(放在 location /api/ 内):
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Expose-Headers' 'X-Total-Count,X-Request-ID' always;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}











