文章转载自:https://blog.csdn.net/u011296355/article/details/106740860/
背景
为了区分线上环境和测试环境,我弄了个自己测试专用的域名test.daemoncoder.com,线上环境的正式域名是www.daemoncoder.com。nginx里的server_name配置改为:
# 只列出了我们关心的配置,省略了其他无关部分
server {
server_name www.daemoncoder.com test.daemoncoder.com;
...
}
但是使用时发现请求一直报错,重定向到错误页面,于是开始了问题的定位。
问题的定位
根据业务上报错时打的日志,定位到请求公共处理的部分里有这么一个判断:
if ($_SERVER['SERVER_NAME'] != parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) {
$this->redirectError();
}
判断请求referer里解析出的域名如果和nginx里的$server_name
变量里的域名如果不一样,就跳到错误页面,也是前面说的问题。
那么问题来了,看referer中的域名为什么和 $_SERVER['SERVER_NAME']
不一致呢?我请求用的链接形式如 http://test.daemoncoder.com/xxx 这种形式,但是最终在PHP这一层取到 $_SERVER['SERVER_NAME']
的值为 www.daemoncoder.com,而 HTTP_REFERER 里的域名为:test.daemoncoder.com。可以看到 SERVER_NAME 的取值和我们的预期不一致,nginx是怎么把这个变量传过来的,需要从 nginx 的 fastcgi_params 配置文件中找一下 SERVER_NAME 的定义:
fastcgi_param SERVER_NAME $server_name;
可以看到 nginx 里的 $server_name
变量就是我们PHP里取的$_SERVER['SERVER_NAME']
的来源。
问题的原因
通过上面的定位,我们基本可以看到问题的根本原因了(敲黑板,划重点):
当nginx配置里一个server节点下,server_name配置多个域名时,$server_name
变量的值都是配置的第一个。
再回顾下我的 nginx 配置:
# 只列出了我们关心的配置,省略了其他无关部分
server {
server_name www.daemoncoder.com test.daemoncoder.com;
...
}
server_name 结点有两个:www.daemoncoder.com 和 test.daemoncoder.com,当我用测试域名去访问页面的时候,可以匹配到 test.daemoncoder.com 这个域名,所以会根据当前这个server节点的配置来处理这个请求,但是 nginx 会把$server_name
的值设置为当前 server 节点的配置的第一个 server_name,也就是 www.daemoncoder.com。如果配置改为:
server_name test.daemoncoder.com www.daemoncoder.com;
那么用测试域名请求就可以得到期望的值了(但是正式域名就出问题了)。
解决方式
第一种方式就是把配置文件按域名拆分到各自单独的server节点下,也就是:
# 省略其他无关部分
server {
server_name www.daemoncoder.com;
...
}
server {
server_name test.daemoncoder.com;
...
}
这样用不同的域名访问会落到各自对应的配置中,解析到的 $server_name
也都是各自的值。
第二种方式是修改 nginx SERVER_NAME 使用 $host
变量, 也就是把
fastcgi_param SERVER_NAME $server_name; 修改为:fastcgi_param SERVER_NAME $host;
$host
变量的解析都是当前请求的host,不会受 server_name 是否配置多个域名的影响,这样我们在PHP里取 $_SERVER['SERVER_NAME']
取出的值就是实际请求的域名,也可以解决问题(但是代码里的这个判断逻辑在测试环境似乎就没有意义了,问题不大)。