如何让Apache在PHP错误时发送500状态码?

6
我正在开发一个使用HTTP响应代码和响应主体进行通信的PHP应用程序。因此,以下是PHP代码中的典型情况:
try {
    doSomething();
}
catch (Exception $e) {
    header('HTTP/1.1 500 Internal Server Error');
}

"...并且客户端中的典型代码如下:"
switch(responseCode) {
     case 200 :
         // all fine
         // ....
     case 500 :
         // trouble!
 } 

这很酷,只要每个错误都在PHP代码中被很好地捕获。
问题是:如果PHP代码中发生未捕获的错误或者像语法错误这样无法捕获的错误,Apache会发送200 OK。但我想让Apache显示500内部服务器错误。也许可以通过.htaccess实现。

曾经想过一个 JavaScript 客户端,但已将其删除。 - hek2mgl
抱歉,我更新了我的答案。 - Your Common Sense
3个回答

2

只有当PHP输出响应体的第一个字节时才会发送响应头。在此期间,您可以更改标题(和状态代码)。牢记这一点,以下是解决方案:

将脚本设置为在脚本开始时发送500响应代码,并在结尾处发送200。以下是一个示例:

header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');

if (rand(0, 1) == 1) {
    die("Script terminated prematurely");
}

header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
echo "Success";

请确保在代码中只有一个位置设置200响应代码。

注意:您可以使用http_response_code(PHP >= 5.4)替代header


不错的点子 +1 :) 但是,如果例如抛出(致命的)语法错误,它仍会发送200 OK。 - hek2mgl
至少在PHP >= 5.3.19版本中,语法错误的脚本会产生500错误。无论如何,没有人愿意在生产服务器上运行这样的脚本。 - Salman A

2
您当然可以自己编写错误处理程序。但是,并非所有PHP错误都是可捕获的。例如,语法错误甚至不允许您的代码运行,包括您的错误处理程序。
为了处理可捕获的错误,您可以使用auto_append_fileauto_prepend_file指令来放置您的错误处理代码。
无法捕获的错误是另一个问题。如果PHP作为FastCGI运行,则会自动为您生成500状态码。但是,您可能正在通过其他SAPI(如Apache模块)运行PHP。对此我不清楚,抱歉。(如果我找到了相关信息,我会回报的。)

谢谢你的快速回答!是的,我写了一个自定义的错误和异常处理程序,但你说得对,无法捕获的错误是个问题。我正在搜索相关信息。 - hek2mgl
无论如何,这种致命错误不被注意到并上线是相当罕见的,不是吗? - Álvaro González
语法错误和其他致命错误无法被任何错误处理程序捕获。 - bcosca

2
现在这已经不是问题了 - 自从 PHP 5.3 终于学会在出错时发送 503 而不是 200 后,这个问题就不存在了。
哦,看起来是在 5.2.4 版本中解决的:
更改错误处理程序以在 PHP 错误发生时发送 HTTP 500 而不是空白页面。
你需要设置 display_errors = off 才能使其正常工作。
我在我的 Windows Apache 2.4 上使用的是 PHP 5.4.5,与此完全一致。

我已经测试过PHP 5.3.10。例如,语法错误仍将导致200 OK的结果。在哪些情况下,PHP会发送503? - hek2mgl
你的更新听起来很不错!但对我仍然没有作用。我已经使用了PHP 5.3.10-1ubuntu3.4进行了测试。我使用了一个简单的语法错误页面 <? error 进行测试,display_errors 被禁用。我仍然得到一个空白页面和响应码200。我正在使用apache2 sapi。你知道有哪个PHP版本/ Web服务器组合可以产生500吗? - hek2mgl
@YourCommonSense,是否存在临时覆盖5.2.4行为的方法,强制在PHP错误发生时显示空白页面? - Pacerier
@YourCommonSense,display_errors的值为0,而页面正在发送HTTP 500错误。我们能否覆盖此行为,强制显示空白页面? - Pacerier
@Pacerier PHP确实会显示一个空白页面。无论您看到的其他装饰是什么,都是由浏览器(最有可能)或Web服务器(需要特殊调整)提供的。尝试使用类似于“wget”的命令行代理请求您的页面并查看。 - Your Common Sense
@YourCommonSense,抱歉没有表达清楚。我的意思是覆盖HTTP 500的行为,强制它显示一个HTTP 200的空白页面? - Pacerier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接