文章轉載自: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']
取出的值就是實際請求的域名,也可以解決問題(但是代碼里的這個判斷邏輯在測試環境似乎就沒有意義了,問題不大)。