文章

Nginx 学习笔记:反向代理与部署实战

更新于 2026/09/20牛耕田

暂存笔记,持续补充中。Nginx 是后端部署的「第一道门」,几乎每个 Java 项目都跑在它后面。

一、Nginx 是什么、为什么快#

Nginx 是一个事件驱动、异步非阻塞的高性能 Web 服务器 / 反向代理服务器。

对比ApacheNginx
并发模型进程/线程 per connectionepoll 事件驱动
内存占用高低
静态资源一般极强
高并发一般优秀(单机轻松万级)

为什么快:

  1. epoll 多路复用:一个 worker 进程用事件循环处理成千上万连接,无线程切换开销。
  2. master-worker 架构:master 管配置与 worker,worker 处理请求,worker 数通常设为 CPU 核数。
  3. 零拷贝(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;
}

九、常用运维命令#

Terminal window
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;两者都大 → 瓶颈在后端应用。
Terminal window
# 统计访问量 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 的差值。
返回主页