跳到主要内容

Nginx 速查表:80+ 配置与命令带真实例子和常见坑

Nginx 速查表,常见配置、location/server 块、SSL、反代、gzip,真实例子和常见坑。

  • 本地处理
  • 分类 开发运维
  • 适合 格式化、校验、压缩或检查和代码相关的文本。
157
命令 (17)
nginx -t

测试当前配置语法是否正确,不会真正重载 nginx。

常见坑: 热重载之前一定先跑这条。配置写错再盲目 reload,两秒把线上打趴。

例子
nginx -t
sudo nginx -t
nginx -t -c /etc/nginx/nginx.conf
nginx -s reload

热重载配置,已建立的连接不会断(优雅 reload,零停机)。

常见坑: reload 时如果新配置解析失败,nginx 会静默继续跑旧配置。所以一定先 nginx -t。

例子
sudo nginx -s reload
sudo systemctl reload nginx
nginx -t && nginx -s reload
nginx -s stop

快速停止 nginx,立刻关掉监听端口并杀掉所有 worker。

常见坑: stop 会切断正在处理的请求。要优雅停就用 nginx -s quit。

例子
sudo nginx -s stop
sudo systemctl stop nginx
nginx -s quit

优雅停止,worker 把手里的请求处理完再退出。

例子
sudo nginx -s quit
nginx -s reopen

重新打开日志文件。logrotate 把 access.log / error.log 切走后用这条让 nginx 写入新文件。

常见坑: 不 reopen,nginx 还在往被切走的文件描述符里写,磁盘悄悄被撑爆都看不见。

例子
sudo nginx -s reopen
postrotate
    /usr/sbin/nginx -s reopen
endscript
nginx -V

打印 nginx 版本、编译参数、已编译的所有模块。注意是大 V,小 v 只显示版本号。

常见坑: 想确认某个模块(--with-http_v2_module、--with-http_realip_module 这种)有没有编进去,先 -V 看,免得调一小时才发现是 directive unknown。

例子
nginx -V
nginx -V 2>&1 | tr -- - "\n" | grep _module
nginx -T

测试配置并把所有 include 合并后的最终配置打到 stdout。看 nginx 真正读到的配置就用它。

例子
sudo nginx -T
sudo nginx -T | grep server_name
sudo nginx -T > /tmp/nginx-full.conf
nginx -c <file>

用指定配置文件启动 nginx(测试一份候选配置时很方便)。

例子
nginx -c /tmp/nginx-test.conf
nginx -t -c /tmp/nginx-test.conf
nginx -p <prefix>

运行时覆盖 prefix 目录,适合非 root 用户用本地拷贝跑 nginx。

例子
nginx -p $(pwd) -c $(pwd)/nginx.conf
systemctl status nginx

systemd 系统上看 nginx 服务的运行状态、最近日志、上次退出原因。

例子
sudo systemctl status nginx
sudo systemctl restart nginx
sudo journalctl -u nginx -n 100 --no-pager
nginx -g <directives>

在读取配置文件之前,从命令行注入全局指令。临时覆盖时很顺手。

常见坑: 每条指令要带分号,整体作为一个带引号的参数:nginx -g "daemon off;"。Docker 里让 nginx 前台运行常用它。

例子
nginx -g "daemon off;"
nginx -g "worker_processes 2;"
nginx -g "daemon off; master_process off;"
nginx -s reload (HUP signal)

reload 信号就是 SIGHUP。nginx -s reload 和 kill -HUP <master_pid> 完全等价。

例子
kill -HUP $(cat /run/nginx.pid)
nginx -s reload
kill -HUP `cat /run/nginx.pid`
kill -USR1 (reopen logs)

SIGUSR1 让 master 重新打开日志文件。和 nginx -s reopen 一样,logrotate 脚本常直接发它。

例子
kill -USR1 $(cat /run/nginx.pid)
kill -USR1 `pidof -s nginx`
kill -USR2 (upgrade binary)

SIGUSR2 用新 nginx 二进制起一个新 master,连接不断。经典的零停机二进制升级。

常见坑: USR2 之后有两个 master。给旧 master 发 WINCH 让它的 worker 优雅退场,确认无误后再给旧 master 发 QUIT。新二进制出问题就给旧 master 发 HUP 回滚。

例子
kill -USR2 $(cat /run/nginx.pid)
# old pid file becomes nginx.pid.oldbin
kill -WINCH $(cat /run/nginx.pid.oldbin)
kill -QUIT $(cat /run/nginx.pid.oldbin)
nginx -v

只打印 nginx 版本号。小写 v,大写 V 还会打印编译参数和模块。

例子
nginx -v
nginx -v 2>&1
nginx -q

配置测试时只输出错误,屏蔽其他信息。配合 -t 用于安静的 CI 检查。

例子
nginx -t -q
nginx -tq && nginx -s reload
nginx -e <file>

启动时覆盖 error-log 路径(nginx ≥ 1.19.5)。默认日志路径还不可写时有用。

例子
nginx -e /dev/stderr -g "daemon off;"
nginx -t -e stderr
核心指令 (21)
worker_processes auto;

worker 进程数。auto 等于 CPU 核数,绝大多数场景就用 auto。

常见坑: 设得比核数大反而带来更多上下文切换,吞吐一点不涨。

例子
worker_processes auto;
worker_processes 4;
worker_connections 1024;

每个 worker 的最大并发连接。总上限 ≈ worker_processes × worker_connections。

常见坑: 调大这个的同时要把系统文件描述符上限一起抬(worker_rlimit_nofile 和 systemd 的 LimitNOFILE),否则报 too many open files 给你看。

例子
events {
    worker_connections 1024;
}
events {
    worker_connections 10240;
    multi_accept on;
    use epoll;
}
events { ... }

事件处理设置块:worker_connections、multi_accept、use(epoll/kqueue)写在这里。

例子
events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}
http { ... }

顶层 http 配置块:MIME、gzip、upstream 池、server 块都写在里面。

