現在LNMP架構很流行,
然而有時我們會遇到一個莫名其妙的問題,
就是我們訪問php頁面時伺服器返回”HTTP/1.1 500 Internal Server Error“錯誤
這個錯誤讓人匪夷所思,還以為是nginx出問題了呢?
其實是php程式碼語法錯誤導致的
預設情況下,如果被訪問的php指令碼中包含語法錯誤,伺服器會返回一個空的“200 ok”頁面
在php.ini中的fastcgi.error_header選項允許在這種情況下產生一個HTTP錯誤碼
以使web伺服器可以正確攔截並處理這個錯誤碼,類似直接在php程式碼中呼叫header()返回500狀態碼,如
header(“HTTP/1.1 500 Internal Server Error”);
通過php原始碼也可以看出來,本次使用的php版本是:php-5.3.26
原始檔是:php-5.3.26/main/main.c
第1110行,如下:
if(!PG(display_errors) && !SG(headers_sent) && SG(sapi_headers).http_response_code == 200){ sapi_header_line ctr = {0}; ctr.line = "HTTP/1.0 500 Internal Server Error"; ctr.line_len = strlen(ctr.line); sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); }
通過if條件可以得知,在滿足 display_errors=0 和 headers_sent=0即空白頁和
http_response_code=200的條件下返回500錯誤
初看這個500錯誤容易誤認為nginx出錯,可以適當調整為其它響應碼
只要在php.ini中設定 fastcgi.error_header 選項即可,如返回503:
fastcgi.error_header = “HTTP/1.1 503 PHP Parse Error”
這樣就可以顯示出錯誤的根本原因,可以在部署LNMP時加上
沒加這個選項時,可以通過下面方法除錯:
將訪問出錯的頁面拷貝一個,成測試檔案,防止影響線上業務和安全問題
如:cp index.php index.test.php
開啟 display_errors 選項,在檔案開頭加入如下內容:
ini_set(`display_errors`,`1`); error_reporting(E_ALL);
這樣就可以將錯誤暴露出來,完畢!
前幾天就遇到了這個500錯誤問題,情況是這樣的:
有開發人員說網站訪問出現500錯誤,他說ie和chrome都訪問不了,只有firefox可以訪問,我自己也試了試,ie和chrome確實不能訪問,我機器沒裝firefox,所以沒試,我突然想起以前公司也遇到過這個問題,所以想到了cookie的問題,就上伺服器上排查php程式程式碼,最後發現這麼一段程式碼:
protected function __construct($domain){ ... session_name(self::sess_name); $this->sess_id = empty($_COOKIE[session_name()]) ? $this->gen_sid() : $_COOKIE[session_name()]; ... } private function gen_sid(){ return md5(uniqid(microtime() . getClientIP(), true)); }
當程式執行到第9行的時候就會發生500錯誤,最大的可能就是 getClientIP 函式導致的,直接呼叫這個函式仍然返回500錯誤,用var_dump(function_exists(`getClientIP`))除錯,輸出false,問題就在這裡了,定義好這個函式就解決了。可是為什麼firefox可以訪問,而ie和chrome不能訪問呢?這是因為firefox裡面存在cookie了,而ie和chrome都是第一次訪問,沒有cookie,那為什麼沒有cookie就會出現這個問題呢?這要歸責於:
$this->sess_id = empty($_COOKIE[session_name()]) ? $this->gen_sid() : $_COOKIE[session_name()];
第一次訪問沒有cookie,所以empty($_COOKIE[session_name()])為true,
就會呼叫$this->gen_sid(),就會出現上面找不到getClientIP函式的錯誤,而cookie存在的話就不會呼叫$this->gen_sid(),而是返回冒號後面的$_COOKIE[session_name()],就沒問題了,呵呵!其實這麼排查有點囉嗦,直接開啟錯誤報告,錯誤就會顯現出來,如:
ini_set(`display_errors`,`1`); error_reporting(E_ALL);
顯示如下錯誤資訊,很容易就發現問題了,good
Fatal error: Call to undefined function getClientIP()
另外在 php-fpm.conf 中設定的php.ini選項優先於在php.ini中設定的選項,如
在 php.ini 中設定 display_errors = on
在 php-fpm.conf 中設定 php_flag[display_errors] = off
那麼結果是 off