运维 ·

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 ~* .(jpgpng)$`正则匹配,不区分大小写
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_timenginx 自己处理全程(接第一个字节 → 发完响应)
  • 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 -temergduplicate location / invalid number of arguments同一个 server 里写了两条一样的 location;指令参数格式错;少 ; 结尾 / 少 {}
(13: Permission denied) while connecting to upstreamSELinux 把 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 Forbidden1) 文件/目录用户是 root,但 nginx worker 是 nginx 用户,没读权限 → chown -R nginx:nginx /html + chmod -R a+rX
2) 开了 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 Gatewayupstream 不可达
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 / brotliopen_file_cache 没开 ④ NFS 挂载远端 fs 慢
同一个 URL 有用户能访问,有人 404ip_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 被浏览器拦截

确认:

  1. proxy_set_header X-Forwarded-Proto $scheme;
  2. 后端应用读到这个头,生成的 URL 就是 https(比如 Django SECURE_PROXY_SSL_HEADER、Spring server.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 字典巨大

七、进阶学习方向

  1. Nginx Plus:商业版,主动健康检查、session persistence、动态配置 API、高级限流、JWK/OIDC 鉴权
  2. OpenResty:Nginx + LuaJIT,可以用 Lua 写逻辑(WAF、灰度、动态 upstream、鉴权网关都能写,性能极高)
  3. APISIX / Kong:基于 Nginx/OpenResty 的开源 API 网关,插件生态丰富
  4. ngx_http_map_module + GeoIP2:根据 IP / Header 做路由、黑白名单、国家分布
  5. ngx_stream_core_module(TCP/UDP 代理):代理 MySQL / Redis / gRPC,Nginx 不止能搞 HTTP
  6. Nginx 作为 Ingress Controller(K8s 场景):ingress-nginx annotations 用法
  7. Brotli 压缩quic / HTTP/3 编译启用

参考资料

问问 AI