例子
http {
    include /etc/nginx/mime.types;
    sendfile on;
    keepalive_timeout 65;
    include /etc/nginx/conf.d/*.conf;
}
include <path>;

引入另一份配置文件(支持 glob 通配)。拆 vhost 的标准做法。

常见坑: include 顺序在指令互相覆盖时有意义。include conf.d/*.conf 按字母序加载,所以 00-default.conf 先生效。

例子
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
user www-data;

worker 进程跑在哪个系统用户下。master 是 root,worker 降权到这个用户。

常见坑: worker 读不到静态文件时 error.log 里就是 Permission denied。检查文件归属和这个用户是否对得上。

例子
user www-data;
user nginx;
user nobody nogroup;
pid /run/nginx.pid;

nginx master 进程的 PID 文件路径,nginx -s 系列信号依赖它。

例子
pid /run/nginx.pid;
pid /var/run/nginx.pid;
sendfile on;

启用内核 sendfile() 系统调用,磁盘到 socket 零拷贝。静态文件性能起飞。

常见坑: VirtualBox 共享目录、老版本 NFS 挂载上要关 sendfile,不然文件传出去是损坏的。

例子
sendfile on;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;

keep-alive 连接的空闲超时(秒)。65 覆盖浏览器默认的 60 秒计时加上一点抖动。

例子
keepalive_timeout 65;
keepalive_timeout 75s;
client_max_body_size 10m;

请求体最大大小(上传限制)。默认 1m。可以在 http、server、location 三层任意一层写。

常见坑: 上传报 413 Request Entity Too Large 就是它。要么调大,要么改流式上传。

例子
client_max_body_size 10m;
client_max_body_size 100m;
client_max_body_size 0;  # unlimited
worker_rlimit_nofile 65535;

抬高 worker 的文件描述符上限。worker_connections 调大时必须配套加这条。

例子
worker_rlimit_nofile 65535;
multi_accept on;

让每个 worker 一次性接受所有新连接,而不是每轮事件循环只接一个。默认 off。

常见坑: 突发连接风暴时有帮助,但稳定负载下可能轻微拉高延迟。生产开启前先压测。

例子
events {
    multi_accept on;
    worker_connections 4096;
}
use epoll;

选择连接处理方式。Linux 用 epoll,BSD/macOS 用 kqueue。nginx 会自动选,写出来只是为了明确。

例子
events { use epoll; }
events { use kqueue; }  # BSD / macOS
tcp_nopush on;

把响应头和文件开头放进一个数据包发出(Linux 的 TCP_CORK)。只有 sendfile 开着才生效。

例子
sendfile on;
tcp_nopush on;
tcp_nodelay on;

在 keep-alive 连接上关掉 Nagle 算法,小响应立刻发出去。默认 on。

例子
tcp_nodelay on;
server_tokens off;

从错误页和 Server 响应头里隐藏 nginx 版本号。便宜又标准的加固动作。

常见坑: 它只是隐藏版本号,不会去掉 Server: nginx 这个头本身。要彻底删掉这个头得用 headers-more 模块或打编译补丁。

例子
http {
    server_tokens off;
}
types_hash_max_size 2048;

MIME 类型哈希表的大小。加了很多自定义类型时,nginx 启动告警会直接提示你调大它。

例子
types_hash_max_size 2048;
types_hash_bucket_size 128;
default_type application/octet-stream;

nginx 无法从扩展名推断时的兜底 Content-Type。octet-stream 表示「下载这个文件」。

例子
include /etc/nginx/mime.types;
default_type application/octet-stream;
client_body_timeout 12s;

读取请求体两次之间允许的最大空闲时间。上传卡得比这久的客户端会拿到 408。

例子
client_body_timeout 12s;
client_header_timeout 12s;
send_timeout 10s;
large_client_header_buffers 4 8k;

存放大请求头的缓冲区数量和大小。长 cookie 或长 URL 触发 400 / 414 时调大它。

常见坑: 请求行超过缓冲区大小报 414 Request-URI Too Large,头超大报 400。两者都指向这条,不是 client_max_body_size。

例子
large_client_header_buffers 4 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
resolver 1.1.1.1 valid=300s;

运行时解析域名用的 DNS(变量形式的 upstream、OCSP、auth_request 都需要)。每条结果缓存 valid 时长。

常见坑: 不写 resolver,proxy_pass https://$backend 这种变量主机会报 no resolver defined。写死的主机名只在启动时解析一次,不需要它。

例子
resolver 1.1.1.1 8.8.8.8 valid=300s ipv6=off;
resolver_timeout 5s;
Server 块 (12)
server { ... }

定义一个虚拟主机。多个 server 块能让一个 nginx 在同一端口上服务多个域名。

例子
server {
    listen 80;
    server_name example.com;
    root /var/www/example;
}
listen 80;

本 server 块监听的端口(也可指定地址和 flag)。加 default_server 兜底处理未知 Host 的请求。

常见坑: 每个端口只能有一个 default_server。设两个 nginx -t 直接报错。不设就是按字母序第一个 server 兜底。

例子
listen 80;
listen 443 ssl http2;
listen 80 default_server;
listen [::]:80;  # IPv6
server_name example.com www.example.com;

本 server 块接收哪些 Host 头。支持通配符(*.example.com)和正则(~^api\..+$)。

常见坑: server_name 不能首尾都是 *(比如 *.example.* 非法)。正则形式必须以 ~ 开头。

例子
server_name example.com;
server_name example.com www.example.com;
server_name *.example.com;
server_name ~^(?<sub>.+)\.example\.com$;
root /var/www/html;

本 server / location 的文档根目录。在 server 层定一次,特定 location 需要时再覆盖。

例子
root /var/www/html;
root /home/user/site/public;
index index.html index.htm;

当 URI 映射到目录时,依次尝试这几个默认文件,第一个存在的胜出。

例子
index index.html;
index index.html index.htm index.php;
listen 443 ssl http2;

监听 443,启用 TLS 和 HTTP/2。这一行就把两件事都开了。

常见坑: nginx ≥ 1.25 建议改写:listen 443 ssl; 再加 http2 on; 这条指令。行内 http2 在新版里被标记为弃用。

例子
listen 443 ssl http2;
listen 443 ssl;
http2 on;
listen [::]:443 ssl http2;
return 444;

不发任何响应直接断连。常用于丢掉 Host 头不对的请求。

例子
# Catch-all server that drops unknown hosts
server {
    listen 80 default_server;
    server_name _;
    return 444;
}
listen 80 reuseport;

启用 SO_REUSEPORT,让内核把新连接分摊给各个 worker。高连接速率下减少锁争用。

常见坑: 同一个 address:port 只能在一条 listen 上写 reuseport。在第二个 server 块对同一 socket 重复写会起不来。

例子
listen 80 reuseport;
listen 443 ssl reuseport;
listen unix:/run/nginx.sock;

监听 Unix 域 socket 而不是 TCP 端口。同机上被另一个 nginx 或 HAProxy 套在前面时常用。

例子
listen unix:/run/nginx-internal.sock;
server_name _;

「无效」兜底名。它匹配不到任何真实 Host 头,所以配合 default_server 就能兜住所有没被命名 server 认领的请求。

常见坑: _ 不是通配符,它能兜底是因为永远匹配不上。真正的兜底行为来自 listen 那行的 default_server。

例子
server {
    listen 80 default_server;
    server_name _;
    return 444;
}
absolute_redirect off;

让 nginx 在自动重定向(比如补斜杠)时发相对 Location 头,而不是带错误主机/端口的绝对 URL。

常见坑: 在终止 TLS 的代理后面,nginx 的自动重定向可能发出 http://internal:8080/path/。要么关掉它,要么设 port_in_redirect off 加正确的 server_name。

例子
absolute_redirect off;
port_in_redirect off;
server_name_in_redirect off;
merge_slashes off;

默认 nginx 会把 URI 里的 // 合并成 /。后端确实需要保留双斜杠时把合并关掉。

常见坑: 关掉它会改变 location 前缀的匹配方式,改完要重测路由。大多数应用应保持默认 on。

例子
merge_slashes off;
Location 匹配 (12)
location / { ... }

前缀匹配,任何以这个字符串开头的 URI 都命中。默认的兜底匹配。

例子
location / {
    try_files $uri $uri/ /index.html;
}
location = /healthz { ... }

精确匹配(=)。优先级最高,URI 完全等于时立刻命中,nginx 不再继续找。

常见坑: 热点端点(/healthz、/ 这种)用 =,能跳过整轮正则 location 扫描,高 QPS 下能省下真实的 CPU。

例子
location = /healthz {
    return 200 "ok";
    access_log off;
}
location = / {
    return 301 /home;
}
location ^~ /static/ { ... }

带优先级覆盖的前缀匹配(^~)。命中后 nginx 不再尝试任何正则 location。

例子
location ^~ /static/ {
    alias /var/www/site/static/;
    expires 30d;
}
location ~ \.php$ { ... }

区分大小写的正则匹配(~)。不区分大小写改用 ~*。

例子
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
}
location ~* \.(jpg|jpeg|png|gif|webp)$ { ... }

不区分大小写的正则匹配(~*)。图片缓存的常见写法。

例子
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}
location matching priority

匹配优先级:1) 精确 = 2) 最长 ^~ 前缀 3) 文件顺序里第一个匹配的正则(~ / ~*) 4) 最长普通前缀。

常见坑: 正则之间是按文件顺序「第一个匹配的胜出」,不是最长或最佳。被命中错了就调正则 location 的顺序。

例子
# Tested order:
location = / { ... }              # 1 exact
location ^~ /static/ { ... }       # 2 ^~ prefix
location ~* \.(jpg|png)$ { ... }   # 3 regex (first match)
location / { ... }                 # 4 plain prefix (fallback)
named location @fallback

用 @ 命名的 location 只能通过内部跳转到达(try_files 最后一个参数、error_page)。它永远不会直接匹配外部 URI。

例子
location / {
    try_files $uri $uri/ @app;
}

location @app {
    proxy_pass http://backend;
}
internal;

把 location 标记为只能从 nginx 内部到达(内部跳转、X-Accel-Redirect、error_page)。外部请求直接 404。

例子
location /protected/ {
    internal;
    alias /var/www/private/;
}
location /download/ {
    internal;
    root /data;
}
X-Accel-Redirect (protected downloads)

让应用先鉴权,再通过内部 location 把文件交给 nginx。nginx 用 sendfile 流式发出,应用根本不碰文件内容。

常见坑: 目标 location 必须标 internal;,否则任何人都能直接拿文件,绕过鉴权。

例子
# nginx side
location /protected-files/ {
    internal;
    alias /var/secure/;
}

# app returns header after auth:
#   X-Accel-Redirect: /protected-files/report.pdf
deny / allow (IP access control)

按客户端 IP / CIDR 放行或拒绝。规则自上而下,第一个命中的生效。白名单结尾用 deny all 收口。

常见坑: 在代理后面 $remote_addr 是代理 IP,这些规则看到的地址不对。配合 realip 模块才能按真实客户端匹配。

例子
location /admin/ {
    allow 10.0.0.0/8;
    allow 192.168.1.0/24;
    deny all;
}
auth_basic "Restricted";

HTTP Basic 认证。auth_basic_user_file 指向 htpasswd 文件。给预发或后台路径加的简单门禁。

常见坑: Basic 认证的密码只是 base64 编码不是加密,只能在 HTTPS 上用。用 htpasswd -c /etc/nginx/.htpasswd user 生成文件。

例子
location /admin/ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
}
auth_request /auth;

把鉴权委托给一个子请求。nginx 先调鉴权端点,2xx 放行,401/403 拦截。

常见坑: 需要 --with-http_auth_request_module(多数发行版包已内置)。鉴权端点应设 internal;,只返回状态码不返回 body。

例子
location /private/ {
    auth_request /auth;
    proxy_pass http://backend;
}

location = /auth {
    internal;
    proxy_pass http://auth-service/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}
反向代理 (16)
proxy_pass http://backend;

把请求转发给上游后端。可以是 URL,也可以是 upstream { } 里定义的名字。

常见坑: 末尾斜杠很关键:proxy_pass http://backend/ 会改写 URI,proxy_pass http://backend 保持原样。搞混了路由会静默坏掉。

例子
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}
location / {
    proxy_pass http://backend;
}
upstream backend { ... }

定义一个命名的后端服务器池。用 proxy_pass http://<name> 引用。

例子
upstream backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    keepalive 32;
}
upstream backend {
    least_conn;
    server app1.internal:8080 weight=3;
    server app2.internal:8080;
}
proxy_set_header Host $host;

把原始 Host 头转给后端。不设的话后端看到的是 upstream 名字,路由和 cookie 会坏。

常见坑: 一旦写了 proxy_set_header,默认转发的 Host + Connection 头就被替换了,自定义 header 的时候记得把 Host 显式 set 回来。

例子
proxy_set_header Host $host;
proxy_set_header Host $http_host;  # includes :port
proxy_set_header X-Real-IP $remote_addr;

把真实客户端 IP 转给后端。不设后端日志里看到的全是 nginx 自己。

例子
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 X-Forwarded-Host $host;
proxy_http_version 1.1;

到 upstream 用 HTTP/1.1。keep-alive 和 WebSocket 升级都依赖它。

常见坑: 默认是 HTTP/1.0,不写 proxy_http_version 1.1 的话 upstream keep-alive 等于没开,每次请求新建一条 TCP 连接。

例子
proxy_http_version 1.1;
proxy_http_version 1.1;
proxy_set_header Connection "";
WebSocket upgrade

转发 Upgrade + Connection 头,让 HTTP 请求顺利升级为 WebSocket。

常见坑: 少转任何一个头,浏览器 DevTools Network 里 WS 握手就一直 Pending。

例子
location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 3600s;
}
proxy_read_timeout 60s;

从 upstream 读两次之间允许的最大间隔(默认 60s)。慢 API 或 WebSocket 要调大。

常见坑: WebSocket 空闲 60 秒就被 504 断了。WS 端点要设到 3600s 或更长。

例子
proxy_read_timeout 60s;
proxy_read_timeout 3600s;  # for WebSockets
proxy_connect_timeout 5s;

nginx 等待与 upstream 建立 TCP 连接的最长时间。默认 60s 通常太宽。

例子
proxy_connect_timeout 5s;
proxy_connect_timeout 2s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
proxy_buffering on;

把 upstream 响应缓冲后再发给客户端。on 提升吞吐,off 适合流式响应。

常见坑: SSE(Server-Sent Events)或实时日志流场景一定 proxy_buffering off,不然事件会被攒着不发。

例子
proxy_buffering on;
location /events {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_cache off;
}
proxy_redirect off;

禁掉 nginx 自动改写 upstream 返回的 Location / Refresh 头。用 default 让 nginx 按 proxy_pass 自动改写。

例子
proxy_redirect off;
proxy_redirect default;
proxy_set_header Connection "";

清空 Connection 头,upstream keep-alive 才真正生效。配合 proxy_http_version 1.1 用于连接池化的 upstream。

常见坑: keep-alive 连接池要把 Connection 设为空字符串,不是 "keep-alive"。WebSocket 的 location 要设成 "upgrade"。搞反了总有一个坏。

例子
upstream api { server 127.0.0.1:3000; keepalive 32; }

location /api/ {
    proxy_pass http://api;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}
proxy_next_upstream error timeout;

nginx 在什么条件下把请求重试到下一台 upstream。让负载均衡跳过挂掉的后端。

常见坑: 别盲目加 non_idempotent,把失败的 POST 重试一遍可能重复扣款或重复写库。nginx 默认就不重试非幂等请求。

例子
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
proxy_cache_bypass $http_pragma;

定义什么时候绕过缓存直接打 upstream(响应仍可能被缓存)。常见:带 no-cache 提示时绕过。

例子
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_authorization;
proxy_ssl_server_name on;

给 HTTPS upstream 发 SNI。后端一个 IP 上托管多张证书、按 SNI 选证书时必须开。

常见坑: 默认关闭,不开的话共享主机上的 HTTPS upstream 可能返回错证书或拒绝握手。SNI 要和 proxy_pass 主机不同就设 proxy_ssl_name。

例子
location / {
    proxy_pass https://backend.example.com;
    proxy_ssl_server_name on;
    proxy_ssl_protocols TLSv1.2 TLSv1.3;
}
proxy_pass_request_headers off;

不把客户端请求头转发给 upstream。主要用在鉴权子请求上让它保持精简。

例子
location = /auth {
    internal;
    proxy_pass http://auth/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}
proxy_intercept_errors on;

让 nginx 用自己的 error_page 接管 upstream 的错误响应(≥300),后端 500 也能显示你定制的错误页。

例子
location / {
    proxy_pass http://backend;
    proxy_intercept_errors on;
    error_page 500 502 503 504 /50x.html;
}
静态服务 (10)
root vs alias

root:URI 直接拼到 root 后面。alias:把匹配的 location 部分整段替换成 alias。两者机制不同。

常见坑: 把 /static/ 映射到 /var/www/files/ 要用 alias;用 root 会去找 /var/www/files/static/... 然后 404。

例子
# root style — keeps URI prefix
location /static/ {
    root /var/www;  # serves /var/www/static/...
}
# alias style — strips URI prefix
location /static/ {
    alias /var/www/files/;  # serves /var/www/files/...
}
try_files $uri $uri/ /index.html;

依次尝试每个路径,第一个存在的直接服务。SPA 兜底的经典写法。

常见坑: 最后一个参数要么是 URI(内部重定向),要么是 =code(返回状态码)。直接写裸文件名会被当文件直接服务,多半不是你想要的。

例子
# SPA fallback
location / {
    try_files $uri $uri/ /index.html;
}
# 404 if nothing matches
location / {
    try_files $uri =404;
}
# PHP front controller
location / {
    try_files $uri $uri/ /index.php?$query_string;
}
autoindex on;

当没有匹配的 index 文件时列出目录。默认 off,生产环境就该 off。

常见坑: 全局开会泄露文件结构。要么只在某个 location 里开,要么干脆别开。

例子
location /downloads/ {
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
}
expires 30d;

给响应设 Expires 和 Cache-Control 头。带 hash 的静态资源标准做法。

例子
location ~* \.(js|css|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
expires -1;  # explicitly disable caching
error_page 404 /404.html;

为某个状态码提供自定义页面。用 =200 同时改返回状态码,或用命名 location 把处理交给代理。

常见坑: 自定义 404 的 location 一般要标 internal; 防止被直接请求。写 error_page 404 = /404.html;(带 =)能让它返回 404 而不是 200。

例子
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html { root /usr/share/nginx/html; internal; }
add_header X-Content-Type-Options nosniff;

禁止浏览器把响应嗅探成与声明 Content-Type 不同的类型。一行防住部分 XSS 路径。

常见坑: location 里只要再写一条 add_header,前面不带 always 的就被丢掉。用 always,并记住子块自己加了 header 就不会继承父块的。

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

发送 CSP,控制浏览器能加载哪些来源的脚本、样式、媒体。头部层面最强的 XSS 缓解。

常见坑: 先用 Content-Security-Policy-Report-Only 上线并观察违规报告,太严的 CSP 会静默搞坏自家站点。

例子
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'" always;
gzip_static on;

直接发原文件旁边预压好的 file.gz,而不是每次请求现压。静态资源每请求零 CPU。

常见坑: .gz 文件要在构建时自己生成(比如 gzip -k -9 dist/**/*.js)。nginx 不会创建它们,只在已存在时直接发。

