【问题标题】:PHP: How to send HTTP response code?PHP:如何发送 HTTP 响应代码?
【发布时间】:2020-04-14 19:03:43
【问题描述】:

我有一个 PHP 脚本需要使用 HTTP 响应代码(状态代码)做出响应,例如 HTTP 200 OK,或者一些 4XX 或 5XX 代码。

我如何在 PHP 中做到这一点?

【问题讨论】:

  • 没有一个答案说明在 PHP 代码中检测到 404 错误调用 header() 后该怎么做。 exit() 好吗?

标签: php http-response-codes


【解决方案1】:

我刚刚发现了这个问题,并认为它需要一个更全面的答案:

PHP 5.4 开始,有三种方法可以完成此操作:

自行组装响应代码(PHP >= 4.0)

header() 函数有一个特殊的用例,可以检测 HTTP 响应行并让您将其替换为自定义响应行

header("HTTP/1.1 200 OK");

但是,这需要对 (Fast)CGI PHP 进行特殊处理:

$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cgi')
    header("Status: 404 Not Found");
else
    header("HTTP/1.1 404 Not Found");

注意:根据HTTP RFC原因短语可以是任何自定义字符串(符合标准),但为了客户端兼容性我建议放一个随机字符串。

注意:php_sapi_name() 需要 PHP 4.0.1

头函数的第三个参数 (PHP >= 4.3)

使用第一个变体时显然存在一些问题。我认为最大的问题是它部分由 PHP 或 Web 服务器解析,并且文档记录不佳。

从 4.3 开始,header 函数具有第三个参数,可以让您轻松地设置响应代码,但使用它需要第一个参数是非空字符串。这里有两个选项:

header(':', true, 404);
header('X-PHP-Response-Code: 404', true, 404);

我推荐第二个。第一个确实适用于我测试过的所有浏览器,但一些小型浏览器或网络爬虫可能会遇到仅包含冒号的标题行的问题。头字段名称在第 2 位。变体当然没有以任何方式标准化并且可以修改,我只是选择了一个有希望的描述性名称。

http_response_code 函数 (PHP >= 5.4)

http_response_code() 函数是在 PHP 5.4 中引入的,它使事情大大变得更容易了。

http_response_code(404);

就是这样。

兼容性

当我需要低于 5.4 的兼容性但想要“新”http_response_code 函数的功能时,这是我编写的一个函数。我相信 PHP 4.3 的向后兼容性绰绰有余,但你永远不知道......

// For 4.3.0 <= PHP <= 5.4.0
if (!function_exists('http_response_code'))
{
    function http_response_code($newcode = NULL)
    {
        static $code = 200;
        if($newcode !== NULL)
        {
            header('X-PHP-Response-Code: '.$newcode, true, $newcode);
            if(!headers_sent())
                $code = $newcode;
        }       
        return $code;
    }
}

【讨论】:

  • @dualed (1) 在调用header() 之后,headers_sent() 不会总是为真吗? (2) 曾经在 5.4 世界中找到类似 http_response_text() 的东西吗?至少旧的 header() 可以影响状态码后面的文本。
  • @BobStein-VisiBone (1) headers_sent() 为真,如果您不能添加更多标头,因为内容已发送,而不是如果您已添加标头。 (2) 抱歉,没有。不过其他语言有更好的支持
  • @Perry 我不建议这样做的原因与我不建议仅使用冒号的原因相同。 PHP 可能会在整个版本中以不同的方式处理这个问题,因为它没有定义这样的“标题”会发生什么,它可能会完全失败 - 没有设置标题或状态,或者它可能添加无效的标题(http 1.1 协议标准 需要冒号)
  • 我花了好几个小时才意识到http_response_code(也许更普遍地修改标题)在你echo之后不再起作用。希望对您有所帮助。
  • http_response_code() 不能用于发明自定义错误代码。例如http_response_code(930) 将导致 apache 日志文件正确显示 930,但实际上将错误 500 发送回客户端。使用header() 方法代替这个公认的奇怪用例。
【解决方案2】:

