Skip to content

10 · 实战:一份生产级 Nginx 配置

你已经学完了 9 章基础知识,这一章我们把所有知识揉成一份真实可用、能直接抄的 nginx.conf——前端 SPA + 后端 API + WebSocket + 多后端 + HTTPS + 缓存压缩 + 监控全套,配套 docker-compose 一键起。


1. 业务场景

                    ┌────────────────┐
                    │   Internet     │
                    └────────┬───────┘


                    ┌────────────────┐
                    │     Nginx      │ :80, :443
                    │ (网关 / 入口)   │
                    └────────┬───────┘
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
        ┌─────────┐    ┌─────────┐    ┌──────────┐
        │ /  →    │    │ /api/ → │    │ /ws/  →  │
        │ SPA 前端│    │ 3 个 Node│    │ Go WS    │
        │ dist/   │    │ (LB)    │    │ Service  │
        └─────────┘    └─────────┘    └──────────┘

功能清单

  • ✅ HTTPS(HTTP/2、HSTS、A+ 评分)
  • ✅ SPA fallback(history 模式刷新不 404)
  • ✅ API 反向代理 + 负载均衡(3 台后端)
  • ✅ WebSocket 反向代理
  • ✅ 静态资源强缓存 + 接口短缓存
  • ✅ gzip 压缩
  • ✅ 限流(防恶意刷接口)
  • ✅ 健康检查端点
  • ✅ access 日志带 RT / upstream RT
  • ✅ 安全响应头

2. 完整目录结构

deploy/
├── docker-compose.yml          ← 一键起所有服务
├── nginx/
│   ├── nginx.conf              ← 主配置(main + events + http)
│   ├── conf.d/
│   │   ├── default.conf        ← 业务 server 块
│   │   └── upstream.conf       ← upstream 后端池
│   └── certs/                  ← Let's Encrypt 证书
├── html/                       ← 前端打包产物
│   ├── index.html
│   └── assets/
└── logs/
    ├── access.log
    └── error.log

3. 各文件简介

3.1 nginx.conf · 主配置

参见"💻 示例代码"中的 nginx.conf。关键看点:

  • worker_processes autoworker_connections 10240
  • 自定义 log_format 带上 $request_time$upstream_response_time强烈建议
  • 全局开 gzip
  • limit_req_zone 全局定义,子 location 引用

3.2 upstream.conf · 后端池

nginx
upstream api_backend {
    least_conn;
    keepalive 64;
    server 10.0.0.11:3000 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:3000 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.0.13:3000 weight=1 max_fails=3 fail_timeout=30s;
    server 10.0.0.99:3000 backup;
}

upstream ws_backend {
    server 10.0.0.20:4000;
    server 10.0.0.21:4000;
}

3.3 default.conf · 业务路由

参见"💻 示例代码"。主要看点:

  • 80 → 443 强制跳转
  • 443 server 配 SSL + HTTP/2 + HSTS
  • 5 个 location:SPA / 静态资源 / API / WS / health
  • API 加了 limit_req 限流
  • error_page 兜底

3.4 docker-compose.yml

参见"💻 示例代码"。一行 docker compose up -d 起 Nginx + 模拟 3 个 Node 后端。


4. 部署流程(5 分钟搞定)

bash
# 1. 克隆 / 准备代码
git clone <你的仓>
cd deploy