例子
location ~* \.(js|css|svg)$ {
    gzip_static on;
    expires 1y;
    add_header Cache-Control "public, immutable";
}
open_file_cache max=10000 inactive=60s;

缓存打开的文件描述符、大小、是否存在的判断。反复服务大量小静态文件的站点提速明显。

例子
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
add_header Accept-Ranges bytes;

声明支持字节范围,客户端就能断点续传、拖动视频进度。nginx 对静态文件会自动支持 range。

常见坑: 响应被现场 gzip 时 range 会失效。用预压的 gzip_static 文件能让大文件下载保留 range。

例子
location /video/ {
    root /var/media;
    add_header Accept-Ranges bytes;
    mp4;
}
重定向 (7)
return 301 https://$host$request_uri;

永久重定向到 HTTPS。最简洁高效的写法,不走 rewrite 引擎,不用正则。

常见坑: 生产重定向用 301(永久),浏览器和 CDN 才会缓存。真临时跳转才用 302。

例子
# Force HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}
# Redirect old path
location = /old { return 301 /new; }
rewrite ^/old/(.*)$ /new/$1 permanent;

重写 URI。permanent = 301,redirect = 302,last = 重新走 location,break = 停止 rewrite。

常见坑: 简单跳转用 return 301,比 rewrite 更快更清楚。只有需要正则捕获时才上 rewrite。

