什么是反向代理
反向代理就是Nginx接收用户请求后,转发给后端的应用服务器。用户只看到Nginx,不知道后面是谁在处理。
一台VPS上跑3个应用?没问题。Node.js用3000端口,Python用5000端口,Go用8080端口,Nginx在前面接80/443端口,根据域名分发请求。
1
2
3
4
| 用户请求 → Nginx (80/443)
├── blog.example.com → Node.js (:3000)
├── api.example.com → Python (:5000)
└── app.example.com → Go (:8080)
|
基本反向代理配置
最简配置
1
2
3
4
5
6
7
8
9
10
11
12
13
| # /etc/nginx/sites-available/myapp
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
|
1
2
3
4
5
| $ sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx
|
| Header | 作用 | 为什么需要 |
|---|
Host | 传递原始域名 | 后端知道请求的是哪个域名 |
X-Real-IP | 传递真实客户端IP | 后端获取用户真实IP |
X-Forwarded-For | IP转发链 | 经过多级代理时保留所有IP |
X-Forwarded-Proto | 原始协议 | 后端知道用户用的是HTTP还是HTTPS |
多站点配置实战
Node.js应用
1
2
3
4
5
6
7
8
9
10
11
12
| server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
|
Python Flask/Django应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:5000;
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;
# 超时设置(API可能耗时较长)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 静态文件直接由Nginx处理
location /static/ {
alias /var/www/api/static/;
expires 30d;
}
}
|
静态站点 + API代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| server {
listen 80;
server_name www.example.com;
# 前端静态文件
root /var/www/frontend/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# API请求转发到后端
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
|
WebSocket代理
如果应用使用WebSocket(聊天、实时推送等),需要额外配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket超时(默认60秒会断开)
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
|
负载均衡
当一个应用跑多个实例时,用Nginx做负载均衡:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 定义上游服务器组
upstream myapp {
# 默认轮询策略
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://myapp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
|
负载均衡策略
| 策略 | 配置 | 说明 |
|---|
| 轮询 | 默认 | 依次分配请求 |
| 权重 | server ... weight=3 | 按权重比例分配 |
| IP Hash | ip_hash; | 同一IP固定到同一后端 |
| 最少连接 | least_conn; | 分配给连接数最少的 |
1
2
3
4
5
6
| upstream myapp {
least_conn; # 最少连接策略
server 127.0.0.1:3001 weight=3; # 权重3
server 127.0.0.1:3002 weight=1; # 权重1
server 127.0.0.1:3003 backup; # 备用,其他都挂了才用
}
|
代理缓存
对于不经常变化的内容,Nginx可以缓存后端响应:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 在http块中定义缓存
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cache my_cache;
proxy_cache_valid 200 10m; # 200响应缓存10分钟
proxy_cache_valid 404 1m; # 404缓存1分钟
add_header X-Cache-Status $upstream_cache_status;
}
}
|
1
2
3
4
| # 测试缓存是否生效
$ curl -I http://app.example.com
HTTP/1.1 200 OK
X-Cache-Status: HIT # HIT表示命中缓存,MISS表示没命中
|
常见问题
502 Bad Gateway
1
2
3
4
5
6
7
| # 后端服务没启动或崩了
$ curl http://127.0.0.1:3000
curl: (7) Failed to connect to 127.0.0.1 port 3000
# 检查后端服务状态
$ systemctl status myapp
$ docker ps # 如果是Docker部署的话
|
真实IP获取不到
后端应用要从Header里读取真实IP:
1
2
3
4
5
| # Python Flask
real_ip = request.headers.get('X-Real-IP', request.remote_addr)
# Node.js Express
const realIP = req.headers['x-real-ip'] || req.connection.remoteAddress;
|
上传文件大小限制
1
2
3
4
5
| # Nginx默认限制1MB,修改为50MB
server {
client_max_body_size 50m;
# ...
}
|
测试配置
1
2
3
4
5
6
7
8
9
| # 永远先测试再重载
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx
# 检查反向代理是否生效
$ curl -H "Host: app.example.com" http://127.0.0.1
|
总结
反向代理是Nginx最强大的功能之一。掌握了它,一台服务器就能跑无数个应用和网站。核心就是记住proxy_pass加上几个proxy_set_header。
配置完反向代理后,别忘了配SSL证书,参考免费SSL证书申请与配置。Nginx的基础安装和配置可以回顾Nginx安装与配置入门。