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