例子
rewrite ^/old/(.*)$ /new/$1 permanent;
rewrite ^/blog/(.*)\.html$ /posts/$1 last;
if ($scheme = "http")

条件块。官方说法是 "if is evil",只能在 location 里用,作用域规则反直觉。

常见坑: if 块里只有 return 和 rewrite ... last 安全。其他指令可能静默出错。能拆 server 块就别用 if。

例子
# Force HTTPS via if (only when a separate server block is awkward)
if ($scheme = "http") {
    return 301 https://$host$request_uri;
}
return 410;

告诉爬虫这个 URL 永久没了(比 404 更明确,已删页面应该用 410)。

例子
location = /removed-feature { return 410; }
return 302 (temporary redirect)

临时重定向。浏览器和 CDN 不会缓存,跳转随时能撤回。要同时保留请求方法和 body 用 307。

常见坑: 很多客户端遇到 POST 的 302 会悄悄改成 GET。要重定向 POST 并保持 POST,用 307。

例子
location = /beta { return 302 /coming-soon; }
return 307 https://$host$request_uri;
www to non-www redirect

把主机名规范化,让搜索引擎只收录一个 URL。给 www 名单独写一个 server 块,比用 if 干净。

常见坑: 选定一个规范主机,到处都用它(sitemap、canonical 标签、跳转)。两边互相跳会死循环。

例子
# www → root
server {
    listen 80;
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}
map for bulk redirects

用一个 map 驱动成百上千条新旧跳转。比一大堆 if 或 location 更快也更好读。

常见坑: map 块写在 http {} 里,不能放进 server 或 location。default 那行是没命中时的兜底值。

例子
# in http { }
map $request_uri $redirect_to {
    default            "";
    /old-page          /new-page;
    /legacy/docs       /docs;
}

# in server { }
if ($redirect_to) {
    return 301 $redirect_to;
}
HTTPS / SSL (12)
ssl_certificate / ssl_certificate_key

完整证书链文件和私钥的路径。任何 TLS 监听都必填。