不幸的是,我发现@dualed 提出的解决方案存在各种缺陷。

  1. 使用 substr($sapi_type, 0, 3) == 'cgi' 不足以检测快速 CGI。使用 PHP-FPM FastCGI 进程管理器时,php_sapi_name() 返回 fpm 而不是 cgi

  2. Fasctcgi 和 php-fpm 暴露了 @Josh 提到的另一个错误 - 在 PHP-FPM (FastCGI) 下使用 header('X-PHP-Response-Code: 404', true, 404); 确实可以正常工作

  3. header("HTTP/1.1 404 Not Found"); 在协议不是 HTTP/1.1(即“HTTP/1.0”)时可能会失败。必须使用$_SERVER['SERVER_PROTOCOL'] 检测当前协议(自 PHP 4.1.0 起可用

  4. 调用http_response_code()时至少有2种情况导致意外行为:

    • 当 PHP 遇到它不理解的 HTTP 响应代码时,PHP 会用它知道的来自同一组的代码替换该代码。例如,“521 Web 服务器已关闭”替换为“500 内部服务器错误”。来自其他组 2xx、3xx、4xx 的许多其他不常见的响应代码都是以这种方式处理的。
    • 在具有 php-fpm 和 nginx http_response_code() 函数的服务器上,可以按预期更改代码,但不会更改消息。例如,这可能会导致奇怪的“404 OK”标头。用户评论http://www.php.net/manual/en/function.http-response-code.php#112423 在 PHP 网站上也提到了这个问题

这里有完整的 HTTP 响应状态代码列表供您参考(此列表包括来自 IETF 互联网标准以及其他 IETF RFC 的代码。其中许多当前不受 PHP http_response_code 函数支持):http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

您可以通过调用以下方法轻松测试此错误:

http_response_code(521);

如果您有例如自定义客户端应用程序调用您的服务器并期望一些额外的 HTTP 代码,服务器将发送“500 内部服务器错误”HTTP 响应代码导致意外错误。


我的解决方案(适用于 4.1.0 以后的所有 PHP 版本):

$httpStatusCode = 521;
$httpStatusMsg  = 'Web server is down';
$phpSapiName    = substr(php_sapi_name(), 0, 3);
if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') {
    header('Status: '.$httpStatusCode.' '.$httpStatusMsg);
} else {
    $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
    header($protocol.' '.$httpStatusCode.' '.$httpStatusMsg);
}

结论

http_response_code() 实现并不支持所有 HTTP 响应代码,并且可能会用同一组中的另一个响应代码覆盖指定的 HTTP 响应代码。

新的 http_response_code() 函数并不能解决所涉及的所有问题,反而会引入新的错误。

@dualed 提供的“兼容性”解决方案不能按预期工作,至少在 PHP-FPM 下是这样。

@dualed 提供的其他解决方案也存在各种错误。快速 CGI 检测不处理 PHP-FPM。必须检测当前协议。

感谢任何测试和 cmets。

【讨论】:

  • 适用于 PHP 8.0 / HTTP 429 Too Many Requests on Gecko 和 Blink 浏览器。
【解决方案3】:

从 PHP 5.4 开始,您可以使用 http_response_code() 获取和设置标头状态代码。

这里是一个例子:

<?php

// Get the current response code and set a new one
var_dump(http_response_code(404));

// Get the new response code
var_dump(http_response_code());
?>

这里是这个函数在php.net中的文档:

http_response_code

【讨论】:

  • 根据我的经验,这是最好的答案。
  • 为什么要使用 var_dump() ?
  • 但是为什么用 var_dump() 而不是 echo?结果不适合简单的回声吗?甚至是 print_r()。 var_dump() 似乎不足以用于生产代码...
  • @TomasGonzalez 这没什么大不了的,我只是想通过使用 var_dump() 打印所有内容来向您展示其中的内容,并且您对它们并不重要
  • 好的,我明白了。引起我注意的是,在官方文档中,该示例也使用了 var_dump() 。所以我很好奇这样做的原因。我可能错过了一些东西。 php.net/manual/en/function.http-response-code.php
【解决方案4】:

如果您不使用输出缓冲,请在正文的任何​​输出之前添加此行。

