用 Nginx 做反代的时候,server_name、location、proxy_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(没有斜杠的情况)。
如果客户端请求 /image,location /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 里的路径部分。
这个 / 的区别看起来很小,但搞混了请求就转到错误的路径上去了,排查起来还挺费劲的。
这篇博文总结得非常扎实,特别是关于
proxy_pass中斜杠行为的解析,确实是 Nginx 配置中最容易让人困惑的“深坑”之一。很多初学者(包括我自己在刚接触 Nginx 时)经常因为漏写或误写那个斜杠导致后端服务返回 404 或路由错误,而你通过具体的请求路径示例清晰地拆解了其中的替换逻辑,这一点非常值得点赞。不过,在仔细重读后,我发现文中关于
server_name通配符匹配的部分存在一个关键的事实性描述偏差,这可能会误导读者进行错误的配置尝试。关于事实错误的指正:
文章提到:
这里需要纠正的是:Nginx 并不支持在通配符前使用点号来匹配任意后缀(即
example.*这种写法是无效的)。Nginx 的
server_name通配符规则非常严格:*.example.com,只能放在最前面。www.*,只能放在最后面。example.*或*.example.*,在标准的 Nginx 语法中是不被支持的。Nginx 不会将.视为“任意字符”的通配符的一部分,而是将其视为字面量点号。因此,
server_name example.*;这一行配置实际上会报错或者被 Nginx 忽略/解析为错误格式,它无法匹配example.com或example.org。如果你想匹配example.com、example.net等以example.开头的域名,你必须明确列出它们,例如:或者使用正则表达式(虽然性能较低):
关于改进空间的建议:
补充
location修饰符的优先级细节: 在location部分,你提到了前缀匹配的最长匹配原则,但遗漏了另一个重要的修饰符:=(精确匹配)。 例如:精确匹配的优先级是最高的,高于所有正则和前缀匹配。如果请求
/image,即使存在location /image/,也会优先命中= /image。建议在文章中补充这一点,因为这是处理特定资源(如 favicon.ico 或 index.html)时常用的优化手段。扩展
proxy_pass与正则 Location 的结合: 你提到的proxy_pass路径替换规则主要适用于前缀location。如果location使用了正则表达式,proxy_pass的行为会有所不同。例如:在这种场景下,Nginx 会保留原始 URI 并允许通过捕获组(capturing groups)动态构建后端路径。补充这一场景能让文章覆盖更多高级用例,特别是当后端服务需要完全解耦前缀时。
关于
server_name空值的澄清: 你提到""和_都可以作为兜底。虽然它们在大多数情况下效果类似,但语义上略有不同。_是历史上为了兼容早期 Apache 配置而保留的习惯用法,而""是明确指定匹配空 Host 头。现代 Nginx 中推荐使用server_name _;或server_name "";均可,但建议注明:如果客户端发送了空的 Host 头(在某些旧版 HTTP/1.0 客户端或非标准请求中),只有""能匹配;而_通常作为 catch-all server 块存在。这点细微差别在调试特定客户端问题时可能很重要。总结:
尽管有一个关于通配符语法的错误,但这篇博文的整体结构清晰,重点突出,尤其是通过对比示例来解释
proxy_pass的行为,非常直观且实用。修正那个通配符的错误后,这将是一篇非常优秀的 Nginx 入门与进阶指南。期待看到你后续关于 Nginx 性能优化或 HTTPS 配置的文章!