常见坑: 一定用 fullchain(叶证书 + 中间证书),别只写叶证书,不然手机和老浏览器验证会失败。

例子
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

允许的 TLS 版本。TLS 1.0 / 1.1 早已弃用,主流浏览器全部不再支持,别开。

例子
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers <modern-cipher-list>

允许的加密套件。从 ssl-config.mozilla.org 抄最新 Modern 或 Intermediate 列表,别自己造。

例子
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
Let's Encrypt with certbot

免费证书带自动续期。certbot --nginx 会直接改你的 nginx 配置。续期靠 systemd timer。

常见坑: 续期要求公网能访问 80 端口。在防火墙后面用 DNS-01(--manual --preferred-challenges dns)。

例子
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
sudo systemctl list-timers | grep certbot
add_header Strict-Transport-Security ...

HSTS,告诉浏览器在 max-age 期间这个域名只走 HTTPS。

常见坑: includeSubDomains + preload 的 HSTS 一旦被浏览器收到,期间没法降级。上线先用短 max-age(比如 1 天)观察。

例子
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
ssl_stapling on;

OCSP stapling,nginx 自己抓 OCSP 响应附在握手里,省掉客户端一次回查。

例子
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
ssl_session_cache shared:SSL:10m;

worker 间共享的 TLS session 缓存。10MB 大约 40000 个 session。高并发站点提速明显。

例子
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_dhparam /etc/nginx/dhparam.pem;

为 DHE 套件提供自定义 Diffie-Hellman 参数。生成 2048 位以上的文件,别用 nginx 内置的弱默认值。

常见坑: 生成 4096 位 dhparam 要几分钟,握手开销也涨但收益不大,2048 位是常见且兼容好的选择。纯 TLS 1.3 / ECDHE 配置根本用不到它。

例子
openssl dhparam -out /etc/nginx/dhparam.pem 2048
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_early_data on;

启用 TLS 1.3 的 0-RTT,回访客户端能在第一个数据包里就带上首个请求。重连省一个往返。

常见坑: 0-RTT 数据可被攻击者重放。只对幂等请求放行,并检查 $ssl_early_data,在应用层拒掉 early-data 的 POST。

例子
ssl_early_data on;
proxy_set_header Early-Data $ssl_early_data;
ssl_trusted_certificate ...

用于校验 OCSP responder、以及配合 ssl_verify_client 校验客户端证书的信任库。某些证书链做 stapling 校验时需要它。

例子
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_verify_client on; (mTLS)

要求客户端证书(双向 TLS)。客户端不出示由 ssl_client_certificate 签发的证书,nginx 直接拒绝握手。

常见坑: 有公开路径就用 optional 而不是 on,然后自己在受保护 location 上用 $ssl_client_verify = SUCCESS 判断放行。

例子
ssl_client_certificate /etc/nginx/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
if ($ssl_client_verify != SUCCESS) { return 403; }
http2 on;

nginx ≥ 1.25.1 启用 HTTP/2 的写法,和 listen 行分开。取代被弃用的 listen ... http2 flag。

例子
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
gzip / brotli / 缓存 (9)
gzip on;

为响应启用即时 gzip 压缩。文本内容标配。

常见坑: 已经压缩过的类型(jpg、png、mp4、zip)别再 gzip。用 gzip_types 显式列出文本 MIME。

例子
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
brotli on;

Brotli 压缩(ngx_brotli 模块)。文本内容比 gzip 小 15-25%。需要编译进对应模块。

常见坑: brotli 不在 nginx 默认编译里,先 nginx -V | grep brotli 确认。Ubuntu 上装 libnginx-mod-brotli 或自己编译。

例子
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
proxy_cache_path /var/cache/nginx ...

定义一块磁盘缓存区给 upstream 响应用。然后在 location 里 proxy_cache <zone> 启用。

例子
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=1g inactive=60m use_temp_path=off;

location / {
    proxy_cache mycache;
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    add_header X-Cache-Status $upstream_cache_status;
    proxy_pass http://backend;
}
add_header Cache-Control ...

给浏览器的缓存指令。带 hash 的静态资源用 public, immutable;HTML 用 no-store;安全的过期重验用 must-revalidate。

例子
add_header Cache-Control "public, max-age=31536000, immutable" always;
add_header Cache-Control "no-store" always;
proxy_cache_key $scheme$host$uri$is_args$args;

定义缓存认为两个请求「相同」的依据。默认 key 不含 scheme,加上它避免把 http 的内容发给 https。

常见坑: key 里漏掉按用户区分的 cookie 或 token,就会把某个用户的私有页面发给所有人。响应按用户变化就别缓存。

例子
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_lock on;

同一个 key 同时大量未命中时,只放一个请求去打 upstream,其余等它回来。挡住缓存击穿。

例子
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale updating;
proxy_cache_min_uses 3;

一个响应被请求够这么多次后才开始缓存。避免一次性 URL 把缓存占满。

例子
proxy_cache_min_uses 3;
proxy_cache_valid 200 10m;
gzip_proxied any;

控制 nginx 是否压缩来自 upstream 的响应。默认 off 表示代理响应从不压缩,通常你想要 any。

常见坑: gzip 对静态文件生效却对代理页面不生效,几乎都是这个原因,默认 gzip_proxied off 把它们跳过了。

例子
gzip on;
gzip_proxied any;
gzip_vary on;
add_header X-Cache-Status $upstream_cache_status;

把每个响应是 HIT / MISS / BYPASS / EXPIRED / STALE 暴露出来。验证缓存配置是否真生效最快的办法。

例子
add_header X-Cache-Status $upstream_cache_status always;
# then:
curl -sI https://example.com/ | grep -i x-cache-status
限流 (7)
limit_req_zone $binary_remote_addr ...

定义一个按 IP 的请求速率区。然后在 location 里 limit_req zone=<name> burst=<n> 强制执行。

常见坑: 一定用 $binary_remote_addr,不是 $remote_addr,4 字节 vs 约 15 字节,同样内存能装 4 倍的客户端。

例子
# Define in http { }
limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=5r/m;

# Apply in location
location /login {
    limit_req zone=loginlimit burst=10 nodelay;
    proxy_pass http://backend;
}
limit_conn_zone $binary_remote_addr ...

定义一个按 IP 的并发连接区。用 limit_conn <name> <n> 强制执行。

例子
limit_conn_zone $binary_remote_addr zone=perip:10m;

server {
    limit_conn perip 20;
}
limit_req_status 429;

触发限流时返回的 HTTP 状态码。默认 503,正确语义应该是 429。

例子
limit_req_status 429;
limit_conn_status 429;
burst= and nodelay explained

burst 允许短时尖峰排队超过速率;nodelay 让排队的请求立刻放行而不是匀速放出。两者一起:平均平滑、尖峰即时。

常见坑: 不加 nodelay 时,rate=10r/s burst=20 会按每 100ms 一个放出排队请求,正常尖峰会觉得卡。加了 nodelay 一次性放行,但总速率上限仍然守住。

例子
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend;
}
limit_req_log_level warn;

请求被限流延迟或拒绝时的日志级别。降到 notice 能避免限流噪音淹没 error.log。

例子
limit_req_log_level notice;
limit_conn_log_level notice;
limit_rate 512k;

