Nginx中root指令本身不处理跨域,跨域控制依赖响应头;若静态资源路径(如location /)需被跨域访问,必须在该location块内显式配置add_header CORS头并加always参数,且Origin为*时不可设Allow-Credentials为true。

Root 指令本身不处理跨域,它只负责把请求映射到文件系统路径;跨域控制靠的是响应头。两者要配合,关键在于:root 定义静态资源位置,而跨域头必须加在能覆盖这些请求的 location 块里——尤其是当 root 所在的 location 也需被前端跨域访问时(比如直接请求 HTML、JS、字体等静态文件)。
root 所在 location 必须显式加 CORS 头
很多人以为只要给 /api 加了跨域头,整个站点就“通了”,但若前端是通过跨域方式直接加载 /index.html 或 /assets/app.js,而这些路径落在 root 所在的 location(如 location / { root /var/www/html; }),那这个 location 就必须自己配置 add_header,否则浏览器会拒绝加载这些静态资源。
- 错误写法(跨域头只加在 /api 下,/ 根路径无任何 CORS 设置):
location / {
root /var/www/html;
index index.html;
}
location /api/ {
proxy_pass http://backend;
add_header 'Access-Control-Allow-Origin' '*';
}
- 正确写法(根路径也要支持跨域,尤其调试时前端跑在 http://localhost:3000):
location / {
root /var/www/html;
index index.html;
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type' always;
if ($request_method = 'OPTIONS') { return 204; }
}
注意 root 和 alias 的行为差异对跨域的影响
用 root 时,URI 路径会拼接到 root 后面;用 alias 时,URI 被完全替换。这意味着跨域头的作用范围取决于你写的 location 匹配逻辑是否覆盖真实请求路径。
- 例如:
location /static/ { root /data; }→ 请求/static/logo.png对应文件是/data/static/logo.png - 而:
location /static/ { alias /data/assets/; }→ 同样请求对应的是/data/assets/logo.png - 无论哪种,只要该 location 可能被跨域请求命中,就必须在里面加 CORS 头,且不能依赖上级 server 块的全局 add_header(因为 add_header 不继承,只作用于当前块及子块)
带凭证(Cookie)时 root 配置要更严格
如果前端需要带 Cookie 访问静态资源(少见但存在,比如某些单页应用的登录态校验页),那么 Access-Control-Allow-Origin 就不能写 *,必须写具体源,并且要配 Access-Control-Allow-Credentials true。
- 例如允许 https://admin.example.com 加载 /login.html:
location = /login.html {
root /var/www/app;
add_header 'Access-Control-Allow-Origin' 'https://admin.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET' always;
}
- 注意:= 精确匹配可避免误加头到其他路径;always 参数确保即使返回 304 或 404 也会带上 CORS 头(浏览器要求预检和实际响应都需一致)
常见踩坑点
- add_header 写在 server 块顶层,但没加 always → 静态文件返回 304 时 CORS 头丢失,导致跨域失败
- root 和 try_files 混用时,location 匹配优先级混乱,CORS 头没落到最终响应的 location 块里
- 用了 proxy_pass 转发 API,却忘了静态资源(如 favicon.ico、manifest.json)也常被跨域请求,它们走的是 root 路径,同样要配 CORS
- 开发环境前端端口频繁变动(如 Vite 默认 5173、5174),建议用正则动态匹配 origin,或临时用 * + credentials=false 组合(上线前必须改掉 *)


