# 2. 把前端打包结果放到 html/ 目录
cp -r ~/myapp/dist/* html/

# 3. (首次)申请证书
docker run --rm -it \
  -v $PWD/nginx/certs:/etc/letsencrypt \
  -v $PWD/html:/var/www/html \
  -p 80:80 \
  certbot/certbot certonly --standalone -d example.com -d www.example.com

# 4. 启动
docker compose up -d

# 5. 看日志
docker compose logs -f nginx

# 6. 验证
curl -I https://example.com/                   # 应该返回 200 + 一堆安全头
curl -I https://example.com/api/healthz        # 应该 200
curl -I https://example.com/static/index.js    # X-Cache-Status: ...

# 7. 改完配置 reload(不断现有连接)
docker compose exec nginx nginx -t && \
docker compose exec nginx nginx -s reload

5. 调试排错命令小抄

bash
# 看 nginx 进程
docker compose exec nginx ps aux | grep nginx

# 检测语法
docker compose exec nginx nginx -t

# 看 access log(带 rt / urt)
tail -f logs/access.log

# 慢请求 Top 10
awk '{print $NF, $7}' logs/access.log | sort -rn | head

# 5xx Top 10
awk '$9>=500' logs/access.log | awk '{print $7, $9}' | sort | uniq -c | sort -rn | head

# 实时 QPS
tail -f logs/access.log | awk '{print $4}' | uniq -c

# 看后端 RT
awk '{print $NF}' logs/access.log | awk -F'urt=' '{print $2}'

6. 上线 Checklist(直接抄)

上线前对一遍这 12 项,能避免 90% 的事故。

  • [ ] nginx -t 通过
  • [ ] 配 HTTPS + HTTP/2 + HSTS
  • [ ] 80 → 443 强制 301
  • [ ] SPA fallback try_files $uri $uri/ /index.html
  • [ ] API proxy_set_header Host / X-Real-IP / X-Forwarded-For / X-Forwarded-Proto
  • [ ] WebSocket 三件套(Upgrade / Connection / read_timeout)
  • [ ] gzip 开启 + 类型够全
  • [ ] 静态资源带 hash + immutable 强缓存
  • [ ] HTML no-cache
  • [ ] client_max_body_size 调大(按业务)
  • [ ] limit_req 限流防刷
  • [ ] 监控告警(5xx 率、RT、QPS)
  • [ ] 日志轮转(logrotate / docker max-size
  • [ ] 证书自动续签(cron / systemd timer)

7. 进阶方向(学完这 10 章之后)

方向推荐工具一句话
主动健康检查 / 动态配置Tengine / OpenResty阿里 / 腾讯都在用,开源版的"加强版 Nginx"
现代 API 网关APISIX / Kong基于 Nginx,有控制台、支持热加载和插件
Kubernetes 入口控制器ingress-nginxK8s 原生路由,工程师标配
Service MeshEnvoy / Istio下一代代理,内部服务间通信用
在 Nginx 里写业务逻辑OpenResty + Lua限流 / 鉴权 / WAF 都能在边缘搞定
性能调优wrkhey/proc 指标压测找瓶颈、调 worker_connections / so_keepalive

8. 一句话总结这 10 章

Nginx 的工作 = 路由 + 缓存 + 流量管控——10 个章节、3 个 demo、1 份生产配置,已经把这 3 件事讲透了。后续你需要的,都是在此基础上的"做大、做精、做稳"。

下一站 → QA · 高频面试题(30+):把所学转化为面试白板上的子弹。

💻 示例代码

💻 示例代码

txt
###############################################################################
#  default.conf · 业务入口(SPA + API + WebSocket)
###############################################################################

# ============================================================
# HTTP → HTTPS 301 跳转
# ============================================================
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # 留给 Let's Encrypt 验证
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# ============================================================
# HTTPS 主站
# ============================================================
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # ---- 证书 ----
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ---- TLS 配置(A+) ----
    ssl_protocols           TLSv1.2 TLSv1.3;
    ssl_ciphers             ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  1d;
    ssl_session_tickets  off;
    ssl_stapling          on;
    ssl_stapling_verify   on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    # ---- 安全响应头 ----
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    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;

    # ---- 站点根目录(前端打包产物) ----
    root  /var/www/html;
    index index.html;

    # ============================================================
    # 1. 健康检查(Nginx 自身)
    # ============================================================
    location = /healthz {
        access_log off;
        return 200 "ok\n";
        add_header Content-Type text/plain;
    }

    # ============================================================
    # 2. API 反向代理 + 限流 + 短缓存
    # ============================================================
    location /api/ {
        # 限流:每 IP 20 r/s,突发 40
        limit_req zone=api_limit burst=40 nodelay;

        proxy_pass http://api_backend/;
        proxy_http_version 1.1;
        proxy_set_header Connection         "";
        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 5s;
        proxy_send_timeout    30s;
        proxy_read_timeout    30s;

        proxy_next_upstream error timeout http_502 http_503 http_504;
        proxy_next_upstream_tries   2;
        proxy_next_upstream_timeout 10s;

        # 仅 GET /api/products 这类接口可短缓存(按需开启)
        # proxy_cache         api_cache;
        # proxy_cache_valid   200 5m;
        # proxy_cache_key     "$scheme$request_method$host$request_uri";
        # proxy_cache_bypass  $http_authorization $cookie_nocache;
        # proxy_no_cache      $http_authorization $cookie_nocache;
        # add_header X-Cache-Status $upstream_cache_status always;
    }

    # ============================================================
    # 3. WebSocket
    # ============================================================
    location /ws/ {
        proxy_pass http://ws_backend/;

        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_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # ============================================================
    # 4. 静态资源(带 hash → 强缓存 1 年)
    # ============================================================
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable" always;
        access_log off;
    }

    location ~* \.[a-f0-9]{8,}\.(?:js|css|png|jpg|webp|svg|woff2?)$ {
        expires 1y;
        add_header Cache-Control "public, immutable" always;
        access_log off;
    }

    # ============================================================
    # 5. SPA history 模式 fallback
    # ============================================================
    location = /index.html {
        expires -1;
        add_header Cache-Control "no-cache, must-revalidate" always;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }

    # ============================================================
    # 6. 隐藏文件 / 错误页
    # ============================================================
    location ~ /\. {
        deny all;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}
yaml
version: "3.8"

# 一键起 Nginx + 3 个模拟后端 + 1 个 WebSocket 服务
# 前置:把前端 dist 拷贝到 ./html 目录

services:
  nginx:
    image: nginx:1.25-alpine
    container_name: gateway
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d:ro
      - ./certs:/etc/letsencrypt:ro
      - ./html:/var/www/html:ro
      - ./logs:/var/log/nginx
    depends_on:
      - api1
      - api2
      - api3
      - ws1
    networks: [edge]

  # ----- 模拟 3 个后端:用 hashicorp/http-echo 起一个返回 JSON 的 mock -----
  api1:
    image: hashicorp/http-echo:latest
    command: ["-listen=:3000", "-text={\"server\":\"api1\",\"data\":\"hello\"}"]
    networks: [edge]

  api2:
    image: hashicorp/http-echo:latest
    command: ["-listen=:3000", "-text={\"server\":\"api2\",\"data\":\"hello\"}"]
    networks: [edge]

  api3:
    image: hashicorp/http-echo:latest
    command: ["-listen=:3000", "-text={\"server\":\"api3\",\"data\":\"hello\"}"]
    networks: [edge]

  api4:
    image: hashicorp/http-echo:latest
    command: ["-listen=:3000", "-text={\"server\":\"backup\",\"data\":\"hello\"}"]
    networks: [edge]

  # ----- WebSocket 用一个简单 echo(生产换成你自己的服务)-----
  ws1:
    image: solsson/websocat
    command: ["-s", "0.0.0.0:4000"]
    networks: [edge]

  ws2:
    image: solsson/websocat
    command: ["-s", "0.0.0.0:4000"]
    networks: [edge]

networks:
  edge:
    driver: bridge
txt
###############################################################################
#  nginx.conf · 生产级主配置
###############################################################################
user  nginx;
worker_processes  auto;
worker_rlimit_nofile 65535;

error_log  /var/log/nginx/error.log  warn;
pid        /var/run/nginx.pid;

events {
    use epoll;
    worker_connections 10240;
    multi_accept on;
    accept_mutex off;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # ------- 日志:带请求时长 / upstream 时长,方便排障 -------
    log_format  main  '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent" '
                      'rt=$request_time uct="$upstream_connect_time" '
                      'urt="$upstream_response_time" '
                      'cache="$upstream_cache_status"';
    access_log  /var/log/nginx/access.log  main;

    # ------- 性能 -------
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout    65;
    keepalive_requests   1000;
    server_tokens   off;
    client_max_body_size 50m;

    # ------- gzip 压缩 -------
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 5;
    gzip_vary on;
    gzip_proxied any;
    gzip_types
        text/plain text/css text/xml
        application/json application/javascript application/xml+rss
        image/svg+xml;

    # ------- 限流:每 IP 每秒 20 个 API 请求 -------
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s;
    limit_req_status 429;

    # ------- proxy_cache 区 -------
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=api_cache:50m
                     max_size=2g
                     inactive=60m
                     use_temp_path=off;

    # ------- 上游池 / 业务 server 拆到子文件 -------
    include /etc/nginx/conf.d/*.conf;
}
txt
###############################################################################
#  upstream.conf · 后端服务池定义
###############################################################################

# ------- API 后端:3 台 + 1 备用 -------
upstream api_backend {
    least_conn;
    keepalive 64;

    server api1:3000 weight=2 max_fails=3 fail_timeout=30s;
    server api2:3000 weight=2 max_fails=3 fail_timeout=30s;
    server api3:3000 weight=1 max_fails=3 fail_timeout=30s;
    server api4:3000 backup;
}

# ------- WebSocket 后端 -------
upstream ws_backend {
    ip_hash;     # 同一用户固定一台(保证连接粘性)
    keepalive 32;

    server ws1:4000 max_fails=3 fail_timeout=30s;
    server ws2:4000 max_fails=3 fail_timeout=30s;
}

default.conf ↗ · docker-compose.yml ↗ · nginx.conf ↗ · upstream.conf ↗