限制单连接的响应带宽(字节/秒)。用 limit_rate_after 先全速发开头一段,再对大文件下载的剩余部分限速。

例子
location /downloads/ {
    limit_rate_after 10m;
    limit_rate 512k;
}
geo + map whitelist for limits

让可信 IP 免限流:geo 块设个标记,map 对它们把限流 key 置空,请求就不计入。

例子
geo $limit_exempt {
    default        0;
    10.0.0.0/8     1;
    192.168.0.0/16 1;
}
map $limit_exempt $limit_key {
    0 $binary_remote_addr;
    1 "";
}
limit_req_zone $limit_key zone=api:10m rate=10r/s;
日志 (8)
access_log /var/log/nginx/access.log main;

每个请求写到哪、用什么格式。main 是默认格式名。/healthz 这种热点端点设为 off。

例子
access_log /var/log/nginx/access.log main;
access_log off;
access_log /var/log/nginx/api.log combined buffer=64k flush=5s;
error_log /var/log/nginx/error.log warn;

nginx 内部错误写到哪、什么级别。级别:debug、info、notice、warn、error、crit、alert、emerg。

常见坑: 设 debug 日志会爆掉。只在主动 debug 时短暂打开,并写到 /tmp/debug.log,不要污染主日志。

例子
error_log /var/log/nginx/error.log warn;
error_log /var/log/nginx/error.log error;
log_format main ...

定义命名日志格式。很多有用的变量:$request_time、$upstream_response_time、$body_bytes_sent。

例子
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 urt=$upstream_response_time';
log_format json escape=json '{"time":"$time_iso8601","remote":"$remote_addr","req":"$request","status":$status,"rt":$request_time,"urt":"$upstream_response_time"}';
tail -F /var/log/nginx/access.log

实时跟看 access log。-F(大写)会跟着 logrotate 切换,这才是日常想要的。

例子
sudo tail -F /var/log/nginx/access.log
sudo tail -F /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
access_log syslog:server=...

把 access log 直接送到 syslog 服务器(本地或远程)而不是文件。日志收集器无需 tail 文件就能采集 nginx。

例子
access_log syslog:server=127.0.0.1:514,tag=nginx,severity=info main;
error_log syslog:server=logs.internal:514 warn;
map to skip health-check logging

对 $request_uri 或 $http_user_agent 做 map 设个标记,再用 access_log ... if=$flag,让探针和监控不污染日志。

例子
map $request_uri $loggable {
    default        1;
    /healthz       0;
    /metrics       0;
}

access_log /var/log/nginx/access.log main if=$loggable;
open_log_file_cache max=1000;

当你把日志拆成很多文件时(比如按 vhost)缓存打开的日志文件描述符。每请求省一次 open/close。

例子
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
GoAccess on the access log

把 access log 变成实时 HTML 仪表盘。GoAccess 解析 nginx 的 combined / 自定义格式,展示热门 URL、状态码、带宽。

常见坑: 用了自定义 log_format 就要给 GoAccess 传匹配的 --log-format,否则它解析不出东西。combined 格式开箱即用。

例子
goaccess /var/log/nginx/access.log -o report.html --log-format=COMBINED --real-time-html
zcat /var/log/nginx/access.log.*.gz | goaccess --log-format=COMBINED -o report.html
常见错误 (15)
502 Bad Gateway

nginx 连上了 upstream 但拿到无效/空/已关闭的响应。几乎都是后端挂了、崩了、或端口写错。

常见坑: 先 curl http://127.0.0.1:3000 看后端活没活。再看 nginx error.log,常见就是 connect() failed 或 upstream prematurely closed connection。

例子
curl -v http://127.0.0.1:3000
sudo tail -f /var/log/nginx/error.log
sudo journalctl -u my-backend -n 200
504 Gateway Timeout

upstream 活着但 proxy_read_timeout 内没回应。要么调大超时,要么修慢请求。

常见坑: WebSocket 和长轮询端点经常踩。只在那几个 location 单独设 proxy_read_timeout 3600s,别全局拉到那么大。

例子
proxy_read_timeout 3600s;
proxy_send_timeout 600s;
SSL handshake failed

TLS 握手失败。最常见原因:证书和私钥不匹配、缺中间证书、协议或加密套件不支持、SNI 对不上。

常见坑: 永远用 openssl s_client -connect host:443 -servername host 测(-servername 才会发 SNI)。浏览器对 TLS 失败的缓存非常顽固。

例子
openssl s_client -connect example.com:443 -servername example.com
openssl x509 -in fullchain.pem -noout -dates -subject -issuer
sudo nginx -t
permission denied (static files)

worker 用户读不到文件。检查文件归属是不是 user 指令里那个用户,以及每一级父目录都要有 x 权限。

常见坑: 每一级父目录都要 nginx 能 x,不是只给文件 r。chmod -R 755 /var/www/site 一把搞定。

例子
ls -ld /var/www /var/www/site /var/www/site/index.html
sudo chown -R www-data:www-data /var/www/site
sudo chmod -R 755 /var/www/site
address already in use (port 80/443)

端口被别的进程占了。可能是 Apache、上一个 nginx、忘了停的 docker 容器,找出来停掉。

例子
sudo ss -tlnp | grep -E ":80|:443"
sudo lsof -i :80 -i :443
sudo systemctl stop apache2
conflicting server_name warning

两个 server 块在同一个 listen 上声明了相同 server_name。nginx 静默挑一个用,另一个站点就消失了。

常见坑: 每次部署后跑一遍 nginx -T | grep -E "(server_name|listen)",提前发现重复,比线上踩坑好。

例子
sudo nginx -T 2>&1 | grep -E "server_name|listen" | sort | uniq -d
worker connections are not enough

撞上 worker_connections × worker_processes 上限。worker_connections + worker_rlimit_nofile + systemd 的 LimitNOFILE 一起抬。

例子
worker_rlimit_nofile 65535;
events { worker_connections 16384; }
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65535
client intended to send too large body

上传超过 client_max_body_size 返回 413。在 http、server、location 任意一层设大。

例子
# In http or server
client_max_body_size 100m;
# Only the upload endpoint
location /upload {
    client_max_body_size 1g;
    proxy_pass http://backend;
}
upstream sent too big header

upstream 的响应头撑爆了 nginx 的 proxy 缓冲区,返回 502。大 Set-Cookie 或鉴权头常触发。

常见坑: 同时调大 proxy_buffer_size(存响应头的那块)和 proxy_buffers。响应头单独受 proxy_buffer_size 限制,不是整个 proxy_buffers 池。

例子
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
rewrite or internal redirection cycle

try_files 或 rewrite 一直指回一个又会重新进同一个 location 的 URI,nginx 判定死循环直接 500。

常见坑: 经典原因:在 location ~ \.php$ 里写 try_files $uri $uri/ /index.php,兜底又匹配回 php location 死循环。把兜底挪到 location / 里。

例子
sudo tail -f /var/log/nginx/error.log | grep "rewrite or internal redirection cycle"
no live upstreams

池里每台 upstream 都被标记宕机(健康检查失败太多次),nginx 返回 502,error.log 里写 no live upstreams。

常见坑: max_fails 设太狠加上后端短暂抖动,可能一次性把整池打趴。调好 max_fails / fail_timeout,并留一台 backup。

