反向代理是互联网架构里最基础也最关键的一环。一个小小的 Nginx,既能扛住百万级并发,也能让你的服务架构变得灵活可扩展。本文从最基础的反向代理讲起,逐步深入到负载均衡、HTTPS 配置、性能调优,手把手带你成为 Nginx 高手。

什么是反向代理

先区分三个概念:

  • 正向代理:代理客户端(如 VPN、科学上网工具)
  • 反向代理:代理服务端(用户无感知的”中间人”)
  • 透明代理:两者结合,客户端和服务端都不知道

反向代理的核心价值:

  1. 隐藏真实服务:用户只看到代理服务器的 IP
  2. 负载均衡:把请求分发到多台后端
  3. SSL 卸载:HTTPS 加密解密在代理层完成
  4. 缓存加速:静态资源走代理,减轻后端压力
  5. 安全防护:可以过滤恶意请求、做 WAF

第一步:最基础的反向代理

场景

把请求 http://proxy.example.com/ 转发到后端 http://127.0.0.1:8080/

配置

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name proxy.example.com;

location / {
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;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

关键指令解读

  • proxy_pass:转发目标,支持 HTTP/HTTPS/upstream 块
  • proxy_set_header Host $host:必须加,否则后端拿到的 Host 是 proxy_pass 里的地址
  • X-Real-IP:传递客户端真实 IP,后端可以通过这个字段获取
  • X-Forwarded-For:IP 链路,多级代理时会一直追加

第二步:负载均衡

upstream 块配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
upstream backend_pool {
# 负载均衡算法
ip_hash; # 同一 IP 始终路由到同一台后端(解决 session 问题)

server 192.168.1.10:8080 weight=3; # weight 权重,默认 1
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
server 192.168.1.13:8080 backup; # 备用,其他全挂才启用
}

server {
listen 80;
server_name lb.example.com;

location / {
proxy_pass http://backend_pool;
}
}

负载均衡算法

算法指令特点
轮询round-robin(默认)请求均匀分配
加权轮询weight=N性能强的机器多分配
IP Haship_hash同一 IP 固定后端,适合有 session 的应用
最少连接least_conn谁闲谁接
URL Hashurl_hash(需第三方模块)同一 URL 固定后端,适合缓存场景

健康检查

Nginx 自带的是被动健康检查(某台后端连不上就标记 down,但下次还会尝试)。

想要主动健康检查,可以使用 nginx_upstream_check_module:

1
2
3
4
5
6
7
8
upstream backend_pool {
server 192.168.1.10:8080;
server 192.168.1.11:8080;

check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

或者用 Tengine(淘宝基于 Nginx 的增强版),自带主动健康检查。

第三步:HTTPS 配置

申请证书(Let’s Encrypt 免费)

1
2
3
4
5
# 安装 certbot
yum install -y certbot python-certbot-nginx

# 自动申请并配置
certbot --nginx -d example.com -d www.example.com

Nginx HTTPS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# HSTS(强制 HTTPS)
add_header Strict-Transport-Security "max-age=31536000" always;

location / {
proxy_pass http://backend_pool;
}
}

# HTTP 自动跳转 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

第四步:动静分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name static.example.com;

# 静态资源走 Nginx,设置长缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
root /var/www/static;
expires 30d;
add_header Cache-Control "public, immutable";
}

# 动态请求转发到后端
location / {
proxy_pass http://backend_pool;
}
}

第五步:WebSocket 代理

1
2
3
4
5
6
7
location /ws {
proxy_pass http://backend_pool;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s; # 长连接超时
}

第六步:性能调优

worker 配置

1
2
3
4
5
6
7
8
9
10
# 主配置
user www www;
worker_processes auto; # 通常设为 CPU 核心数
worker_rlimit_nofile 65535; # 单进程最大文件描述符

events {
worker_connections 10240; # 单进程最大连接数
use epoll; # Linux 推荐 epoll
multi_accept on;
}

理论最大并发 = worker_processes × worker_connections,但还要除以 2(HTTP 请求一般有客户端连接 + 后端连接)。

缓冲区调优

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
http {
client_body_buffer_size 128k;
client_max_body_size 50m;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;

proxy_buffer_size 16k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}

# Gzip 压缩
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/json application/javascript application/xml;
gzip_vary on;

开启 HTTP/2

HTTP/2 多路复用,性能大幅提升,前面配置已经加了 http2

连接复用

1
2
3
4
5
6
7
8
9
10
upstream backend_pool {
server 192.168.1.10:8080;
keepalive 32; # 与后端保持的长连接数
}

location / {
proxy_pass http://backend_pool;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 清掉 Connection: close
}

第七步:日志分析与监控

自定义日志格式

1
2
3
4
5
6
7
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';

access_log /var/log/nginx/access.log main;

重点关注三个时间:

  • $request_time:整个请求处理时间
  • $upstream_response_time:后端响应时间
  • $upstream_connect_time:与后端建立连接的时间

日志切割

1
2
3
4
5
6
7
#!/bin/bash
# /usr/local/sbin/nginx_log_split.sh
LOG_DIR="/var/log/nginx"
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
mv ${LOG_DIR}/access.log ${LOG_DIR}/access_${YESTERDAY}.log
mv ${LOG_DIR}/error.log ${LOG_DIR}/error_${YESTERDAY}.log
nginx -s reload

加入 crontab:

1
0 0 * * * /bin/bash /usr/local/sbin/nginx_log_split.sh

常见问题排查

502 Bad Gateway

后端连不上。检查:

  • 后端服务是否启动
  • 网络是否通:telnet 192.168.1.10 8080
  • Nginx 错误日志:tail -f /var/log/nginx/error.log

504 Gateway Timeout

后端响应超时。增大超时时间:

1
2
3
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

但最好还是排查后端为什么慢。

客户端真实 IP 丢失

确保配置了 X-Real-IPX-Forwarded-For,并且后端应用正确读取这两个 header。

总结

Nginx 看似简单,实则深不见底。本文带你走完了从基础到进阶的核心知识点:

阶段核心能力
反向代理隐藏后端、传递真实 IP
负载均衡多机协作、健康检查
HTTPS加密通信、HSTS
动静分离性能优化、缓存策略
WebSocket长连接代理
性能调优worker、缓冲区、HTTP/2

学 Nginx 没有捷径,就是多配、多查文档、多踩坑。建议大家把本文的配置都亲手敲一遍,然后在自己博客或者测试服务器上跑起来,这样才能真正掌握。