用 Nginx 做反代的时候,server_namelocationproxy_pass 这三个指令几乎每次都要写,但它们各自有好几种写法,细节上的差异还挺容易踩坑的,记录一下。


server_name 有哪几种写法

1. 精确域名匹配

server_name example.com www.example.com;

最常用的写法,直接写域名,可以同时写多个,用空格隔开。

2. 通配符匹配(前缀或后缀)

server_name *.example.com;          # 匹配所有子域名,如 www.example.com, mail.example.com
server_name example.*;              # 匹配所有后缀,如 example.com, example.org
server_name *.example.*;            # 匹配子域名和后缀,如 www.example.com, mail.example.org

通配符只能出现在最前面或最后面,不能写在中间,比如 www.*.com 这样是不行的。

3. 正则表达式匹配

server_name ~^www\d+\.example\.com$;    # 匹配 www1.example.com, www2.example.com 等
server_name ~^(.*)\.example\.com$;      # 匹配所有 example.com 的子域名

正则以 ~ 开头,Nginx 会按正则来匹配。不过正则的优先级低于精确匹配和通配符匹配,性能也稍差一些,能用通配符解决的就不要用正则。

4. 空值(默认主机)

server_name "";
# 或
server_name _;

"" 表示匹配没有 Host 头的请求。_ 其实不是什么特殊语法,它只是一个不可能被真正匹配到的域名,通常用来做兜底的 default server。

5. IP 地址

server_name 192.168.1.1;

直接通过 IP 访问时会匹配到这个 server 块。

6. 变量

server_name $hostname;  # 使用服务器的主机名

$hostname 会自动取服务器的主机名,不过这种写法用得比较少。

7. 组合使用

server_name example.com *.example.com ~^www\d+\.example\.com$;

多种写法可以混在一起用,Nginx 的匹配优先级是:精确匹配 > 前缀通配符 > 后缀通配符 > 正则表达式


location /、location /image、location /image/ 有什么区别

这三个看起来差不多,但匹配行为有区别。

末尾斜杠的影响

  • location /image 匹配 /image/image/,甚至 /imageabc 也能匹配到(因为是前缀匹配)。
  • location /image/ 只匹配以 /image/ 开头的路径,比如 /image/photo.jpg,但不会匹配 /image(没有斜杠的情况)。

如果客户端请求 /imagelocation /image 会处理,而 location /image/ 不会匹配,请求可能会回退到 location /

多个 location 存在时的优先级

Nginx 选择最长匹配的前缀。举个例子,请求 /image/photo.jpg

  • 如果有 location /image/,优先匹配它。
  • 如果只有 location /image,匹配它。
  • 如果都没有,匹配 location /

location / 是最短的前缀,所以它相当于兜底,任何路径都能匹配到。


proxy_pass 末尾带不带斜杠的区别

这个是我觉得 Nginx 最容易踩坑的地方,proxy_pass 后面带不带 /,转发出去的路径完全不一样。

假设我们的 location 是这样的:

location /api/ {
    proxy_pass ...;
}

1. proxy_pass http://backend(不带路径)

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

请求 http://example.com/api/users,转发到 http://backend/api/users

原始的 URI /api/users 会完整保留,直接拼到 backend 后面。

2. proxy_pass http://backend/(带斜杠)

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

请求 http://example.com/api/users,转发到 http://backend/users

注意这里 /api/ 被剥掉了,只剩下 /users 拼到 http://backend/ 后面。这是因为 proxy_pass 后面带了路径(哪怕只是一个 /),Nginx 就会把 location 匹配到的前缀替换掉。

再比如请求 http://example.com/api/,转发到 http://backend/

3. proxy_pass http://backend/image(带具体路径)

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

请求 http://example.com/api/users,转发到 http://backend/imageusers

这里就能看出来规律了:Nginx 把 location 匹配到的 /api/ 部分去掉,剩下的 users 直接拼到 http://backend/image 后面,所以变成了 /imageusers——这通常不是你想要的结果。

如果你想转发到 /image/users,应该写成:

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

总结一下规律

简单记就是:

  • proxy_pass 没有路径部分(比如 http://backend):原始 URI 原封不动转发。
  • proxy_pass 有路径部分(比如 http://backend/http://backend/xxx):location 匹配到的前缀会被替换成 proxy_pass 里的路径部分。

这个 / 的区别看起来很小,但搞混了请求就转到错误的路径上去了,排查起来还挺费劲的。