Files

88 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: devops.nginx.proxy-static
title: 反向代理、负载均衡和静态资源:location 匹配、proxy_pass、SPA 回退
type: lesson
domain: devops
stack: [nginx]
tags: [反向代理, location, proxy_pass, upstream, try_files, gzip]
level: basic
est_hours: 2
related: [devops.nginx.https-stream-limit, devops.nginx.interview]
week: [3, 7]
order: 1
---
# 反向代理、负载均衡和静态资源:location 匹配、proxy_pass、SPA 回退
## 解释说明
- **正向代理 vs 反向代理**:正向代理代表客户端(翻墙代理),服务器不知道真实客户端;反向代理代表服务端,客户端只看到 Nginx,不知道后面有几台服务器。
- **配置结构**:`http` → `server`(按域名和端口区分站点)→ `location`(按路径匹配)。
- **location 匹配优先级**:`=` 精确匹配最高;然后找最长的前缀匹配,如果它带 `^~` 就直接用;否则按配置顺序检查正则(`~` 区分大小写、`~*` 不区分),第一个匹配的正则胜出;都不匹配就用最长前缀。
- **`proxy_pass` 末尾的斜杠**:`location /api/ { proxy_pass http://backend; }` 会把 `/api/users` 原样转发;`proxy_pass http://backend/;`(带路径)会把匹配的 `/api/` 替换成 `/`,后端收到 `/users`。
- **转发真实客户端信息**:后端看到的来源 IP 是 Nginx,要通过 `X-Real-IP`、`X-Forwarded-For`、`X-Forwarded-Proto` 传递;后端框架要配置信任这个代理(Gin 的 `SetTrustedProxies`、Uvicorn 的 `--forwarded-allow-ips`)。
- **负载均衡**:`upstream` 中列多个 server,默认轮询;还有 `weight` 权重、`least_conn` 最少连接、`ip_hash` 同一 IP 固定到同一台。`max_fails` / `fail_timeout` 做被动健康检查。
- **静态资源**:`root` 拼接路径(`root /var/www;` + `/a.js` → `/var/www/a.js`),`alias` 替换路径;SPA 用 `try_files $uri $uri/ /index.html;` 把前端路由回退到 index.html;带哈希的静态文件设长缓存,`index.html` 不缓存;开启 `gzip`。
## 案例
前后端分离部署在一台服务器:`/` 是 Vue / React 打包后的静态文件,`/api/` 转发到两台 Go API 实例,`/ai/` 转发到 Python AI 服务。刷新 `/orders/42` 时出现 404,原因是没有配置 `try_files` 回退。
## 代码示例
```nginx
upstream api {
least_conn;
server 127.0.0.1:8081 max_fails=3 fail_timeout=10s; # 示例:两个后端实例
server 127.0.0.1:8082 max_fails=3 fail_timeout=10s;
keepalive 32; # 和后端保持长连接
}
server {
listen 80;
server_name example.com;
root /var/www/app/dist;
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
location / {
try_files $uri $uri/ /index.html; # SPA 前端路由回退
}
location = /index.html {
add_header Cache-Control "no-cache"; # 入口文件不缓存,发版立即生效
}
location /assets/ {
expires 1y; # 文件名带哈希,可以长缓存
add_header Cache-Control "public, immutable";
}
location /api/ {
proxy_pass http://api; # 不带路径:/api/users 原样转发
proxy_http_version 1.1;
proxy_set_header Connection ""; # 配合 keepalive
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
}
}
```
可在本机用真实 Nginx 验证的完整配置和测试见 [examples/gateway](examples/gateway/)。修改配置后先 `nginx -t` 检查语法,再 `nginx -s reload` 平滑重载。
## 易错点
- **`proxy_pass` 带不带斜杠搞混**:后端收到的路径多了或少了 `/api`,表现为 404。
- **`root` 和 `alias` 搞混**:`location /static/ { alias /data/files/; }` 中 alias 末尾的斜杠要和 location 一致。
- **SPA 没配 `try_files`**:刷新非首页路由 404。
- **`index.html` 被长缓存**:发版后用户还在用旧页面、引用已删除的旧资源。
- **后端拿到的 IP 全是 Nginx 的**:没传 `X-Forwarded-For` 或后端没配置可信代理;反过来,盲目信任该头会被伪造。
- **改完配置直接 reload 不先 `nginx -t`**:语法错误时 reload 失败(旧配置继续运行),但如果是 restart 就直接起不来了。
## 面试常问
见 [Nginx 面试题](interview.md):正向和反向代理、location 优先级、负载均衡策略、proxy_pass 斜杠问题。