文章

Nginx配置深度解析

前端部署的"看门人":静态资源与 SPA 路由怎么配、反向代理怎么解决跨域、负载均衡与限流怎么写,以及 root/alias、location 优先级等易错点。

Nginx配置深度解析

一句话概括

Nginx 是高性能的 HTTP 服务器和反向代理,事件驱动 + 异步非阻塞,几 MB 内存就能扛上万并发。对前端来说它干三件最实在的事:托管打包后的静态资源、把 API 请求反代到后端(顺手解决跨域)、在多台服务间做负载均衡。

面试常问的不是”你会不会配”,而是”root 和 alias 区别、location 匹配顺序、反向代理怎么透传真实 IP、SPA 刷新 404 怎么解“。这些配错一个,页面就白屏或接口就跨域。

核心知识点

1. SPA 静态部署:最关键的一行 try_files

前端 npm run build 出 dist,交给 Nginx 托管。最容易翻车的点是前端路由刷新 404——浏览器直接请求 /user/123,磁盘上没有这个文件,必须用 try_files 兜底回 index.html。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
    listen 80;
    server_name example.com;
    root /var/www/my-app/dist;
    index index.html;

    # 静态资源:长缓存 + 关日志,省 IO
    location ~* \.(js|css|png|jpg|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # SPA 路由兜底:先找文件 → 找目录 → 都没有就回 index.html
    location / {
        try_files $uri $uri/ /index.html;
    }
}

try_files $uri $uri/ /index.html 是单页应用部署的命根子:没它,刷新非根路由就 404。

2. 反向代理:跨域的终极解法

前后端分离时,让 Nginx 同时代理页面和 API,浏览器眼里都在同源,跨域自然消失——比在 Node 里写一堆 CORS 中间件优雅得多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/nginx/ssl/example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    location /api/ {
        proxy_pass http://127.0.0.1:3000;          # 转发到 Node 后端
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;   # 透传真实客户端 IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 必须加这两行,否则长连接建立不起来
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
    }
}
1
2
3
4
// ❌ 不推荐:把跨域甩给代码层,每个服务都得配一遍
app.use(cors({ origin: 'http://localhost:8080' }));

// ✅ 推荐:Nginx 同域反代,前端根本感知不到跨域

3. 负载均衡:把流量分到多台后端

单台 Node 扛不住时,用 upstream 定义后端组,Nginx 按算法分发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upstream backend_nodes {
    # 默认 round-robin;可选 least_conn / ip_hash
    server 192.168.1.10:3000 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:3000 weight=3;
    server 192.168.1.12:3000 backup;   # 备用节点,主全挂才顶上
    keepalive 32;                       # 与后端保持长连接
}

server {
    location / {
        proxy_pass http://backend_nodes;
        proxy_http_version 1.1;
        proxy_set_header Connection "";   # 配合 keepalive 必须清空 Connection
    }
}

4. 限流与安全:别让后端被刷爆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 按客户端 IP 限流:每秒 10 个,突发允许 20 个
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;
        proxy_pass http://backend_nodes;
    }
    # IP 白名单保护后台
    location /admin/ {
        allow 192.168.1.0/24;
        deny all;
    }
}

5. HTTPS 与跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# HTTP 强制跳 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    # HSTS:告诉浏览器以后只走 HTTPS
    add_header Strict-Transport-Security "max-age=63072000" always;
}

其实你每天都在用

  • 部署 Vue/React 打包产物:dist 丢进 root,配 try_files 兜底路由
  • 本地开发联调:vite / webpack-dev-server 的 proxy 本质就是 Nginx 反代思路
  • 线上跨域:前后端同域反代,接口直接 /api/xxx 调用,不用配 CORS
  • 刷新子路由白屏:忘了 try_files 才会 404,这是新手最常见的部署事故
  • 灰度发布:按 Cookie / IP 把部分流量导到新版本(见 FAQ)
  • 接口限流防爬虫:limit_req 挡住恶意刷接口
  • Gzip 压缩:gzip on 让 JS/CSS 体积砍掉一大半,首屏更快
  • WebSocket 代理:聊天室 / 实时推送靠 Upgrade 头透传

常见误解(FAQ)

❌ 误区一:”root 和 alias 差不多,随便用”

差很多,配错路径直接 404。root 是拼接完整 URI:

1
2
location /static/ { root /var/www/app; }
# 请求 /static/a.css → 实际读 /var/www/app/static/a.css

alias 是替换掉匹配的 location 部分:

1
2
location /static/ { alias /var/www/app/public/; }
# 请求 /static/a.css → 实际读 /var/www/app/public/a.css

记住:root 拼、alias 换;且 alias 路径末尾必须带 /。

❌ 误区二:”location 写在前面就优先匹配”

Nginx 的 location 不是按顺序,而是按优先级匹配:

1
2
3
4
5
1. =        精确匹配(最高)
2. ^~       前缀匹配且停止后续正则搜索
3. ~ / ~*   正则匹配(区分/不区分大小写,按出现顺序)
4. 普通前缀  最长前缀匹配
5. /        通用兜底

所以即使正则写在文件后面,也可能优先于前面的普通前缀 location 命中。排配置前先想清楚优先级,否则”明明配了却不生效”。

❌ 误区三:”X-XSS-Protection 加上就安全了”

X-XSS-Protection 这个响应头现代浏览器已废弃并默认关闭(Chrome/Edge 早就不用它,反而可能引入漏洞)。正确做法是靠 CSP(Content-Security-Policy) 限制脚本来源,而不是依赖这个过时的头。生产里可以保留 X-Content-Type-Options: nosniff、Strict-Transport-Security,但别把 XSS 防护押在 X-XSS-Protection 上。

❌ 误区四:”灰度发布就是再起一个 upstream 随便切”

基于 Cookie/IP 的灰度配置里有个常见笔误——有人的示例把 upstream 关键字写成 upstable(少了个 t),Nginx 起都起不来。正确写法:

1
2
3
4
5
6
7
upstream production { server 10.0.1.1:3000; }
upstream canary     { server 10.0.1.3:3000; }   # 新版本
server {
    set $group production;
    if ($http_cookie ~* "canary=true") { set $group canary; }
    location / { proxy_pass http://$group; }
}

灰度本质是”按规则把一小撮流量导到新版本”,配置要可回滚、可观测,别一刀切。

一句话总结

Nginx 是前端部署的”前门”——用 try_files 兜住 SPA 路由、用反代吃掉跨域、用 upstream 分摊流量、用 limit_req 挡住刷子,搞懂 root/alias 与 location 优先级这两个坑,你部署时就不再靠玄学。

本文由作者按照 CC BY 4.0 进行授权