反向代理
反向代理是 Nginx 最重要的功能之一。本章将详细介绍如何配置 Nginx 作为反向代理服务器。
什么是反向代理?
正向代理 vs 反向代理
正向代理(Forward Proxy):
客户端 → 代理服务器 → 目标服务器
例如:VPN、翻墙工具
- 客户端知道目标服务器
- 目标服务器不知道真实客户端
- 代理代表客户端
反向代理(Reverse Proxy):
客户端 → 反向代理服务器 → 后端服务器
例如:Nginx、CDN
- 客户端不知道后端服务器
- 后端服务器知道真实客户端(通过头部传递)
- 代理代表服务器
解释:
- 正向代理:代理客户端,帮助客户端访问无法直接访问的资源
- 反向代理:代理服务器,隐藏后端服务器,提供负载均衡、缓存等功能
反向代理的优势
- 安全:隐藏后端服务器的真实地址
- 负载均衡:分发请求到多个后端服务器
- 缓存:缓存静态内容,减少后端压力
- SSL 终端:集中处理 HTTPS,简化后端配置
- 压缩:减少传输数据量
- 静态资源分离:处理静态文件,减轻应用服务器负担
基本配置
proxy_pass 指令
proxy_pass 是反向代理的核心指令:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
解释:
- 所有访问
example.com的请求都会被转发到http://127.0.0.1:8080 - 客户端看到的是
example.com,不知道实际请求的是:8080端口
代理到不同路径
location /api/ {
# 请求 /api/users 会转发到 http://backend/api/users
proxy_pass http://backend;
}
location /app/ {
# 请求 /app/users 会转发到 http://backend/users(去掉 /app 前缀)
proxy_pass http://backend/;
}
解释:
- 如果
proxy_pass有 URI(如/),会替换匹配的路径 - 如果没有 URI,会保留原始路径
常用代理头设置
传递客户端信息
location / {
proxy_pass http://backend;
# 传递客户端真实 IP
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;
}
解释:
Host:原始请求的主机名X-Real-IP:客户端真实 IPX-Forwarded-For:代理链,记录客户端和各级代理的 IPX-Forwarded-Proto:原始请求的协议(http/https)
完整的代理头配置
location / {
proxy_pass http://backend;
# 基本代理头
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# 连接信息
proxy_set_header Connection "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Nginx 版本
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
超时配置
连接和响应超时
location / {
proxy_pass http://backend;
# 连接后端的超时时间
proxy_connect_timeout 60s;
# 读取响应的超时时间
proxy_read_timeout 60s;
# 发送请求的超时时间
proxy_send_timeout 60s;
}
解释:
proxy_connect_timeout:建立连接的超时,通常设置较短proxy_read_timeout:等待后端响应的超时,根据业务调整proxy_send_timeout:向后端发送请求的超时
长连接配置
upstream backend {
server 127.0.0.1:8080;
# 保持长连接
keepalive 32;
}
location / {
proxy_pass http://backend;
# 启用 HTTP/1.1 长连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
解释:
keepalive:保持到后端的长连接数量proxy_http_version 1.1:使用 HTTP/1.1 支持长连接Connection "":清除 Connection 头,允许长连接
缓冲配置
代理缓冲
location / {
proxy_pass http://backend;
# 开启代理缓冲
proxy_buffering on;
# 缓冲区数量和大小
proxy_buffers 8 16k;
# 单个缓冲区大小(用于响应头)
proxy_buffer_size 4k;
# 忙碌时的缓冲区大小
proxy_busy_buffers_size 32k;
# 临时文件大小限制
proxy_max_temp_file_size 256m;
}
解释:
proxy_buffering on:开启后,Nginx 会缓冲后端响应proxy_buffers:第一个参数是数量,第二个是每个大小- 缓冲可以提高性能,但增加内存使用
实际场景配置
场景 1:代理 Node.js 应用
server {
listen 80;
server_name example.com;
# 静态文件直接由 Nginx 处理
location /static/ {
alias /var/www/static/;
expires 30d;
}
# 动态请求代理到 Node.js
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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
}
}
场景 2:代理 Python Flask/Django 应用
server {
listen 80;
server_name example.com;
# 静态文件
location /static/ {
alias /var/www/static/;
expires 30d;
}
# 上传文件大小限制
client_max_body_size 10M;
# 代理到 Gunicorn/uWSGI
location / {
proxy_pass http://unix:/tmp/gunicorn.sock;
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;
}
}
场景 3:代理 Spring Boot 应用
server {
listen 80;
server_name example.com;
# 健康检查
location /health {
proxy_pass http://127.0.0.1:8080;
access_log off;
}
# 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;
proxy_set_header X-Forwarded-Proto $scheme;
# Spring Boot 特定
proxy_set_header X-Request-ID $request_id;
}
}
场景 4:代理 WebSocket
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com;
location /ws/ {
proxy_pass http://127.0.0.1:8080;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
# WebSocket 超时设置
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
解释:
- WebSocket 需要
Upgrade和Connection头 - 使用
map指令处理不同的连接类型 - WebSocket 连接可能保持很长时间,需要设置较长的超时
场景 5:代理 GraphQL
server {
listen 80;
server_name example.com;
location /graphql {
proxy_pass http://127.0.0.1:4000;
# GraphQL 请求可能很大
client_max_body_size 10M;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 禁用缓存(如果需要)
proxy_buffering off;
}
}
高级配置
请求重写
location /old-api/ {
# 重写 URL
rewrite ^/old-api/(.*)$ /api/v1/$1 break;
proxy_pass http://backend;
}
location /api/v1/ {
# 移除前缀
rewrite ^/api/v1/(.*)$ /$1 break;
proxy_pass http://backend;
}
条件代理
# 根据 User-Agent 代理到不同后端
location / {
if ($http_user_agent ~* "mobile") {
proxy_pass http://mobile-backend;
}
proxy_pass http://desktop-backend;
}
# 根据请求头代理
location / {
set $backend "default";
if ($http_x_backend = "new") {
set $backend "new-backend";
}
proxy_pass http://$backend;
}
限制代理
# 限制上传大小
location /api/upload {
client_max_body_size 50M;
proxy_pass http://backend;
}
# 限制请求方法
location /api/ {
limit_except GET POST {
deny all;
}
proxy_pass http://backend;
}
故障处理
超时和错误处理
location / {
proxy_pass http://backend;
# 连接失败时返回 502/503/504
proxy_next_upstream error timeout http_502 http_503 http_504;
# 超时时间
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
自定义错误页面
# 自定义 502 错误页面
error_page 502 /502.html;
location = /502.html {
root /usr/share/nginx/errors;
internal;
}
# 自定义 504 错误页面
error_page 504 /504.html;
location = /504.html {
root /usr/share/nginx/errors;
internal;
}
调试和日志
代理日志
log_format proxy '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'upstream: $upstream_addr '
'response_time: $upstream_response_time '
'status: $upstream_status';
server {
access_log /var/log/nginx/proxy.log proxy;
}
调试信息
location / {
proxy_pass http://backend;
# 添加调试头
add_header X-Backend $upstream_addr;
add_header X-Response-Time $upstream_response_time;
add_header X-Request-ID $request_id;
}
安全建议
1. 隐藏后端信息
# 移除敏感头
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
# 设置自定义 Server 头
more_set_headers 'Server: Web Server';
2. 限制请求方法
location /api/ {
# 只允许 GET 和 POST
if ($request_method !~ ^(GET|POST)$) {
return 405;
}
proxy_pass http://backend;
}
3. 验证请求来源
location /internal/ {
# 只允许内网访问
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://internal-backend;
}
小结
本章我们学习了:
- 反向代理的概念和优势
- proxy_pass 基本配置
- 代理头设置(Host、X-Real-IP、X-Forwarded-For)
- 超时和缓冲配置
- 各种实际场景的配置示例
- WebSocket 代理
- 高级配置技巧
- 安全建议
练习
- 配置 Nginx 代理一个本地 Node.js 应用
- 配置 WebSocket 代理
- 设置自定义错误页面
- 添加调试头信息