LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

[点晴永久免费OA]Nginx 从入门到精通:一篇讲透反向代理、负载均衡、HTTPS、缓存与性能优化

admin
2026年7月30日 9:26 本文热度 93

很多人第一次接触 Nginx,是因为部署网站时需要“把 80 端口转发到 8080”;真正进入生产环境后才发现,它还承担着静态资源服务器、反向代理、负载均衡器、TLS 终结层、缓存层、限流器和流量入口等多重角色。

本文不堆砌指令,而是用一条完整的学习路径,帮你从“能配出来”走到“知道为什么这样配、出了问题如何排查”。


一、先回答一个问题:Nginx 到底是什么?

Nginx 是一个高性能的 Web 服务器和反向代理服务器,也常用于负载均衡、HTTP 缓存、TLS 终结和四层 TCP/UDP 代理。

它最核心的优势不是“配置文件短”,而是事件驱动、异步非阻塞的处理模型。

传统的“一连接一线程”模型,在并发连接快速增加时,线程切换和内存开销会持续上升。Nginx 通常由一个 master 进程和多个 worker 进程协作:

  •  master 进程:读取配置、绑定端口、管理 worker、执行平滑重载;
  •  worker 进程:真正处理连接和请求;
  •  事件循环:单个 worker 可以同时管理大量连接,而不是为每个连接创建独立线程。

这也是为什么 Nginx 特别适合处理高并发、静态资源和大量长连接。

不过要注意:Nginx 擅长的是网络 I/O 调度,不适合直接承担复杂业务计算。一个典型架构通常是:

浏览器 / App
      ↓
CDN / WAF
      ↓
Nginx
  ├── 静态资源
  ├── TLS 终结
  ├── 限流与访问控制
  └── 反向代理
          ↓
   Java / Go / Node.js / Python 服务
          ↓
      数据库与缓存

一句话概括:让 Nginx 做流量入口,让应用服务专注业务逻辑。


二、安装之后,先认识这些目录和命令

不同发行版的目录会略有差异,常见位置如下:

/etc/nginx/nginx.conf         # 主配置文件
/etc/nginx/conf.d/            # 常见的业务配置目录
/etc/nginx/sites-available/   # Debian/Ubuntu 常见
/etc/nginx/sites-enabled/     # Debian/Ubuntu 常见
/var/log/nginx/access.log     # 访问日志
/var/log/nginx/error.log      # 错误日志
/usr/share/nginx/html/        # 常见默认站点目录

最重要的不是立刻启动服务,而是记住下面这组命令:

# 查看版本与编译参数
nginx -V

# 检查配置语法
nginx -t

# 查看 Nginx 实际加载的完整配置
nginx -T

# 启动
nginx

# 平滑重载配置
nginx -s reload

# 快速停止
nginx -s stop

# 优雅停止:等待当前请求处理完成
nginx -s quit

如果由 systemd 管理,也可以使用:

systemctl status nginx
systemctl start nginx
systemctl reload nginx
systemctl restart nginx

生产环境中最值得形成肌肉记忆的一条命令是:

nginx -t && nginx -s reload

它先检查配置,只有语法正确才重载。很多线上事故,都是因为跳过了前半句。


三、读懂 Nginx 配置文件:上下文比指令更重要

Nginx 配置不是简单的键值对,而是由不同层级的“上下文”组成。

一个简化后的主配置如下:

user nginx;
worker_processes auto;

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

