Nginx通过map匹配User-Agent并用if动态注入CORS响应头,仅对指定UA允许跨域:预检请求返回204并设全量CORS头,常规请求仅匹配时添加Access-Control-Allow-Origin等头,严禁未匹配UA返回任何CORS头。

只对特定 User-Agent 开放 CORS 权限,本质是通过 Nginx 的请求头匹配 + 条件响应头注入来实现。核心不在“跨域本身”,而在“精准控制谁有权限跨域”——不能靠前端判断,必须由 Nginx 在响应阶段动态决定是否写入 Access-Control-Allow-Origin 等头。
识别并匹配目标 User-Agent
Nginx 使用 $http_user_agent 变量获取请求头中的 User-Agent 字符串,配合 map 指令可高效做模糊或精确匹配。推荐用 map 预定义布尔标记,避免在每个 location 中重复正则判断。
例如,只允许来自某款内部 App(User-Agent 包含 MyApp/2.1.0)的跨域请求:
map $http_user_agent $cors_allowed {
default 0;
~*MyApp/2\.1\.0 1;
}
注意:正则需转义点号,~* 表示忽略大小写;值为字符串,但后续可用于条件判断(Nginx 会自动将非空非"0"视为真)。
动态注入 CORS 响应头
在 server 或 location 块中,使用 if 判断 $cors_allowed,仅当为真时添加 CORS 头。关键点是:这些头必须在响应生成前设置,且不能与预检请求(OPTIONS)冲突。
- 对非预检请求(如 GET/POST),直接添加
Access-Control-Allow-Origin和其它必要头 - 对 OPTIONS 预检请求,需显式返回 204 并设置全部预检相关头
- 避免在非匹配 UA 请求中写入任何 CORS 头,否则可能被浏览器拒绝(例如写了
Access-Control-Allow-Origin: *但没带凭证支持,而请求又带了 cookies)
完整配置示例(含预检支持)
以下配置放在 location 块内,假设服务接口路径为 /api/:
# 允许预检请求直接返回,不代理到后端
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "https://www.php.cn/link/7e3da1dca2700e3225382921dd70b8c7";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
<h1>对常规请求,仅当 UA 匹配时注入 CORS 头</h1><p>if ($cors_allowed) {
add_header Access-Control-Allow-Origin "<a href="https://www.php.cn/link/7e3da1dca2700e3225382921dd70b8c7">https://www.php.cn/link/7e3da1dca2700e3225382921dd70b8c7</a>";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
}</p>说明:
– Access-Control-Allow-Origin 不建议用 *,尤其开启凭据(credentials)时必须指定明确域名;
– 所有 CORS 头应在 if 块中统一添加,确保未匹配 UA 的请求完全不携带这些头;
– 预检响应必须含 Content-Length 0 和 return 204,否则可能触发双重响应错误。
验证与调试要点
实际生效前务必验证三类请求:
- 用 curl 模拟目标 UA 发起真实请求,检查响应头是否含 CORS 字段
- 用 curl 发送 OPTIONS 预检请求(带
-X OPTIONS -H "Origin: https://www.php.cn/link/7e3da1dca2700e3225382921dd70b8c7"),确认返回 204 且头完整 - 换一个不匹配的 UA(如 curl 默认 UA)发起相同请求,确认响应中 完全不出现 任何
Access-Control-头
若浏览器仍报跨域错误,优先检查:是否后端也输出了冲突的 CORS 头(导致重复)、是否 HTTPS 环境下 Origin 协议不一致、是否遗漏 Access-Control-Allow-Credentials 但前端设了 withCredentials: true。


















