現在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