events {
    worker_connections 1024;
}

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

    sendfile on;
    keepalive_timeout 65;

    include /etc/nginx/conf.d/*.conf;
}

常见上下文可以这样理解:

上下文作用
main全局配置,例如运行用户、worker 数量、日志位置
events连接处理模型、单个 worker 的连接上限
httpHTTP 全局配置,例如日志格式、压缩、缓存区域
server一个虚拟主机,通常对应域名和端口
location在某个虚拟主机中匹配 URI,并执行具体处理
upstream定义一组后端服务,用于反向代理和负载均衡

初学者最常见的错误,不是指令拼错,而是把正确的指令写在了错误的上下文中

例如,proxy_cache_path 通常定义在 http上下文,而 proxy_cache 一般在 server  location 中启用;limit_req_zone 需要先在 http 中定义共享内存区域,再在业务位置中使用。

遇到 directive is not allowed here,首先检查的就应该是上下文。


四、第一份可用配置:部署一个静态网站

假设网站文件位于:

/var/www/example/
├── index.html
├── css/
├── js/
└── images/

可以创建一个站点配置:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

这里有三个关键点。

1. root 是拼接路径

请求:

https://example.com/images/logo.png

在上面的配置中,会映射到:

/var/www/example/images/logo.png

2. try_files 按顺序检查文件

try_files $uri $uri/ =404;

含义是:

  1. 1. 先检查请求对应的文件是否存在;
  2. 2. 再检查对应目录是否存在;
  3. 3. 都不存在则返回 404。

3. 单页应用需要回退到 index.html

Vue、React 等前端路由使用 History 模式时,可以配置:

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

这样访问 /users/123 时,即使服务器上没有这个真实文件,也会交给前端路由处理。

但不要把这个写法机械地用于所有站点。普通静态站点如果统一回退到 index.html,原本应该返回 404 的资源请求可能会返回 HTML,给排障带来麻烦。


五、location 匹配规则:Nginx 配置最容易踩坑的地方

常见写法如下:

location = /health { }
location ^~ /static/ { }
location /api/ { }
location ~ \.php$ { }
location ~* \.(jpg|jpeg|png|gif)$ { }

可以先记住这几个符号:

写法含义
location = /path精确匹配
location ^~ /path/优先使用此前缀匹配,不再检查正则
location /path/普通前缀匹配
location ~ regex区分大小写的正则匹配
location ~* regex不区分大小写的正则匹配

一个实用的理解顺序是:

  1. 1. 先找精确匹配;
  2. 2. 再找最长的前缀匹配;
  3. 3. 如果最长前缀使用了 ^~,直接采用;
  4. 4. 否则继续按配置顺序检查正则;
  5. 5. 正则命中则使用正则,否则回到最长前缀。

例如:

location ^~ /assets/ {
    expires 30d;
}

location ~* \.(js|css|png|jpg)$ {
    expires 7d;
}

请求 /assets/app.js 会命中 /assets/,因为 ^~ 阻止了后续正则覆盖它。

排查路由问题时,不要只问“哪个 location 看起来更像”,要按匹配优先级一步一步推演。


六、反向代理:Nginx 最常见的生产用途

假设应用运行在本机 127.0.0.1:8080,Nginx 对外提供 80 端口:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;

        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;
    }
}

这些请求头非常重要:

  •  Host:把原始域名传给后端;
  •  X-Real-IP:传递当前客户端地址;
  •  X-Forwarded-For:记录代理链路上的客户端地址;
  •  X-Forwarded-Proto:告诉后端原始请求是 HTTP 还是 HTTPS。

不过,后端应用不能无条件信任任何客户端传来的 X-Forwarded-For。只有在确认请求来自可信代理时,才应该解析这些头部,否则客户端可以自行伪造。

proxy_pass 末尾斜杠的区别

这是 Nginx 最经典的坑之一。

配置一:保留 /api/ 前缀。

location /api/ {
    proxy_pass http://127.0.0.1:8080;
}

请求:

/api/users

后端收到的路径通常仍是:

/api/users

配置二:移除 /api/ 前缀。

location /api/ {
    proxy_pass http://127.0.0.1:8080/;
}

同样的请求,后端收到:

/users

记忆方法:proxy_pass 后面带 URI 部分时,会用该 URI 替换匹配到的 location 前缀。

修改这类配置后,不要只看语法检查,要用真实请求验证后端最终收到的路径。


七、超时、缓冲与大文件上传

反向代理配置中,下面几项经常被一起修改:

location /api/ {
    proxy_pass http://backend;

    proxy_connect_timeout 5s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    client_max_body_size 20m;
}

它们分别表示:

  •  proxy_connect_timeout:连接后端服务的超时时间;
  •  proxy_send_timeout:向后端发送请求时,两次写操作之间允许的最大间隔;
  •  proxy_read_timeout:从后端读取响应时,两次读操作之间允许的最大间隔;
  •  client_max_body_size:客户端请求体上限,常用于上传文件。

需要特别注意:把所有超时都改成几分钟甚至几十分钟,只是把问题藏起来。正确做法是先确认:

  1. 1. 后端是否真的需要长时间处理;
  2. 2. 是否应该改成异步任务;
  3. 3. 上游网关、负载均衡器和客户端的超时是否一致;
  4. 4. 连接长期占用是否会拖垮系统。

对于普通 API,请优先让业务快速失败,而不是无限等待。

代理缓冲何时关闭?

Nginx 默认会缓冲后端响应,这有利于释放后端连接,并平滑处理慢客户端。

但对于 Server-Sent Events、流式输出或部分实时场景,可能需要:

location /events/ {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 1h;
}

不要全局关闭缓冲。只在确实需要流式传输的位置关闭。


八、WebSocket 与长连接

WebSocket 需要把 HTTP/1.1 升级相关头部传给后端。

推荐先在 http 上下文定义:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

然后在业务配置中使用:

location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_read_timeout 1h;
}

很多 WebSocket “握手失败”,根源并不在应用,而是代理层没有传递 Upgrade  Connection 头,或者中间网络设备提前断开空闲连接。


九、负载均衡:从单实例走向多实例

当应用扩展到多个实例,可以定义 upstream

upstream app_backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://app_backend;
    }
}

默认策略是轮询:请求依次分配到不同后端。

1. 权重

upstream app_backend {
    server 10.0.0.11:8080 weight=3;
    server 10.0.0.12:8080 weight=1;
}

第一台机器理论上会获得更多请求,适合配置差异明显的实例。

2. 最少连接

upstream app_backend {
    least_conn;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

适合请求处理时长差异较大的场景。

3. 基于客户端地址的会话粘性

upstream app_backend {
    ip_hash;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

同一客户端地址通常会落到同一后端。但这并不是理想的会话方案:用户可能经过 NAT,共享同一个出口地址;客户端地址也可能变化。

更推荐把会话放到 Redis、数据库或签名 Cookie 中,让应用实例保持无状态。

4. 被动故障处理

upstream app_backend {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
}

开源版 Nginx 常见的是被动失败判断:真实请求失败达到一定次数后,暂时认为节点不可用。主动健康检查属于另一类能力,选型时要区分清楚。

5. 复用到上游的连接

upstream app_backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    keepalive 32;
}

location / {
    proxy_pass http://app_backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

上游 keepalive 可以减少频繁建立 TCP 连接的成本,但连接池大小需要结合 worker 数量、后端容量和真实并发评估。


十、HTTPS:不只是“配置一张证书”

一个常见的 HTTPS 配置如下:

server {
    listen 80;
    server_name example.com www.example.com;

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

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

上线前至少检查四件事:

  1. 1. 证书是否包含完整链;
  2. 2. 证书域名是否覆盖当前域名;
  3. 3. 私钥权限是否安全;
  4. 4. 应用是否正确识别代理前的 HTTPS 协议。

HSTS 要谨慎开启

add_header Strict-Transport-Security "max-age=31536000" always;

HSTS 会要求浏览器在有效期内只通过 HTTPS 访问。它能提升安全性,但也会放大证书或 HTTPS 配置错误的影响。

在确认所有子域名都已长期支持 HTTPS 之前,不要贸然加入 includeSubDomains;更不要在测试不充分时提交浏览器预加载列表。


十一、静态资源优化:缓存、压缩与版本化

1. 浏览器缓存

对于带内容哈希的静态资源,例如:

app.8f3a2c.js
style.19ab42.css

可以设置长缓存:

location ~* \.(js|css|png|jpg|jpeg|gif|svg|webp|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000, immutable";
}

但 HTML 通常不应该长期缓存,因为它负责引用最新版本的资源:

location = /index.html {
    add_header Cache-Control "no-cache";
}

最可靠的发布策略是:

  • • 文件内容改变时,文件名也改变;
  • • 哈希静态资源长期缓存;
  • • HTML 短缓存或不缓存。

2. Gzip 压缩

gzip on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_vary on;
gzip_types
    text/plain
    text/css
    application/json
    application/javascript
    application/xml
    image/svg+xml;

压缩级别不是越高越好。级别过高会增加 CPU 消耗,收益却可能很有限。通常应基于真实文件、CPU 使用率和带宽测试,而不是盲目拉满。

图片、视频、压缩包等已经压缩过的内容,继续使用 Gzip 往往收益很小。


十二、代理缓存:让 Nginx 替后端扛住热点流量

先在 http 上下文定义缓存区域:

proxy_cache_path /var/cache/nginx/api
    levels=1:2
    keys_zone=api_cache:100m
    max_size=10g
    inactive=60m
    use_temp_path=off;

再在业务位置启用:

location /api/public/ {
    proxy_pass http://app_backend;

    proxy_cache api_cache;
    proxy_cache_methods GET HEAD;
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;

    add_header X-Cache-Status $upstream_cache_status always;
}

常见缓存状态包括:

  •  MISS:没有缓存,向后端请求;
  •  HIT:直接命中缓存;
  •  EXPIRED:缓存已过期;
  •  BYPASS:跳过缓存;
  •  UPDATING:缓存正在更新。

不要缓存个性化响应

带有用户身份、购物车、权限结果、一次性令牌的响应,通常不应该共享缓存。

可以根据 Cookie 或 Authorization 头跳过缓存:

map $http_authorization $skip_auth_cache {
    default 1;
    ""      0;
}

location /api/public/ {
    proxy_pass http://app_backend;

    proxy_cache api_cache;
    proxy_cache_bypass $skip_auth_cache;
    proxy_no_cache $skip_auth_cache;
}

缓存设计必须先回答一个问题:哪些请求在不同用户之间具有完全相同的响应?

没有明确答案,就不要轻易共享缓存。


十三、限流与并发控制:保护服务,不是惩罚用户

1. 请求速率限制

 http 上下文定义区域:

limit_req_zone $binary_remote_addr zone=api_rate:10m rate=10r/s;

在接口上使用:

location /api/ {
    limit_req zone=api_rate burst=20 nodelay;
    proxy_pass http://app_backend;
}

含义是:

  • • 平均速率为每秒 10 个请求;
  • • 允许最多 20 个突发请求;
  •  nodelay 表示突发请求在额度内立即处理,而不是排队平滑释放。

2. 并发连接限制

limit_conn_zone $binary_remote_addr zone=per_ip_conn:10m;

server {
    location /download/ {
        limit_conn per_ip_conn 5;
    }
}

3. 限流键要根据业务设计

按 IP 限流简单,但并不总是公平:

  • • 公司或校园网络可能共享出口 IP;
  • • 移动网络地址可能变化;
  • • 攻击者可能拥有大量 IP。

登录用户场景可以考虑按用户标识、API Key 或组合维度限流。无论采用什么键,都要配合监控和合理的错误响应。

建议明确返回 429:

limit_req_status 429;

同时让客户端知道应该退避重试,而不是疯狂重放。


十四、安全加固:从“默认能用”到“生产可用”

1. 隐藏版本信息

server_tokens off;

它不能替代升级和补丁,但可以减少不必要的信息暴露。

2. 限制不需要的 HTTP 方法

只读资源可以这样处理:

location /public/ {
    limit_except GET HEAD {
        deny all;
    }
}

不要在全局机械禁用方法,API 可能确实需要 POST、PUT、PATCH 或 DELETE。

3. 常见安全响应头

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;

内容安全策略 CSP 很强大,但必须根据页面实际加载的脚本、样式、字体和第三方资源精细设计。直接复制一份严格 CSP,可能让页面大面积失效;过度宽松又起不到保护作用。

4. 注意 add_header 的继承规则

add_header 有一个很容易被忽略的行为:如果当前配置层级声明了任意 add_header,上一级的 add_header 通常不会继续继承下来。

例如,server 中设置了安全响应头,但某个 location 又单独设置缓存头:

server {
    add_header X-Content-Type-Options "nosniff" always;

    location /assets/ {
        add_header Cache-Control "public, max-age=2592000";
    }
}

此时 /assets/ 的响应可能只有缓存头,而缺少上层的安全头。生产环境可以把公共响应头整理到独立的 snippet 中,在所有需要声明 add_header 的层级重复 include;也可以显式重复这些头,并通过 curl -I 验证最终响应。

5. 禁止访问隐藏文件

location ~ /\. {
    deny all;
}

但 ACME 证书验证可能需要访问 /.well-known/,应先添加例外:

location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
}

location ~ /\. {
    deny all;
}

6. 上传目录不要执行脚本

用户可上传内容的目录应与可执行代码严格隔离。不要仅依赖文件扩展名判断安全性,也不要让上传目录落入动态脚本处理规则。


十五、日志:排障的第一现场

默认访问日志通常信息有限。可以定义一份更适合分析的格式:

log_format main_ext
    '$remote_addr - $remote_user [$time_local] '
    '"$request$status $body_bytes_sent '
    'rt=$request_time urt=$upstream_response_time '
    'ua="$http_user_agent" xff="$http_x_forwarded_for" '
    'upstream="$upstream_addr" cache="$upstream_cache_status"';

access_log /var/log/nginx/access.log main_ext;

重点变量:

变量含义
$request_time从读取请求到发送完响应的总时间
$upstream_connect_time连接后端耗时
$upstream_header_time等到后端响应头的耗时
$upstream_response_time与后端交互的总耗时
$upstream_addr实际命中的后端地址
$statusNginx 返回给客户端的状态码
$upstream_status后端返回给 Nginx 的状态码

判断慢请求时,可以这样分析:

  •  request_time 高、upstream_response_time 也高:大概率后端慢;
  •  request_time 高、上游时间低:可能是客户端慢、响应体大或代理层排队;
  •  upstream_connect_time 高:后端连接建立慢、网络异常或连接耗尽;
  • • Nginx 返回 502 且没有正常上游状态:后端可能未监听、重启或连接被拒绝。

错误日志级别

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

常见级别包括 errorwarnnoticeinfodebug

生产环境不建议长期启用 debug,因为日志量会非常大。需要深度排障时,应限定时间和范围,并确认当前构建是否支持调试日志。


十六、性能优化:先测量,再调整

一份常见的基础配置:

worker_processes auto;

worker_rlimit_nofile 65535;

events {
    worker_connections 8192;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 65;
    keepalive_requests 1000;
}

但请注意:这些参数并不是“复制后性能就翻倍”。

1. worker_processes auto

通常会根据可用 CPU 自动设置 worker 数量,是一个合理起点。

2. worker_connections

它表示单个 worker 可打开的连接数量上限之一,但实际容量还受文件描述符上限、系统内核、上游连接、日志和其他资源影响。

反向代理场景中,一个客户端请求可能同时占用客户端连接和上游连接,因此不能简单地用:

worker 数量 × worker_connections

直接等同于业务并发数。

3. 文件描述符限制

需要同时关注:

ulimit -n
cat /proc/$(cat /run/nginx.pid)/limits

systemd 服务还可能有独立的 LimitNOFILE限制。

4. Keepalive

长连接可以减少 TCP 和 TLS 握手,但超时时间过长会让大量空闲连接占用资源。合理值取决于客户端类型、请求频率和前置负载均衡器。

5. 压测要接近真实流量

不要只用一个固定小响应测 QPS。至少模拟:

  • • 不同 URI;
  • • 命中与未命中缓存;
  • • 不同响应大小;
  • • 长连接与短连接;
  • • 正常请求与慢请求;
  • • 接近生产的 TLS、日志和上游调用。

优化目标也不应该只有平均响应时间,还要关注 P95、P99、错误率、CPU、内存、连接数和后端负载。


十七、平滑重载:为什么修改配置通常不需要重启

执行:

nginx -t && nginx -s reload

Nginx 会让 master 进程重新读取配置,并启动新的 worker。旧 worker 不再接收新连接,在处理完已有请求后退出。

这就是平滑重载。

它非常适合常规配置变更,但仍应注意:

  • • 语法正确不代表业务逻辑正确;
  • • 证书和私钥路径可能存在权限问题;
  • • 新配置可能导致流量路由错误;
  • • 长连接可能让旧 worker 存活较久;
  • • 频繁重载会造成管理混乱。

生产变更应配合:

  1. 1. 配置版本管理;
  2. 2. 自动语法检查;
  3. 3. 小流量验证;
  4. 4. 监控错误率和延迟;
  5. 5. 明确回滚方案。

十八、最常见的状态码与排障思路

1. 502 Bad Gateway

常见原因:

  • • 后端服务没有启动;
  • • IP 或端口写错;
  • • Unix Socket 路径或权限错误;
  • • 后端主动断开连接;
  • • 协议不匹配,例如把 HTTPS 上游当作 HTTP;
  • • 容器网络或防火墙不通。

排查:

curl -v http://127.0.0.1:8080/health
ss -lntp
journalctl -u nginx
tail -f /var/log/nginx/error.log

2. 504 Gateway Timeout

常见原因:

  • • 后端处理过慢;
  • • 数据库、缓存或第三方接口超时;
  •  proxy_read_timeout 小于业务实际耗时;
  • • 后端线程池或连接池耗尽。

不要一上来就调大超时。先查看应用链路和上游响应时间。

3. 413 Request Entity Too Large

请求体超过限制:

client_max_body_size 50m;

还要检查更上游的 CDN、云负载均衡器和应用框架是否有独立限制。

4. 403 Forbidden

常见原因:

  • • 文件或目录权限不足;
  • • 没有索引文件且禁止目录列表;
  •  denylimit_except 等访问控制命中;
  • • 安全模块或系统安全策略阻止访问。

检查 Nginx worker 的运行用户是否有权限逐级进入目录。

5. 重定向循环

常见于:

  • • 前置负载均衡器已经终结 HTTPS,但后端误以为请求仍是 HTTP;
  • • 应用和 Nginx 同时执行不一致的跳转;
  •  X-Forwarded-Proto 未正确传递或未被应用信任。

可使用:

curl -I -L --max-redirs 10 https://example.com

观察每一跳的 Location

6. 修改配置后不生效

依次检查:

nginx -t
nginx -T | less
ps -ef | grep nginx
systemctl status nginx

最常见的情况是:改错文件、配置没有被 include、重载的是另一个实例,或者容器内外文件并不是同一份。


十九、一份可作为起点的生产配置

下面的示例把前面的知识串起来。它不是“万能最佳配置”,而是一份便于理解和继续改造的骨架。

/etc/nginx/nginx.conf

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

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

events {
    worker_connections 8192;
}

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

    log_format main_ext
        '$remote_addr - $remote_user [$time_local] '
        '"$request$status $body_bytes_sent '
        'rt=$request_time urt=$upstream_response_time '
        'upstream="$upstream_addr" xff="$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main_ext;

    sendfile on;
    tcp_nopush on;
    keepalive_timeout 65;
    keepalive_requests 1000;

    server_tokens off;

    gzip on;
    gzip_min_length 1024;
    gzip_comp_level 5;
    gzip_vary on;
    gzip_types
        text/plain
        text/css
        application/json
        application/javascript
        application/xml
        image/svg+xml;

    limit_req_zone $binary_remote_addr zone=api_rate:10m rate=20r/s;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/example.conf

upstream app_backend {
    least_conn;

    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;

    keepalive 32;
}

server {
    listen 80;
    server_name example.com www.example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

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

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Frame-Options "SAMEORIGIN" always;

    root /var/www/example;
    index index.html;

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

    location /api/ {
        limit_req zone=api_rate burst=40 nodelay;
        limit_req_status 429;

        client_max_body_size 20m;

        proxy_pass http://app_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 60s;
        proxy_read_timeout 60s;
    }

    location /ws/ {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 1h;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|svg|webp|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000, immutable";
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "strict-origin-when-cross-origin" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        try_files $uri =404;
    }

    location ~ /\. {
        deny all;
    }
}

上线前执行:

nginx -t
nginx -T > /tmp/nginx-effective.conf
nginx -s reload

然后至少验证:

curl -I http://example.com
curl -I https://example.com
curl -v https://example.com/api/health
curl -I https://example.com/assets/app.js

二十、从“会用”到“精通”的学习路线

Nginx 的“精通”,不是记住所有指令,而是具备三种能力。

第一阶段:能正确配置

掌握:

  •  server  location
  • • 静态文件;
  •  proxy_pass
  • • HTTPS;
  • • 日志与基础排障。

目标是独立完成一个网站和一组 API 的部署。

第二阶段:能解释流量路径

面对一个请求,你能清楚回答:

  1. 1. 请求先到哪个端口;
  2. 2. 命中哪个 server
  3. 3. 命中哪个 location
  4. 4. URI 是否被重写;
  5. 5. 哪些头部传给上游;
  6. 6. 是否命中缓存;
  7. 7. 最终由哪个实例响应;
  8. 8. 日志中如何还原整个过程。

这一步决定了你是否能快速排障。

第三阶段:能基于数据做优化

掌握:

  • • 负载均衡与故障转移;
  • • 缓存命中率;
  • • 限流策略;
  • • TLS 与连接复用;
  • • P95、P99 延迟;
  • • 系统连接、文件描述符和网络参数;
  • • 灰度发布与回滚。

真正的优化不是“参数越大越好”,而是找到当前系统的瓶颈,并用指标证明修改有效。

第四阶段:把配置工程化

生产环境应逐步建立:

  • • Git 版本管理;
  • • 环境化配置模板;
  • • CI 中执行 nginx -t
  • • 自动化证书续期;
  • • 日志集中采集;
  • • 指标监控与告警;
  • • 变更审计;
  • • 灰度、回滚和灾备流程。

到这个阶段,Nginx 不再是一份手工维护的配置文件,而是整个交付体系中的基础设施代码。


二十一、上线前检查清单

可以把下面这份清单放进团队发布流程。

配置

  •  nginx -t 通过;
  • • 使用 nginx -T 确认实际加载配置;
  • • 域名、端口和默认站点符合预期;
  •  location 匹配顺序已验证;
  •  proxy_pass 路径拼接已用真实请求测试;
  • • 上游地址和协议正确;
  • • 上传大小、超时和缓冲策略符合业务需求。

HTTPS 与安全

  • • 证书、私钥和完整证书链正确;
  • • HTTP 到 HTTPS 跳转无循环;
  • • TLS 协议和安全头已验证;
  • • 隐藏文件和敏感目录不可访问;
  • • 上传目录不具备脚本执行能力;
  • • 限流不会误伤主要用户群体;
  • • 后端只信任来自可信代理的转发头。

性能与稳定性

  • • 日志中包含请求时间和上游时间;
  • • worker、文件描述符和连接上限协调;
  • • 静态资源缓存策略正确;
  • • 个性化接口没有被共享缓存;
  • • 压测模型接近真实流量;
  • • 监控覆盖 QPS、错误率、P95/P99、连接数和上游状态;
  • • 已准备回滚方案。

结语

Nginx 的配置语法并不复杂,复杂的是它处在整个请求链路的最前面:域名、证书、路由、缓存、限流、连接、日志和后端状态,都会在这里交汇。

初学时,你可能只需要记住一条反向代理配置;进入生产后,你需要理解请求从客户端到后端、再从后端返回客户端的每一步。

学习 Nginx 最有效的方法,不是背完整指令表,而是反复练习三件事:

  1. 1. 画出流量路径;
  2. 2. 用日志验证判断;
  3. 3. 用压测和监控证明优化。

当你能够根据一条请求,准确解释它命中了哪个配置、被转发到哪里、为什么慢、为什么失败,以及如何安全地修改和回滚时,你就已经真正从“入门”走向了“精通”。


建议收藏本文,并在每次修改生产配置前重复执行:

nginx -t && nginx -s reload

配置可以重写,线上事故却不一定能撤回。


阅读原文:点击这里


该文章在 2026/7/30 9:26:22 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号