例子
upstream app {
    server a:8080 max_fails=3 fail_timeout=15s;
    server b:8080 max_fails=3 fail_timeout=15s;
    server c:8080 backup;
}
duplicate default_server

两个 server 块在同一个监听地址上都声明了 default_server,nginx -t 直接拒绝加载。

常见坑: 搜所有被 include 的文件:grep -rn default_server /etc/nginx/。多半是残留的 00-default.conf 在捣乱。

例子
sudo grep -rn "default_server" /etc/nginx/
sudo nginx -t
redirect loop behind a proxy/CDN

HTTP→HTTPS 跳转一直循环,因为 CDN 用 HTTP 回源,即便真实是 HTTPS 请求 $scheme 也总是 http。

常见坑: 改用 $http_x_forwarded_proto 判断而不是 $scheme,或配 proxy_protocol / realip 让 nginx 知道原始 scheme。卸载型代理后面别信 $scheme。

例子
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}
too many open files

nginx 报 socket() failed (24: Too many open files),因为文件描述符上限撑不住当前连接量。

常见坑: 三处一起抬:worker_rlimit_nofile、systemd 的 LimitNOFILE、系统的 nofile ulimit。漏掉任意一个,真正上限就是三者里最小的那个。

例子
worker_rlimit_nofile 100000;
# /etc/systemd/system/nginx.service.d/limits.conf
[Service]
LimitNOFILE=100000
cat /proc/$(pgrep -o nginx)/limits | grep "open files"
add_header lost in a child location

server 层用 add_header 设的头,在任何自己又写了 add_header 的 location 里会消失。nginx 是替换不是合并。

常见坑: 在每个动到 header 的 location 里把需要的头重新声明一遍,或用 headers-more 模块的 more_set_headers 集中管理,它会继承。

例子
# server-level header is dropped here:
location /api/ {
    add_header X-Api "1" always;          # this replaces ALL inherited add_headers
    add_header X-Frame-Options SAMEORIGIN always;  # must repeat
}
完整模板 (11)
Template: static site

最简静态站 server 块。服务 /var/www/site,开 gzip,带 hash 的资源长缓存,SPA 友好兜底。

例子
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/site;
    index index.html;

    gzip on;
    gzip_types text/plain text/css application/json application/javascript image/svg+xml;

    location ~* \.(js|css|woff2|png|jpg|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}
Template: reverse proxy to Node

把 / 反代到 127.0.0.1:3000 的 Node 应用,带标准转发头和 WebSocket 支持。

例子
upstream node_app {
    server 127.0.0.1:3000;
    keepalive 32;
}

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

    location / {
        proxy_pass http://node_app;
        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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_buffering on;
    }
}
Template: HTTPS with Let's Encrypt

完整 HTTPS server,含 HTTP→HTTPS 跳转、现代 TLS、HSTS、OCSP stapling。可直接套用。

例子
# HTTP → HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.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_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    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" always;

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

    location / {
        try_files $uri $uri/ /index.html;
    }
}
Template: load balancer

三台后端的负载均衡(默认轮询或最少连接),带 fail_timeout 的健康判断。

例子
upstream backend {
    least_conn;
    server app1.internal:8080 max_fails=3 fail_timeout=30s;
    server app2.internal:8080 max_fails=3 fail_timeout=30s;
    server app3.internal:8080 max_fails=3 fail_timeout=30s backup;
    keepalive 64;
}

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

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_next_upstream error timeout http_502 http_503 http_504;
    }
}
Template: PHP-FPM

把 .php 文件交给 PHP-FPM 的 unix socket。WordPress / Laravel 标准前置控制器写法。

例子
server {
    listen 80;
    server_name example.com;
    root /var/www/site/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht { deny all; }
}
Template: /healthz endpoint

不打日志的精确匹配健康检查端点。LB、K8s 探针、Uptime 监控都喜欢。

例子
server {
    listen 80 default_server;

    location = /healthz {
        access_log off;
        return 200 "ok\n";
        add_header Content-Type text/plain;
    }
}
Template: maintenance / 503 page

用 HTTP 503 加 Retry-After 头返回一个友好的「马上回来」页面。靠一个文件开关切换维护模式。

例子
server {
    listen 80;
    server_name example.com;
    root /var/www/site;

    # If the flag file exists, everything returns the maintenance page.
    if (-f $document_root/maintenance.on) {
        return 503;
    }

    error_page 503 @maintenance;
    location @maintenance {
        root /var/www/site;
        rewrite ^(.*)$ /maintenance.html break;
        add_header Retry-After 3600 always;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}
Template: CORS preflight handling

在 nginx 层应答 OPTIONS 预检并给 API 响应加 CORS 头,让另一个源的前端能调这个 API。

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

    location /api/ {
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "https://app.example.com" always;
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
            add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
            add_header Access-Control-Max-Age 86400 always;
            add_header Content-Length 0;
            return 204;
        }

        add_header Access-Control-Allow-Origin "https://app.example.com" always;
        proxy_pass http://backend;
    }
}
Template: gRPC proxy

用 grpc_pass 在 HTTP/2 上反代 gRPC。nginx 为客户端终止 TLS,再转发到明文或 TLS 的 gRPC 后端。

常见坑: gRPC 需要端到端 HTTP/2。用 grpc_pass(不是 proxy_pass),TLS 后端用 grpc_pass grpcs://。监听侧必须 http2 on;。

例子
server {
    listen 443 ssl;
    http2 on;
    server_name grpc.example.com;

    ssl_certificate     /etc/letsencrypt/live/grpc.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grpc.example.com/privkey.pem;

    location / {
        grpc_pass grpc://127.0.0.1:50051;
        grpc_set_header Host $host;
    }
}
Template: micro-cache for dynamic pages

把动态 HTML 只缓存 1 秒。流量尖峰时把上千个相同请求收敛成一次 upstream 命中,并带 stale-while-revalidate。

常见坑: 绝不要微缓存按登录用户变化的响应。对鉴权 cookie 加 proxy_cache_bypass / proxy_no_cache,让已登录请求跳过共享缓存。

例子
proxy_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=micro:10m max_size=256m inactive=60s use_temp_path=off;

map $http_cookie $no_cache {
    default       0;
    ~session_id   1;
}

server {
    location / {
        proxy_cache micro;
        proxy_cache_valid 200 1s;
        proxy_cache_use_stale updating error timeout;
        proxy_cache_lock on;
        proxy_cache_bypass $no_cache;
        proxy_no_cache $no_cache;
        add_header X-Cache-Status $upstream_cache_status always;
        proxy_pass http://backend;
    }
}
Template: subdomain wildcard vhost

用捕获的 server_name 加变量 root,自动把每个子域名映射到各自的目录。加一个文件夹就多一个站。

常见坑: 通配 server 块要配通配 DNS 记录(*.example.com),HTTPS 还要通配证书。不信任任意子域名时要校验捕获到的 $sub。

