在PHP中,我如何读取任何请求头?

350
如何在PHP中读取任何标题?
例如自定义标题:X-Requested-With

“任何标题”是不明确的。您无法在一次操作中读取“任何标题”,但可以分别读取传入请求头列表和当前传出响应头列表。 - David Spector
16个回答

470
如果:您只需要一个标题,而不是所有标题,最快的方法是:

<?php
// Replace XXXXXX_XXXX with the name of the header you need in UPPERCASE (and with '-' replaced by '_')
$headerStringValue = $_SERVER['HTTP_XXXXXX_XXXX'];


ELSE IF:您是作为Apache模块或者自PHP 5.4开始使用FastCGI(简单方法)运行PHP的:

apache_request_headers()

<?php
$headers = apache_request_headers();

foreach ($headers as $header => $value) {
    echo "$header: $value <br />\n";
}
ELSE: 在其他情况下,可以使用(用户自定义实现):
<?php
function getRequestHeaders() {
    $headers = array();
    foreach($_SERVER as $key => $value) {
        if (substr($key, 0, 5) <> 'HTTP_') {
            continue;
        }
        $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
        $headers[$header] = $value;
    }
    return $headers;
}

$headers = getRequestHeaders();

foreach ($headers as $header => $value) {
    echo "$header: $value <br />\n";
}
参见:
getallheaders() - (PHP >= 5.4) 跨平台版,是apache_request_headers()的别名。 apache_response_headers() - 获取所有HTTP响应头信息。 headers_list() - 获取即将被发送的HTTP头列表。

3
我假设这仅适用于使用Apache服务器的情况...也许需要让问题发起人知道这一点 :) - alex
17
我不关心82%的业余爱好者。我关心专业的安装。没有一个理智的人会试图在mod_php上运行高流量网站。 - vartec
15
是的,我认为这是下投票的一个完美理由。在任何时候,最好的答案应该得到赞同,而不好的答案则应该被投反对票。这个网站并不是历史解决方案的存放地 :-) - Thomas Jensen
4
请注意,有些人可能对其他或所有的标题感兴趣,而不是特别关注“HTTP_X_REQUESTED_WITH”;答案是绝对正确的,Jacco明确表示它仅适用于Apache;在某些情况下,它不是最佳/最高效的解决方案,这不是我认为应该被投票反对的理由。 - Sebastian Hoffmann
2
你只扫描大写的HTML是错误的,RFC2616(HTTP/1.1)规定头部不区分大小写,而不仅仅是你所覆盖的值部分。头部名称也可以全部小写。 - Glenn Plas
显示剩余11条评论

390
$_SERVER['HTTP_X_REQUESTED_WITH']

RFC3875,4.1.18:

如果使用的协议是HTTP,则以 HTTP_ 开头的元变量名包含从客户端请求标头字段读取到的值。HTTP标头字段名称被转换为大写,所有出现的 - 都替换为 _,并在前面加上 HTTP_ 来生成元变量名。


