直接回答:Nginx 配置的难点不在指令本身,而在两件事——location 的匹配顺序和proxy_pass 末尾有没有斜杠。把这两点搞清楚,反向代理、动静分离、缓存、限流这些常见场景基本都是照抄几行的事。
配置文件是怎么组织的
Nginx 的配置是分层的,从上到下依次是:
| 层级 | 作用 |
|---|---|
main |
全局生效,比如运行用户、工作进程数、日志路径 |
events |
网络连接模型,比如每个进程的最大连接数、是否开启多路复用 |
http |
所有站点的公共部分:MIME 类型、日志格式、gzip、ssl 全局设置 |
server |
一个站点,由 listen 和 server_name 决定服务哪个域名和端口 |
location |
站点里的路径匹配规则 |
主配置通常在 /etc/nginx/nginx.conf,各站点拆到 conf.d/ 或 sites-enabled/ 里再 include 进来。改配置优先改站点文件,别去动主配置——升级时主配置容易被覆盖。
还有一条容易忽略的规则:多数指令是"就近继承"的。http 里写了 client_max_body_size 20m,server 没写就用 20m;server 里再写一个,就在这个站点内覆盖掉。
location 的匹配顺序:配错了最难查
Nginx 收到请求后,按这个顺序决定用哪个 location:
=精确匹配,命中就结束;^~前缀匹配,命中最长的那个之后不再看正则;~和~*正则匹配,按配置文件里出现的顺序,第一个命中就用它;- 正则都没命中,回到刚才记住的最长前缀匹配。
所以下面这两条是常见误区:
- 以为
location /api会盖住location /,其实只要后面有一条能命中的正则(比如.php$),正则就会把前缀抢走; - 正则的顺序有意义,
\.png$写在\.(png|jpg)$前面,后者永远轮不到。
不确定的时候,在 location 里加一行 add_header X-Matched-Location "$uri"; 之类的标记头,用 curl -I 看实际命中了哪一条,比盯着配置猜快得多。
反向代理:最常用的那几行
1 | location /api/ { |
几个关键点:
proxy_pass末尾有没有斜杠决定路径怎么拼。 写http://host/会把location匹配到的前缀替换掉(/api/user→/user);写http://host则整个原始路径原样转发(/api/user→/api/user)。后端 404 或路径多了一段,基本都在这儿。Host透传不能少。 不回传 Host,后端拿到的就是 upstream 里的地址,虚拟主机、绝对地址跳转、签名校验都可能出问题。- 真实 IP 要一层层加。
X-Forwarded-For是可以伪造的,只有直接相连的那一跳可信,业务侧取真实 IP 时要从右往左数过可信代理的层数。 - 长连接和 SSE 要清空
Connection头。 HTTP/1.1 默认 keep-alive,写死Connection: close会让 WebSocket 和 SSE 断掉。
负载均衡
1 | upstream backend { |
- 默认是轮询;
weight加权,ip_hash按客户端 IP 固定后端,least_conn给连接数最少的。 backup只在前面的机器都挂了才启用,适合做冷备。- 开源版没有主动健康检查,
max_fails/fail_timeout是被动探测:连续失败几次就先摘掉,过一段时间再放回来。要主动探测得用商业版或者 OpenResty 那套。 - 配置了
keepalive,server后面必须跟proxy_http_version 1.1和proxy_set_header Connection "",否则长连接不生效。
动静分离与静态资源缓存
静态资源交给 Nginx 直接读文件,动态请求转给应用:
1 | location ~* \.(?:js|css|png|jpg|jpeg|gif|svg|webp|woff2?)$ { |
缓存策略按"文件名会不会变"来分:
- 带内容哈希的资源(
app.9f3c1a.js)可以设一年加immutable; - 不带哈希的图片、字体给几天到 30 天;
index.html和接口响应不要长缓存,否则发了新版用户还是旧的。
gzip:压什么、不压什么
1 | gzip on; |
图片、视频本身就是压缩格式,再 gzip 是白费 CPU。gzip_comp_level 超过 6 之后体积收益很小,但 CPU 消耗明显上升。静态文件还可以用 gzip_static 直接读预压缩好的 .gz,但那需要单独编译模块。
HTTPS
1 | server { |
fullchain.pem 要包含中间证书,只有服务器证书的话,部分客户端会因为链不完整而报错。加了 HSTS 之后浏览器会强制走 HTTPS,确认整站都能用 HTTPS 再加,否则回不去。
访问控制与限流
1 | # 只允许内网访问后台 |
limit_req_zone 只能写在 http 层,limit_req 写在 server 或 location 层,写错位置会直接报语法错误。nodelay 决定突发请求是立刻处理还是排队等待,接口类建议加上。
配完一定要自查的几件事
nginx -t通过再 reload,改坏了语法会直接导致 reload 失败;- 用
nginx -s reload而不是restart——同机其他站点不会被中断; - 上传接口记得调
client_max_body_size,默认 1M 会让大文件请求直接 413; - 反向代理后,应用里取到的 IP 和协议是代理的,要把
X-Real-IP、X-Forwarded-Proto透传并让应用按可信代理解析; - 加了缓存头之后,用
curl -I确认Cache-Control、Expires真的是你想要的值。
内置变量速查
| 变量 | 含义 |
|---|---|
$host |
请求头里的主机名,没有则用 server_name |
$uri |
当前请求的路径,不含参数,会被 rewrite 改写 |
$request_uri |
原始请求行里的完整路径,含参数,不会变 |
$args |
查询字符串 |
$remote_addr |
客户端地址(经过代理时是上一跳的地址) |
$scheme |
http 或 https |
$http_xxx |
任意请求头,- 换成 _,如 $http_user_agent |
对外跳转尽量用 $request_uri 而不是 $uri,否则参数会丢。
本文整理自我自己早先的 Nginx 笔记和公开资料(主要参考了 chrootliu 的《Nginx 最全配置讲解》与网上流传的一份 Nginx 学习手册),文字由 AI 协助改写后经我复核。配置以所用 Nginx 版本的官方文档为准。
这篇笔记整理自我自己的实践记录,如果做法有出入,或者你踩过别的坑,欢迎到留言板一起聊聊。