文章
Nginx 学习笔记:反向代理与部署实战
牛耕田
暂存笔记,持续补充中。Nginx 是后端部署的「第一道门」,几乎每个 Java 项目都跑在它后面。
一、Nginx 是什么、为什么快
Nginx 是一个事件驱动、异步非阻塞的高性能 Web 服务器 / 反向代理服务器。
| 对比 | Apache | Nginx |
|---|---|---|
| 并发模型 | 进程/线程 per connection | epoll 事件驱动 |
| 内存占用 | 高 | 低 |
| 静态资源 | 一般 | 极强 |
| 高并发 | 一般 | 优秀(单机轻松万级) |
为什么快:
- epoll 多路复用:一个 worker 进程用事件循环处理成千上万连接,无线程切换开销。
- master-worker 架构:master 管配置与 worker,worker 处理请求,worker 数通常设为 CPU 核数。
- 零拷贝(
sendfile):静态文件直接从内核缓冲区发送,不经用户态。
二、配置文件结构
# 全局块
user nginx;
worker_processes auto; # 通常 = CPU 核数
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# events 块
events {
worker_connections 10240; # 单 worker 最大连接数
use epoll;
multi_accept on;
}
# http 块
http {
include mime.types;
default_type application/octet-stream;
sendfile on; # 开启零拷贝
keepalive_timeout 65;
client_max_body_size 50m; # 上传大小限制
# server 块(一个虚拟主机)
server {
listen 80;
server_name sxz-blog.xyz;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
匹配优先级(location):
= 精确匹配 > ^~ 前缀匹配 > ~ / ~* 正则匹配 > / 通用前缀
location = /favicon.ico { } # 只匹配 /favicon.ico
location ^~ /static/ { } # 匹配 /static/ 开头,不再走正则
location ~ \.php$ { } # 区分大小写正则
location ~* \.(png|jpg)$ { } # 不区分大小写
location / { } # 兜底
三、反向代理 vs 正向代理
| 类型 | 代理谁 | 典型场景 |
|---|---|---|
| 正向代理 | 代理客户端 | 科学上网、公司出网代理 |
| 反向代理 | 代理服务端 | 负载均衡、隐藏真实后端、统一入口 |
正向代理:客户端 → 代理 → 目标服务器 (服务端不知道真实客户端)
反向代理:客户端 → 代理 → 后端服务器 (客户端不知道真实后端)
Java 后端典型配置:
server {
listen 80;
server_name api.sxz-blog.xyz;
location /api/ {
proxy_pass http://127.0.0.1:8080/; # 注意结尾的 /
proxy_http_version 1.1;
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 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
}
⚠️
proxy_pass结尾斜杠的坑:
proxy_pass http://backend/;→ 请求/api/user转发为/user(去掉/api)。proxy_pass http://backend;→ 请求/api/user转发为/api/user(保留原路径)。这一条配错会导致全部 404,是新手最常见的坑。
四、负载均衡
upstream backend {
# 策略(默认轮询,无需关键字)
# 权重轮询:weight
# IP 哈希:ip_hash(同一 IP 固定到同一台)
# 最少连接:least_conn
# 一致性哈希:hash $request_uri consistent
server 127.0.0.1:8081 weight=3;
server 127.0.0.1:8082 weight=1;
server 127.0.0.1:8083 backup; # 备用机
server 127.0.0.1:8084 down; # 手动下线
keepalive 32; # 与后端保持长连接
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 配合 keepalive
}
}
健康检查:开源版 Nginx 只做被动检查(max_fails + fail_timeout),主动健康检查需商业版或 nginx_upstream_check_module。
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
五、静态资源与缓存
# 图片/字体等强缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|woff2?)$ {
root /var/www/static;
expires 30d;
add_header Cache-Control "public, immutable";
}
# HTML/JS/CSS 带 hash 的产物可长期缓存
location ~* \.(js|css)$ {
expires 7d;
add_header Cache-Control "public";
}
# HTML 不缓存,保证发版立即生效
location ~* \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
}
Gzip 压缩(放在 http 块):
gzip on;
gzip_min_length 1k;
gzip_comp_level 6; # 1-9,6 是性价比拐点
gzip_types text/plain text/css application/json
application/javascript text/xml application/xml image/svg+xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
注意:图片/视频已是压缩格式,gzip 反而浪费 CPU,不要加进
gzip_types。
六、HTTPS 配置
server {
listen 443 ssl;
http2 on;
server_name sxz-blog.xyz;
ssl_certificate /etc/nginx/ssl/sxz-blog.xyz.pem;
ssl_certificate_key /etc/nginx/ssl/sxz-blog.xyz.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
root /var/www/blog;
index index.html;
}
}
# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name sxz-blog.xyz;
return 301 https://$host$request_uri;
}
Strict-Transport-Security(HSTS)会让浏览器强制只用 HTTPS,一旦开启测试不充分很难回退(需清理浏览器缓存或等max-age过期)。
七、SPA 与静态博客的路由处理
单页应用(React/Vue)或 Astro 静态站,刷新非首页路径会 404,需要 fallback:
location / {
root /var/www/blog;
index index.html;
try_files $uri $uri/ /index.html; # 找不到就回退到 index.html
}
Astro 静态构建产出的其实是多个
.html,配合trailingSlash配置,通常try_files $uri $uri/ =404;即可。若用了客户端路由,才需要 fallback 到index.html。
八、安全加固
# 隐藏版本号
server_tokens off;
# 限制请求方法与大小
if ($request_method !~ ^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$) {
return 405;
}
client_max_body_size 50m;
# 防点击劫持 / XSS / 嗅探
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
# 限流(http 块定义,server/location 引用)
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
location /api/ {
limit_req zone=api burst=40 nodelay;
proxy_pass http://backend;
}
# 限并发连接
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 20;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
九、常用运维命令
nginx -t # 测试配置语法(改配置后必做)
nginx -s reload # 平滑重载(不中断连接)
nginx -s stop # 快速停止
nginx -s quit # 优雅停止(处理完当前请求)
nginx -v # 查看版本
nginx -T # 打印最终生效的完整配置(含 include)
reload 的原理:master 进程读取新配置、启动新 worker,再优雅关闭旧 worker。旧 worker 会处理完已建立的连接才退出,因此不断连接。但若配置有语法错误,reload 会失败且保持旧配置继续服务,是安全的。
十、日志与排查
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" rt=$request_time ut=$upstream_response_time';
access_log /var/log/nginx/access.log main;
$request_time:Nginx 收到请求到响应的总耗时。$upstream_response_time:后端处理耗时。- 两者差距大 → 瓶颈在网络或 Nginx;两者都大 → 瓶颈在后端应用。
# 统计访问量 Top 10 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 统计 5xx 错误
awk '$9 ~ /^5/ {print}' access.log | wc -l
# 实时观察
tail -f /var/log/nginx/access.log
十一、小结
- Nginx 快的核心:epoll 事件驱动 + master-worker + 零拷贝。
- 反向代理是 Java 后端部署标配,注意
proxy_pass结尾斜杠语义。 - 负载均衡默认轮询,
weight/ip_hash/least_conn按场景选。 - 静态资源按类型设缓存,HTML 不缓存,JS/CSS 长缓存。
- 生产必备:
server_tokens off+ 安全响应头 + 限流 + Gzip + HTTPS。 - 排查性能问题看
$request_time与$upstream_response_time的差值。