记一次 nginx 多站点配置错误导致301跳转的 debug

晚上顺手打开工作室某网站看看有无动态,一打开发现跳转到了工作室另一个网站上。

于是乎我简单的用 wget 看了一下,提示301转移,但是没有转移到其他 IP 上。

 

看到301我想到最近折腾过的 rewrite 规则和 return 语句实现301跳转,于是乎遍查/etc/nginx/sites-enabled 下所有配置文件,但是没发现可疑的重写语句,似乎不像是重写规则写错导致。

查询 Access log:

没有发现有价值的信息(似乎是跳转等处理不会记录在Access log 里,Access log 只记录请求 HTTP 头)

想了想能引起域名之间的访问跳转,如果不是写在全局配置里的字段在生效,或者是某个配置写的位置不对( 本应该在location 字段下却写在 server 字段下等)

询问了一下其他管理员,最近这台服务器上部署了一个网站的镜像备份作为测试,配置在搬运中可能出现冲突。并被告知在 server 字段的 listen 语句里加上IP ,把 listen 80;改成 listen IP:80; 就能正常访问。

一试,果然可以访问了,但是以前没遇到过在 listen 里加 IP 的情况感觉有点虚(PS,这个设置大概是为了多网卡接口的服务器使用,也可以用于内网外网访问的区分),进一步尝试修改了几个其他的配置文件慢慢测试之后发现了更神奇的问题,域名没跳转,但是请求的目录不是正确的目录,这就更离奇了,相当于原本 A 域名指向 A 目录,B 域名指向 B 目录,现在 A 域名的访问直接使用了 B 域名的配置文件,不过和上次相同的是,第一个响应还是一个301,感觉是 nginx 判断请求的时候使用的错误的配置文件而不是使用了错误的跳转条件的问题,还有一个疑问就是为什么明明每个配置里都标注了 server_name ,实际 nginx 判断的时候却像是直接忽略了 server_name,配置和目录都完全是另一个网站,以至于打开的完全是另一个网站而域名显示却是原本请求的网站——却没有任何报错,nginx 没有,浏览器也没有。

于是去官网查询了一下 nginx 的配置文件细节问题,处理请求的时候如何定位。参考文档:http://nginx.org/cn/docs/http/request_processing.html#how_to_prevent_undefined_server_names

 下面让我们来看一个复杂点的配置,在这个配置里,有几个虚拟主机在不同的地址上监听:

server {
listen 192.168.1.1:80;
server_name example.org www.example.org;

}

server {
listen 192.168.1.1:80;
server_name example.net www.example.net;

}

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

}
这个配置中,nginx首先测试请求的IP地址和端口是否匹配某个server配置块中的listen指令配置。接着nginx继续测试请求的Host头是否匹配这个server块中的某个server_name的值。如果主机名没有找到,nginx将把这个请求交给默认虚拟主机处理。例如,一个从192.168.1.1:80端口收到的访问www.example.com的请求将被监听192.168.1.1:80端口的默认虚拟主机处理,本例中就是第一个服务器,因为这个端口上没有定义名为www.example.com的虚拟主机。

这个情况有点像我所遇到的情况,虽然这台服务器没有配置多网卡接口。

根据这这篇文档,我的理解是最近部署的那个网站的配置里有 IP+端口,nginx 先检测到 IP+端口符合的就先转交到这个 Server 区块下,优先跳转到那个新加入的测试镜像网站。

前面提到的在各个配置文件 listen 语句里加入 IP:PORT 就能访问是同为 IP+端口的前提下各个配置文件又是平权的,在遇到多个 IP+端口的时候才检测  server_name。所以出现了只要有部分加上 IP 的时候没有判断 server_name 而分配了一个不正确的配置和文件目录。

一般情况下,所有配置文件都只有 listen 80 的时候所有配置也是平权的,所以 nginx 会再看  server_name 进行判断使用哪个 Server 区块。

如果没有 server_name,在解析 DNS 后 IP 一致前提下随后的处理优先级是根据server块出现的顺序,如果是通过 include 方式包含的配置文件,则是根据文件名的字典排序。

总结一下就是:

如果有配置使用 listen IP +端口方式配置,优先于 listen 端口 处理,多个配置文件都采用listen IP+端口或者都采用 listen 端口,则通过server_name判断,在不注明 server_name 的前提下,按出现顺序或文件名字典排序处理。

 


UPDATE 2014-11-29

晚上的时候这台服务器上的某个网站出现403错误,于是登上 SSH 看配置发现和前天的没有任何改动,wget 没有发现301记录,遍查该站点所在目录,权限属主均正常,访问记录和错误记录均无任何异常,惊叹难道玄学问题出现了?最后偶然发现在这台服务器上的其他网站也出现403才反应过来可能又是遇到IP:端口导致所有访问被重定向的问题,重新将配置文件链接到/etc/nginx/sites-enabled下 reload 后正常,检查果然是有个新添加的网站配置前面有listen IP:端口,而指向的目录权限已经改变。

(但是有个疑问,为什么直接报了403错误而没有301,这有点奇怪)

总之:

不要随便在 listen 语句里加 IP,只要一个配置修改了一定修改所有已启用的站点配置!对于不是具有多个公网IP的服务器不建议使用listen ip:port!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.