7
我能否可靠地期望任何服务器将所有头信息放入$_SERVER变量中?PHP文档(http://php.net/manual/en/reserved.variables.server.php)对于我们可以确信其中有什么内容含糊其辞。 - Mark Amery
4
这种方法并不总是可行,特别是在 PHP-fpm(或 CGI)中。该标头并非始终可从 PHP 内部访问。 - Glenn Plas
@EvandelaCruz:http://leserged.online.fr/phpinfo.php 我在这里看到了:_SERVER["HTTP_CACHE_CONTROL"] max-age=0 - Quassnoi
@Quassnoi 好的,没事了。我正在使用 Chrome 浏览器,并且已经打开了 F12 开发工具,并勾选了“禁用缓存”的框。在同一窗口中,我三次检查了浏览器是否确实发送了缓存请求头,Chrome 报告说它们已经被发送了。但是 $_SERVER 没有显示它们。然而,在阅读了您的答案后,我检查了我的 phpinfo.php 文件,发现这些头信息确实存在!所以,我回到我的调试脚本:print_r($_SERVER);die(); 并进行了硬刷新(ctrl+f5),确实,缓存头现在正在显示。 - Evan de la Cruz
缓存请求头。因此,$_SERVER似乎说了实话。问题可能是现代浏览器在显示源代码视图时重新加载页面(尽管也有原因),或者Chrome在显示源视图时应该保持开发工具状态。无论如何,我现在偏离主题了,但我想提供一个结论。 - Evan de la Cruz
显示剩余6条评论

61

你应该在全局变量 $_SERVER 中查找所有以 HTTP_ 为前缀的 HTTP 标头,并将破折号 (-) 替换为下划线 (_)。

例如,你的 X-Requested-With 可以在以下位置找到:

$_SERVER['HTTP_X_REQUESTED_WITH']

$_SERVER 变量创建关联数组可能很方便。有几种风格可以实现,但这里是一个输出驼峰键的函数:

$headers = array();
foreach ($_SERVER as $key => $value) {
    if (strpos($key, 'HTTP_') === 0) {
        $headers[str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))))] = $value;
    }
}

现在只需使用$headers ['XRequestedWith']来检索所需的标头。

$_SERVER的PHP手册:http://php.net/manual/en/reserved.variables.server.php


3
在我看来,最好的答案是Thomas的解释和Quassnoi的最终结果。关联数组通常不是所需的,从阅读parseRequestHeaders()函数中也很难得出简单的解决方案。如果确实需要这样一个关联数组,则在我看来,Apache函数是最佳选择,因为它返回接收到的确切头信息,而不是一个混淆的CamelCase版本。(还要注意的是,自PHP 5.4起,它不再仅限于Apache。) - Brilliand
如果你在2年11个月之前回答了这个问题,那么这个答案可能会有200多个赞。 - DividedByZero
apache_request_headers()getallheaders()在我的测试中似乎没有将标头名称大写。它们返回的内容与我从客户端传递的完全相同。那么,为什么您在这样一个替换函数中要将标头名称大写呢? - rineez

39

自 PHP 5.4.0 起,您可以使用getallheaders函数将所有请求标头作为关联数组返回:

var_dump(getallheaders());

// array(8) {
//   ["Accept"]=>
//   string(63) "text/html[...]"
//   ["Accept-Charset"]=>
//   string(31) "ISSO-8859-1[...]"
//   ["Accept-Encoding"]=>
//   string(17) "gzip,deflate,sdch"
//   ["Accept-Language"]=>
//   string(14) "en-US,en;q=0.8"
//   ["Cache-Control"]=>
//   string(9) "max-age=0"
//   ["Connection"]=>
//   string(10) "keep-alive"
//   ["Host"]=>
//   string(9) "localhost"
//   ["User-Agent"]=>
//   string(108) "Mozilla/5.0 (Windows NT 6.1; WOW64) [...]"
// }

之前这个函数只能在PHP作为Apache/NSAPI模块运行时使用。


23
我正在使用PHP-FPM 5.5和NGINX。getallheaders()函数不存在。 - CMCDragonkai
@CMCDragonkai 在 FPM 中,你是如何获取头部信息的? - Ajit Singh
1
TLFTP;如果您正在使用FPM,则在7.3之后getallheaders()变得可用。 - d70rr3s

7

将头文件名称传递给此函数,以便无需使用for循环就能获取其值。如果未找到头,则返回null。

/**
 * @var string $headerName case insensitive header name
 *
 * @return string|null header value or null if not found
 */
function get_header($headerName)
{
    $headers = getallheaders();
    return isset($headerName) ? $headers[$headerName] : null;
}

注意:此功能仅适用于Apache服务器,请参见:http://php.net/manual/en/function.getallheaders.php

注意:此函数将处理并加载所有标头到内存中,其性能不如for循环。


函数出现错误,请将 $pHeaderKey 替换为 $headerKey - Tegos

6

如果只需要检索一个键,例如需要"Host"地址,则可以使用以下方法

apache_request_headers()['Host']

