运维 ·
Nginx 入门:配置、反向代理、负载均衡与日常排障
Nginx 是 Web 服务与反向代理的事实标准。本文覆盖安装、核心配置块(location/upstream/rewrite)、HTTPS、限流缓存、日志、以及 502/499/403/证书、性能瓶颈等高频问题排查。
Nginx 入门:配置、反向代理、负载均衡与日常排障
Nginx(engine-x) 是俄罗斯人写的事件驱动高性能 HTTP / 反向代理 / TCP/UDP 代理服务器,占全球 Web 服务的头把交椅。它主要用来:静态文件服务器、反向代理(把请求转发给后面的 Node/Go/Python/PHP-FPM)、负载均衡(多台后端分流)、HTTPS 终结、缓存、限流、WAF 前置网关。
一、安装
1.1 macOS
brew install nginx
brew services start nginx
# 默认配置在 /opt/homebrew/etc/nginx/nginx.conf
# 默认根目录 /opt/homebrew/var/www
nginx -t # 配置语法检查(非常重要,每次改完必跑)
1.2 Ubuntu / Debian
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
# 配置:/etc/nginx/nginx.conf,站点:/etc/nginx/sites-available/*.conf
# 启用站点:sudo ln -s /etc/nginx/sites-available/xxx /etc/nginx/sites-enabled/
# 重启:sudo nginx -s reload # 推荐,平滑不中断
1.3 CentOS / Rocky / RHEL
sudo dnf install -y epel-release
sudo dnf install -y nginx
sudo systemctl enable --now nginx
sudo firewall-cmd --add-service=http --permanent && sudo firewall-cmd --reload
1.4 Docker
docker run -d --name nginx -p 80:80 -p 443:443 \
-v ./conf.d:/etc/nginx/conf.d:ro \
-v ./html:/usr/share/nginx/html:ro \
-v ./certs:/etc/nginx/certs:ro \
-v ./logs:/var/log/nginx \
--restart=unless-stopped nginx:1.27-alpine
安装完永远先跑:
nginx -v # 版本
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'configure|with-' # 看编译进了哪些模块(含不含 http_ssl_module、http_v2_module、stream…)
二、核心配置结构
2.1 主配置文件骨架
# /etc/nginx/nginx.conf
user nginx; # worker 进程以哪个用户跑(安全:不能是 root)
worker_processes auto; # worker 数 = CPU 核数
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 4096; # 每个 worker 能同时接多少连接
use epoll; # Linux 事件模型(高性能)
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
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;
sendfile on; # 零拷贝发静态文件
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on; # 响应体 gzip
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1024;
# 虚拟主机 include(Debian/Ubuntu)
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
2.2 一个完整”虚拟主机”示例
/etc/nginx/conf.d/example.conf:
# ① 后端应用池
upstream app_backend {
# 负载均衡策略:默认轮询,可选 ip_hash / least_conn / least_time
least_conn;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s weight=2;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 backup; # 只有前两台都挂了才启用
keepalive 64; # 与后端长连接数(性能优化!)
}
# ② HTTP → HTTPS 301 跳转
server {
listen 80;
listen [::]:80;
server_name www.example.com example.com;
return 301 https://$host$request_uri;
}
# ③ HTTPS 主站
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL 配置(用 Let's Encrypt / 阿里云免费证书)
ssl_certificate /etc/nginx/certs/example.com.pem;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 文档根
root /var/www/example.com;
index index.html index.htm;
# 客户端真实 IP(前面有 CDN / LB 的话)
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# ---------- location 匹配顺序:精确 = > 前缀 ^~ > 正则 ~ / ~* > 最长前缀 ----------
# 静态文件:直接交给 nginx,不打到后端
location ~* \.(?:css|js|jpg|jpeg|png|gif|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
try_files $uri =404;
}
# 健康检查(LB 用)
location = /healthz {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
# 限流(每秒 10 请求/IP,漏桶容量 20)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
location /api/ {
limit_req zone=req_limit burst=20 nodelay;
# 反向代理
proxy_pass http://app_backend;
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_set_header Connection ""; # 配合 keepalive
# 超时
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# 缓冲(提高性能,默认开)
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 16 8k;
}
# 单页应用(React/Vue)前端路由
location / {
try_files $uri $uri/ /index.html;
}
# 404
error_page 404 /404.html;
location = /404.html { internal; }
}
2.3 location 匹配规则记忆表(面试+排障都考)
| 写法 | 含义 | 优先级 |
|---|---|---|
location = /foo | 精确匹配 /foo | 最高,找到立刻停 |
location ^~ /foo/ | 前缀匹配,命中后不再检查正则 | 次高 |
location ~ \.php$ | 正则匹配,区分大小写;多个写前面先匹配 | 按顺序 |
| `location ~* .(jpg | png)$` | 正则匹配,不区分大小写 |
location /foo | 普通前缀匹配,最后用”最长匹配那一条” | 最低 |
三、nginx.conf 必改的性能参数
user nginx;
worker_processes auto; # auto = CPU 核数
events {
worker_connections 4096; # 太小会"too many open files"
use epoll;
multi_accept on;
}
http {
sendfile on; # 发大文件零拷贝,非常关键
tcp_nopush on; # 攒满包再发,减少小包
tcp_nodelay on; # keepalive 上小包不延迟
keepalive_timeout 65; # 客户端长连接
keepalive_requests 1000; # 一个长连接上最多多少请求
open_file_cache max=10000 inactive=30s; # fd 缓存(静态文件站必开)
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
client_max_body_size 50m; # 上传文件大小上限(默认 1m 太小!)
client_body_timeout 60s;
# 工作进程打开文件数上限(配合 ulimit -n)
worker_rlimit_nofile 65535;
}
配合 /etc/security/limits.conf 或 systemd 给 nginx 加大 open files:
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
四、Let’s Encrypt 免费 HTTPS(Certbot)
# Debian / Ubuntu
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# 自动写进 nginx conf 并配置 systemd timer 自动续期
systemctl list-timers | grep certbot
五、日志与分析
5.1 日志字段说明(上例 main 格式)
重点关注 rt / uct / uht / urt 四个时间:
rt=$request_time:nginx 自己处理全程(接第一个字节 → 发完响应)uct:和后端 TCP 建立连接花的时间uht:后端返回响应头时间(≈ 后端处理时长 + 传头)urt:后端返回整个响应时间
5.2 常用统计命令(面试 / 排障利器)
# 1. 按 HTTP 状态码统计 TOP 情况
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
# 2. 访问量 TOP 20 的 URI(去掉 query)
awk '{split($7, a, "?"); print a[1]}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# 3. 访问量 TOP 20 的 IP(看是不是被刷)
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# 4. 慢请求 TOP 30(按 rt 排序,假设 rt 在倒数第 4 个字段)
awk '{match($0, /rt=([0-9.]+)/, m); if (m[1]+0 > 1) print m[1]"\t"$0}' /var/log/nginx/access.log | sort -rn | head -30
# 5. 实时看当前每秒请求数
tail -f /var/log/nginx/access.log | awk -W interactive '{print strftime("%H:%M:%S")}' | uniq -c
六、日常排障手册
6.1 通用四步排查
# ① 配置对不对?语法检查
nginx -t
# ② nginx 在跑吗?端口在听吗?
systemctl status nginx
ss -lntp | grep -E ':(80|443)\b'
# ③ 错误日志看具体啥错(最重要!)
tail -n 100 /var/log/nginx/error.log
# ④ 某次请求到底走到哪个 location 了?
curl -v https://example.com/xxx -o /dev/null # 看响应头里 Server、状态码
# 或开 debug 日志:改 error_log /var/log/nginx/error.log debug; reload 再看
6.2 常见报错对照表
| 现象 / 关键字 | 根因 / 排查 |
|---|---|
nginx -t 报 emerg:duplicate location / invalid number of arguments | 同一个 server 里写了两条一样的 location;指令参数格式错;少 ; 结尾 / 少 {} |
(13: Permission denied) while connecting to upstream | SELinux 把 nginx 外连拦住了。getenforce → Enforcing 就临时 setenforce 0,永久:setsebool -P httpd_can_network_connect 1 |
(24: Too many open files) | worker_rlimit_nofile / worker_connections 不够,按第三节调大 + systemd LimitNOFILE=65535 |
403 Forbidden | 1) 文件/目录用户是 root,但 nginx worker 是 nginx 用户,没读权限 → chown -R nginx:nginx /html + chmod -R a+rX2) 开了 autoindex off 却访问目录路径又没 index 文件3) SELinux 上下文: restorecon -R /var/www |
404 Not Found | ① URL / root / alias 指错目录,最常见是 location /app/ { root /html; } → 实际找 /html/app/xxx 而不是 /html/xxx,要用 alias 或把 root 调对② SPA 前端没写 try_files $uri $uri/ /index.html |
413 Request Entity Too Large | 上传文件超过 1MB。加大 client_max_body_size 50m;(http/server/location 任意层级都行) |
499(Nginx 特有) | 客户端主动断开:浏览器点了停止、用户关了网页、客户端设置超时太短(常见:移动端网不好)。一般不担心,量大了可能是后端太慢用户等不及 |
502 Bad Gateway | upstream 不可达: 1) 后端进程没起来 → curl 后端本机端口试试 2) Nginx 跟后端协议不对(后端是 FastCGI 你写了 http_pass,或者反过来) 3) proxy_read_timeout(秒)比后端处理时间短 → 加大,或者优化后端4) upstream 里所有机器 max_fails 都到阈值了,被临时摘除 → fail_timeout 过后会恢复 |
504 Gateway Timeout | 跟 502 很像,但特指 nginx 等后端响应超时。优先看后端业务(慢 SQL、锁、GC…)。临时加大 proxy_read_timeout 120s; |
500 Internal Server Error | 要么 nginx 自己处理出错(rewrite 死循环、模块 bug),要么是 PHP-FPM 类 upstream 返回了异常。看 error.log 具体 *345 FastCGI sent in stderr... 这种行 |
SSL_CTX_use_PrivateKey_file(...) failed: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch | 证书和私钥不匹配。对比:`openssl x509 -noout -modulus -in a.pem |
SSL_do_handshake() failed / 浏览器证书不可信 | 没把证书链(中间证书)拼上去。Let’s Encrypt fullchain.pem 是对的,cert.pem 是单张;云厂商一般给 证书+证书链.pem 合并版。配 ssl_certificate 一定要用包含中间链那个 |
recv() failed (104: Connection reset by peer) while reading response header from upstream | 后端 TCP 突然 RST 断开连接,多半是后端挂了 / 重启 / OOM Killed / 上游 LB 把 TCP 掐了 |
| 静态文件请求慢 | ① 没开 sendfile on; ② 文件大没启用 gzip_static / brotli ③ open_file_cache 没开 ④ NFS 挂载远端 fs 慢 |
| 同一个 URL 有用户能访问,有人 404 | ① ip_hash/hash $cookie_xxx 负载均衡下,不同人分到的后端机器部署版本不一致② CDN 缓存脏数据,清缓存 / 看 X-Cache 响应头 |
6.3 Nginx 与前端最常踩的 3 个坑
坑 1:Vue/React 刷新页 404
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html; # 加这行:找不到静态文件就交给前端路由 index.html
}
坑 2:前端 router base = /app/ 但资源 404
要么改 nginx 用 alias,要么打包时 base: '/app/' + 资源前缀 /app/assets/ 两边对齐。
坑 3:HTTPS 下静态资源是 http 就 Mixed Content 被浏览器拦截
确认:
proxy_set_header X-Forwarded-Proto $scheme;- 后端应用读到这个头,生成的 URL 就是 https(比如 Django
SECURE_PROXY_SSL_HEADER、Springserver.forward-headers-strategy=native)
6.4 性能瓶颈定位
# 本机先看 worker CPU 占用
top -H -p $(cat /var/run/nginx.pid) # 多核 CPU% 全吃满就是计算瓶颈
# 压测(别在生产直接干!)
ab -n 10000 -c 200 http://127.0.0.1/ # Apache Bench
wrk -t4 -c200 -d30s http://127.0.0.1/ # wrk 更现代
# 调大 worker_connections / keepalive / open_file_cache 一般能解决 80%
# 若还是慢 → 看 upstream 是否慢(urt 长就是后端慢,找后端;uct 长是网络慢)
# nginx 自己 CPU 高 → 正则 rewrite 太多;大量 gzip on 小文件;geoip / map 字典巨大
七、进阶学习方向
- Nginx Plus:商业版,主动健康检查、session persistence、动态配置 API、高级限流、JWK/OIDC 鉴权
- OpenResty:Nginx + LuaJIT,可以用 Lua 写逻辑(WAF、灰度、动态 upstream、鉴权网关都能写,性能极高)
- APISIX / Kong:基于 Nginx/OpenResty 的开源 API 网关,插件生态丰富
ngx_http_map_module+ GeoIP2:根据 IP / Header 做路由、黑白名单、国家分布ngx_stream_core_module(TCP/UDP 代理):代理 MySQL / Redis / gRPC,Nginx 不止能搞 HTTP- Nginx 作为 Ingress Controller(K8s 场景):
ingress-nginxannotations 用法 - Brotli 压缩、
quic/ HTTP/3 编译启用
参考资料
- Nginx 官方文档:https://nginx.org/en/docs/
- Beginner’s Guide:https://nginx.org/en/docs/beginners_guide.html
- 阿尔巴乔夫《深入理解 Nginx:模块开发与架构解析》
- OpenResty 中文社区:https://openresty.org/cn/
- certbot:https://certbot.eff.org/