Nginx 无法直接用 $server_port 判断前端调试端口,因该变量表示 Nginx 监听端口而非浏览器地址栏端口;正确做法是通过 map 模块提取 $http_origin 中的端口并映射为 $cors_origin,再在 location 中用 add_header 动态设置 Access-Control-Allow-Origin。

在 Nginx 中,仅靠 if 或简单的 location 匹配无法直接根据请求的 目标端口号(即客户端访问的端口)做条件判断——因为 Nginx 的变量如 $server_port 表示的是 Nginx 监听并接收请求的端口,而非浏览器最终要访问的后端服务端口。但实际开发中,前端常通过不同端口(如 http://localhost:3000、http://localhost:8080)调试,需为每个端口配置独立的跨域响应头(如 Access-Control-Allow-Origin)。这时,Map 模块就派上用场了:它支持基于任意变量(包括自定义变量或请求头)做映射,再结合 add_header 实现精准控制。
为什么不能直接用 $server_port 判断前端调试端口?
Nginx 工作在反向代理层,它看到的是自己监听的端口(比如 80/443),而不是浏览器地址栏里写的 :3000。浏览器发出的请求中,Origin 请求头会携带完整协议+域名+端口(如 http://localhost:3000),这才是真正需要识别的“前端来源端口”。因此,正确做法是:
- 提取
$http_origin中的端口号 - 用
map将该端口映射为对应允许的源值或开关标识 - 在
location块中引用映射结果,动态设置响应头
实战:用 Map 提取 Origin 端口并映射允许策略
以下配置实现:当请求来自 localhost:3000 或 localhost:8080 时,分别返回对应的 Access-Control-Allow-Origin;其他来源则不放行(或设为 null 屏蔽)。
将这段写在 http 块内(不在 server 内):
map $http_origin $cors_origin {
~^http://localhost:3000$ "http://localhost:3000";
~^http://localhost:8080$ "http://localhost:8080";
default "";
}
说明:
-
~^...$表示正则匹配,确保完整匹配 Origin 字符串,避免误匹配localhost:30001 - 匹配成功时,
$cors_origin变量被赋值为指定字符串;未匹配则为"" - 该变量可在后续所有
server和location中使用
在 location 中启用跨域响应头
在你的 API 接口 location 块中(例如 location /api/),加入:
location /api/ {
proxy_pass http://backend;
<pre class="brush:php;toolbar:false;"># 启用 CORS
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
# 处理预检请求
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;
}
}
关键点:
-
add_header支持变量,所以可直接用$cors_origin - 若
$cors_origin为空字符串,Nginx 不会发送该 header(符合安全预期) - 预检请求(OPTIONS)必须返回 204,且不能带 body;
add_header在if内也生效
进阶:支持多个子域名 + 动态端口(如 *.local:3000)
如果前端部署在不同子域名(app1.local:3000, app2.local:3000),但都用同一端口调试,可改用更灵活的正则:
map $http_origin $cors_origin {
~^https?://[^/]+:3000$ "$http_origin";
~^https?://[^/]+:8080$ "$http_origin";
default "";
}
说明:
-
[^/]+匹配任意非斜杠字符(即域名部分),忽略协议和端口外的路径 -
$http_origin直接复用,保持原始值,更简洁安全 - 注意:不要在生产环境对任意端口开放,此配置仅适用于本地开发联调
不复杂但容易忽略:Map 必须定义在 http 级,且正则匹配需锚定首尾,否则可能意外命中。只要 Origin 正确传递(浏览器自动加),这套机制就能稳定区分不同调试端口的跨域策略。