header("HTTP/1.1 200 OK");

将消息部分(“OK”)替换为适当的消息,并将状态代码替换为您的代码(404、501 等)

【讨论】:

  • 我们输入的信息(替换OK)可以是任何东西吗?
  • 这对我有用。我正在使用 PHP 5.3 在网站上制作联系表格。这个解决方案对我有用。它将为 AJAX 请求完成失败功能提供响应文本和此 HTTP 代码。这就是我想要的。
【解决方案5】:

如果您是因为 Wordpress 在加载环境时出现 404 而出现在这里,这应该可以解决问题:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
status_header( 200 );
//$wp_query->is_404=false; // if necessary

问题是由于它发送了一个 Status: 404 Not Found 标头。你必须覆盖它。 这也可以:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
header("HTTP/1.1 200 OK");
header("Status: 200 All rosy");

【讨论】:

    【解决方案6】:

    使用header 函数。在它所采用的第一个参数部分中有一个示例。

    【讨论】:

      【解决方案7】:

      如果您的 PHP 版本不包含此功能:

      <?php
      
      function http_response_code($code = NULL) {
              if ($code !== NULL) {
                  switch ($code) {
                      case 100: $text = 'Continue';
                          break;
                      case 101: $text = 'Switching Protocols';
                          break;
                      case 200: $text = 'OK';
                          break;
                      case 201: $text = 'Created';
                          break;
                      case 202: $text = 'Accepted';
                          break;
                      case 203: $text = 'Non-Authoritative Information';
                          break;
                      case 204: $text = 'No Content';
                          break;
                      case 205: $text = 'Reset Content';
                          break;
                      case 206: $text = 'Partial Content';
                          break;
                      case 300: $text = 'Multiple Choices';
                          break;
                      case 301: $text = 'Moved Permanently';
                          break;
                      case 302: $text = 'Moved Temporarily';
                          break;
                      case 303: $text = 'See Other';
                          break;
                      case 304: $text = 'Not Modified';
                          break;
                      case 305: $text = 'Use Proxy';
                          break;
                      case 400: $text = 'Bad Request';
                          break;
                      case 401: $text = 'Unauthorized';
                          break;
                      case 402: $text = 'Payment Required';
                          break;
                      case 403: $text = 'Forbidden';
                          break;
                      case 404: $text = 'Not Found';
                          break;
                      case 405: $text = 'Method Not Allowed';
                          break;
                      case 406: $text = 'Not Acceptable';
                          break;
                      case 407: $text = 'Proxy Authentication Required';
                          break;
                      case 408: $text = 'Request Time-out';
                          break;
                      case 409: $text = 'Conflict';
                          break;
                      case 410: $text = 'Gone';
                          break;
                      case 411: $text = 'Length Required';
                          break;
                      case 412: $text = 'Precondition Failed';
                          break;
                      case 413: $text = 'Request Entity Too Large';
                          break;
                      case 414: $text = 'Request-URI Too Large';
                          break;
                      case 415: $text = 'Unsupported Media Type';
                          break;
                      case 500: $text = 'Internal Server Error';
                          break;
                      case 501: $text = 'Not Implemented';
                          break;
                      case 502: $text = 'Bad Gateway';
                          break;
                      case 503: $text = 'Service Unavailable';
                          break;
                      case 504: $text = 'Gateway Time-out';
                          break;
                      case 505: $text = 'HTTP Version not supported';
                          break;
                      default:
                          exit('Unknown http status code "' . htmlentities($code) . '"');
                          break;
                  }
                  $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
                  header($protocol . ' ' . $code . ' ' . $text);
                  $GLOBALS['http_response_code'] = $code;
              } else {
                  $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
              }
              return $code;
          }
      

      【讨论】:

        【解决方案8】:
        header("HTTP/1.1 200 OK");
        http_response_code(201);
        header("Status: 200 All rosy");
        

        http_response_code(200);不起作用,因为测试警报 404 https://developers.google.com/speed/pagespeed/insights/

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-11
          • 1970-01-01
          • 2020-11-12
          相关资源
          最近更新 更多