Apache/PHP returns 错误页面上的 HTTP 状态代码 200

Apache/PHP returns HTTP Status Code 200 on error pages

我是 CentOS 7 上的 运行 PHP 5.4,当 php 文件抛出错误(异常或语法错误)时,它 return 是 HTTP 200 状态代码而不是 500。

如何在PHP遇到错误时return出现500服务器错误?

我试过查看其他 Whosebug 帖子,它们似乎都指向解决方案 returning 你自己的 500 错误代码(我相信这应该是 PHP 的正常行为它自己不需要我手动触发 http header,根据这个信息:PHP emitting 500 on errors - where is this documented?) 它也指出 Xdebug 是一个可能的问题,但即使我重建服务器时我的问题仍然存在Xdebug.

我的相关设置是:

根据 a PHP bug report,此处描述的行为是由于 display_errors 的设置方式所致。

[2010-02-03 19:03 UTC] derick@php.net

The reason why display errors needs to be turned of, is because displayed errors generate output, and output causes the headers to be send out. I'm afraid we can't do much about this.

所以问题的答案是:

  • 当 display_errors 开启时,它将始终 return 200
  • 当 display_errors 关闭时,它将 return 500

请注意,这有时可能与 apache 中的 CustomLog 格式有关。例如 %>s ("the final status") 可以(正确地)在访问日志中产生 403,而即使响应的状态代码是其他内容,%s 也可能会记录 200。

解决错误 #50921 的方法:使用 auto_prepend_file 将 http 响应代码设置为 500,然后在您知道页面已启动时将其设置为 200。一个有代表性但不全面的例子:

# php.ini
auto_prepend_file = /path/to/auto_prepend_file.php

# auto_prepend_file.php
<?php http_response_code(500);

# index.php
<?php
register_shutdown_function(function () {
    $error = error_get_last();
    switch ($error['type'] ?? 0) {
    case E_ERROR:                                                         
    case E_PARSE:                                                         
    case E_CORE_ERROR:                                                    
    case E_COMPILE_ERROR:                                                 
    case E_RECOVERABLE_ERROR:                                             
        file_put_contents('/path/to/log', $error['message'], FILE_APPEND);
        http_response_code(500);
        echo 'Something went horribly wrong.';
        echo str_repeat(' ', 512);
        break;
});

http_response_code(200);
require_once '/path/to/bootstrap.php';

如果 index.php 中有崩溃错误,通过 auto-prepend 文件设置的 500 仍将执行。不过,除非 display_error and/or display_startup_error 打开,否则不会有任何输出。如果 bootstrap.php 或其任何依赖项出现崩溃错误,则关闭函数将处理这些错误。

这里还有一些场景需要考虑:

  • 应该输出什么格式?检查 ACCEPT header,或相应地默认。
  • 如果已经开始输出怎么办?你如何打断流来发出信号?

这在很大程度上取决于您对环境的控制程度(例如,写入 INI_SYSTEM 级别配置)以及框架的运行方式(例如 ob_start,或否)。

我要强调这只是一个解决方法。如果您的 auto_prepend_file 本身有解析错误,那么您回到原点:PHP 发出 200 状态错误代码。修复引擎发出 500 是打破这个 chicken-and-egg 循环的唯一正确方法。