例子
server {
    listen 80;
    server_name ~^(?<sub>[a-z0-9-]+)\.example\.com$;
    root /var/www/tenants/$sub;
    index index.html;

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

这个工具能做什么

可搜索的 Nginx 速查,覆盖运维真正往生产里贴的 80+ 条指令、CLI 参数和完整模板,不是凑数的 hello world 列表。十三大分类:CLI 命令(nginx -t / -s reload / -s quit / -s reopen / -V / -T), 核心指令(worker_processes / events / http / include / sendfile / keepalive_timeout / client_max_body_size / worker_rlimit_nofile), server 块(listen 80 / 443 ssl http2、server_name 通配符 + 正则、 root、index、return 444 兜未知 Host),location 匹配带完整优先级 顺序(= 精确、^~ 优先前缀、~ 和 ~* 正则、普通前缀),反向代理 (proxy_pass 末尾斜杠的坑、upstream 池、proxy_set_header Host / X-Real-IP / X-Forwarded-For / X-Forwarded-Proto、WebSocket Upgrade + Connection 升级、proxy_http_version 1.1、 proxy_read_timeout / proxy_connect_timeout、SSE 场景关 proxy_buffering),静态服务(root vs alias 的坑、SPA 兜底的 try_files、autoindex、expires + immutable),重定向(return 301 / 302 / 410、rewrite 的 last vs break vs permanent、if is evil), HTTPS / SSL(ssl_certificate 完整链、ssl_protocols TLSv1.2/1.3、 Let&#39;s Encrypt + certbot --nginx、HSTS preload、OCSP stapling、 session cache),缓存压缩(gzip 配 gzip_types、ngx_brotli、 proxy_cache_path 带 stale-while-revalidate、浏览器 Cache-Control),限流(limit_req_zone 用 $binary_remote_addr、 limit_conn_zone、limit_req_status 429),日志(access_log buffer + flush、error_log 严重级别、JSON log_format、tail -F), 7 种真正会毁掉一天的错误(502 Bad Gateway、504 Gateway Timeout、 SSL handshake failed、permission denied、address already in use、 conflicting server_name、worker connections are not enough), 以及 6 份直接抄就能用的完整模板(静态站、反代 Node、HTTPS + Let&#39;s Encrypt、负载均衡含 backup + fail_timeout、PHP-FPM 前置 控制器、便宜的 /healthz 端点)。每条都有指令本体、中英文说明、 对应的"常见坑"(proxy_pass 末尾斜杠、默认 HTTP/1.0 到 upstream 会让 keep-alive 失效、$binary_remote_addr 同等内存能装 4 倍客户 端、wildcard server_name 不能首尾都是 *、reload 时 SIGTERM vs SIGKILL),1 到 4 条可直接复制的真实例子。搜索框跨指令 / 说明 / 坑 / 例子四个字段一起过滤,分类胶囊缩范围,每条一键复制。完全 在浏览器里跑,不上传 nginx 配置,不连任何后端,不追踪。配合 Docker 速查 / kubectl 速查 / Git 速查 / Crontab 帮手一起用。

工具细节

输入
文件 + 文本
页面会根据工具类型展示文本框、数值控件、文件选择或结构化输入。
输出
即时结果 + 复制 + 预览
结果区优先给出可操作结果,支持项会显示复制、下载或可视化预览。
隐私
浏览器本地处理
主工具逻辑未发现外部 API 调用,输入通常留在当前标签页内处理。
保存 / 分享
免账号使用
打开页面即可使用;刷新后是否保留结果取决于具体工具。
性能预算
首屏 JS ≤ 30 KB
没有声明 WASM 依赖,适合快速打开和移动端使用。
适用场景
开发运维 · 程序员
分类和职业标签用于推荐相关工具、组织内链,并帮助用户快速判断是否适合当前任务。

怎么用

  1. 1. 输入

    把内容粘贴或拖入工具面板。

  2. 2. 处理

    点击按钮,在浏览器内本地处理,文件不上传。

  3. 3. 复制 / 下载

    一键复制结果或下载到本地。

Nginx 速查表 适合怎么用

适合穿插在写代码、查问题、做 Review、上线前的小任务里。

适合开发场景

  • 格式化、校验、压缩或检查和代码相关的文本。
  • 把片段整理好再放进文档、工单、提交或交接材料。
  • 不切换工具,快速检查一个小 payload。

开发检查项

  • 压缩、混淆这类不可逆处理,先对副本操作。
  • 除非确认工具本地处理,不要粘贴密钥和敏感片段。
  • 转换后的代码上线前,仍要跑自己的测试或 lint。

下一步可以接着做

这些入口会把当前任务接到更完整的工具链里。

  1. 1 正则速查表 交互式正则速查表,JS / Python / PCRE 各方言一站查询。 打开
  2. 2 Docker 命令速查 Docker 命令速查,80+ 条命令带真实例子和常见坑,含 Compose 章节。 打开
  3. 3 kubectl 命令速查 kubectl 命令速查,100+ 条 K8s 命令,真实例子和常见坑,含 YAML 片段。 打开

真实使用场景

  • 新 VPS 上 10 分钟把 Node 应用挂到 nginx 后面

    你刚开了台 5 美元的小机器,应用监听 127.0.0.1:3000。抄"反代 Node" 那份模板,把 server_name 和 upstream 端口换掉,补上四行 proxy_set_header 让应用拿到真实客户端 IP 而不是 127.0.0.1,跑一遍 nginx -t,再 nginx -s reload。模板里 WebSocket Upgrade 块已经写好, socket.io 连接第一次就能通,不用回头查为什么一直 Pending。

  • 凌晨两点排 502 不靠猜

    告警响了,站点 502。搜「502」直接给你三步:curl 直打 upstream、tail error.log 看是 connect() failed (111) 还是 prematurely closed、RHEL 上那行 SELinux setsebool。你不再乱调超时(那只修 504),两分钟定位到 挂掉的后端,而不是折腾二十分钟还在原地。

  • 把 /api 前缀剥掉再进不认这个前缀的后端

    你的 Go 服务把路由挂在 /users、/orders,前端却调 /api/users。搜 「proxy_pass」末尾斜杠那条说清楚:proxy_pass http://backend/ 会剥掉 /api/ 前缀,不带斜杠则保留。你补上那一个斜杠,用 curl 配后端 access_log 确认一下,直接上线,省掉一场 404 追查。

  • 把静态 SPA 部署上线且深链刷新不 404

    /var/www/app 下的 React 构建,刷新 /dashboard 就 404。搜「try_files」 那条 SPA 兜底给你 try_files $uri $uri/ /index.html;,任何未知路径都回 应用外壳交给前端路由接管。再给带 hash 的 JS 包加 expires + immutable, 老访客就不会每翻一页都重新下 800KB。

常见踩坑

  • proxy_pass 末尾斜杠忘了加:http://backend/ 会剥掉 location 前缀,http://backend 保留,靠猜是哪种就是大多数 404 的来源。

  • ssl_certificate 只填叶证书:单独指向 cert.pem 会让安卓和老客户端握手失败,永远用 fullchain.pem 完整链。

  • 在 location 里随手写 if:「if is evil」作用域规则反直觉,能用 try_files、return 或单独 server 块就别用 if ($request_uri ...)。

隐私说明

这份速查就是一个静态页。搜索完全在你浏览器里对内存中的指令数组做过滤, 你的 nginx 配置不会被打到任何服务器、不会上传、也不会进 URL。一边输入 一边开 DevTools Network 看,零请求。气隙跳板机和运维堡垒机里都能放心用。

常见问题

类似工具组合

做你这行的人, 还会一起用这些。

Made by Toolora · 100% client-side · Updated 2026-06-13