为了避免循环并将其内联到echo输出中。

从PHP 5.4开始,而在5.3中,这种语法会导致错误。 - Robin K
这是使用 PHP 7.1 进行测试的。 - Dickens A S
你可以将PHP作为Apache模块运行,或者从PHP 5.4开始使用FastCGI(简单方法):如下所述使用apache_request_headers()函数。 - JonoJames

6
我使用CodeIgniter并使用以下代码获取它。对于将来的某些人可能有用。
$this->input->get_request_header('X-Requested-With');

我知道get_request_header()方法,但不确定是否可以直接使用标头名称,即无需将连字符更改为下划线。 - Valkay

6

strtolower 函数在一些提议的解决方案中缺失,RFC2616(HTTP/1.1)将头字段定义为不区分大小写的实体。整个字段都是如此,而不仅仅是 value 部分。

因此,像仅解析 HTTP_ 条目这样的建议是错误的。

更好的做法是:

if (!function_exists('getallheaders')) {
    foreach ($_SERVER as $name => $value) {
        /* RFC2616 (HTTP/1.1) defines header fields as case-insensitive entities. */
        if (strtolower(substr($name, 0, 5)) == 'http_') {
            $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
        }
    }
    $this->request_headers = $headers;
} else {
    $this->request_headers = getallheaders();
}

请注意与之前建议的微妙差别。此处的功能还适用于php-fpm(+nginx)。


1
RFC 2616在哪里明确说明字段值不区分大小写?它明确指出“HTTP-date区分大小写”——这适用于Date头部——以及“参数值[分号后Content-Type中的文本]可能区分大小写,也可能不区分大小写”。因此,考虑到至少有两个具有区分大小写值的头部,似乎你是错误的。 - Joker_vD
1
HTTP头字段包括通用头部(第4.5节)、请求头部(第5.3节)、响应头部(第6.2节)和实体头部(第7.1节),遵循与RFC 822 [9]第3.1节中给出的相同的通用格式。每个头字段由名称后跟冒号(“:”)和字段值组成。字段名称不区分大小写。 - Glenn Plas
5
字段名称(names)不区分大小写。这段文字中没有提到字段值(values),而文档的其他部分明确说明了区分大小写的字段值。 - Joker_vD
1
为什么你们要把下划线替换成空格,然后再把空格替换成破折号?这样做有必要吗?难道不可以直接这样写吗:$headers[ucwords(strtolower(substr($name, 5)))] = $value;? - temirbek

4
为了简单明了,以下是如何获得您需要的内容:
简单:
$headerValue = $_SERVER['HTTP_X_REQUESTED_WITH'];

或者当你需要一次获取一个时:

<?php
/**
 * @param $pHeaderKey
 * @return mixed
 */
function get_header( $pHeaderKey )
{
    // Expanded for clarity.
    $headerKey = str_replace('-', '_', $pHeaderKey);
    $headerKey = strtoupper($headerKey);
    $headerValue = NULL;
    // Uncomment the if when you do not want to throw an undefined index error.
    // I leave it out because I like my app to tell me when it can't find something I expect.
    //if ( array_key_exists($headerKey, $_SERVER) ) {
    $headerValue = $_SERVER[ $headerKey ];
    //}
    return $headerValue;
}
// X-Requested-With mainly used to identify Ajax requests. Most JavaScript frameworks
// send this header with value of XMLHttpRequest, so this will not always be present.
$header_x_requested_with = get_header( 'X-Requested-With' );

其他的头信息也在超全局数组 $_SERVER 中,你可以在这里了解如何获取它们:http://php.net/manual/en/reserved.variables.server.php

1
与其他答案相比,您的函数似乎不起作用,因为它没有将 HTTP_ 添加到 $headerKey - EECOLOR

2
如果您有Apache服务器,这将起作用。
PHP代码:
$headers = apache_request_headers();

foreach ($headers as $header => $value) {
    echo "$header: $value <br />\n";
}

结果:

Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0
Host: www.example.com
Connection: Keep-Alive

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