PHP Conference Japan 2024

get_headers

(PHP 5, PHP 7, PHP 8)

get_headers获取服务器响应 HTTP 请求发送的所有报头

描述

get_headers(字符串 $url, 布尔值 $associative = false, ?资源 $context = null): 数组|false

get_headers() 返回一个数组,其中包含服务器响应 HTTP 请求发送的报头。

参数

url

目标 URL。

associative

如果可选的 associative 参数设置为 true,则 get_headers() 会解析响应并设置数组的键。

context

使用 stream_context_create() 创建的有效上下文资源,或 null 以使用默认上下文。

返回值

返回一个包含报头的索引数组或关联数组,如果失败则返回 false

变更日志

版本 描述
8.0.0 associative 已从 整数 更改为 布尔值
7.1.0 添加了 context 参数。

示例

示例 #1 get_headers() 示例

<?php
$url
= 'http://www.example.com';

print_r(get_headers($url));

print_r(get_headers($url, true));
?>

以上示例将输出类似以下内容

Array
(
    [0] => HTTP/1.1 200 OK
    [1] => Date: Sat, 29 May 2004 12:28:13 GMT
    [2] => Server: Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [3] => Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
    [4] => ETag: "3f80f-1b6-3e1cb03b"
    [5] => Accept-Ranges: bytes
    [6] => Content-Length: 438
    [7] => Connection: close
    [8] => Content-Type: text/html
)

Array
(
    [0] => HTTP/1.1 200 OK
    [Date] => Sat, 29 May 2004 12:28:14 GMT
    [Server] => Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
    [ETag] => "3f80f-1b6-3e1cb03b"
    [Accept-Ranges] => bytes
    [Content-Length] => 438
    [Connection] => close
    [Content-Type] => text/html
)

示例 #2 使用 HEAD 的 get_headers() 示例

<?php
// 默认情况下,get_headers 使用 GET 请求来获取报头。如果您
// 想发送 HEAD 请求,则可以使用流上下文:
$context = stream_context_create(
[
'http' => array(
'method' => 'HEAD'
)
]
);
$headers = get_headers('http://example.com', false, $context);
?>

参见

添加注释

用户贡献的注释 18 条注释

58
nick at innovaweb dot co dot uk
14 年前
似乎有些人只在寻找 3 位数的 HTTP 响应代码 - 这是一个快速而粗略的解决方案

<?php
function get_http_response_code($theURL) {
$headers = get_headers($theURL);
return
substr($headers[0], 9, 3);
}
?>

就这么简单?回显包含要检查响应代码的 URL 的函数,瞧。自定义重定向,替代被阻止的 is_file() 或 flie_exists() 函数(就像我在我的服务器上遇到的那样),因此采用了这种廉价的解决方法。但是嘿 - 它有效!

布丁
3
sey at sey dot prometheus-designs dot net
19 年前
aeontech at gmail dot com 更新的 get_headers 函数在 $format = 1 时格式化日期不正确。

替换
<?
else {
$headers[strtolower($h2[0])] = trim($h2[1]);
}
?>


<?
else {
$foo = implode( ':', $h2 );
$foo = preg_replace( '/[a-zA-Z- ]*: /', '', $foo );
$headers[strtolower($h2[0])] = trim( $foo );
}
7
mcilva
6 年前
如何检查 URL 是否指向有效的视频

<?php
function isVideo($url){
$url = get_headers($url,1);
if(
is_array($url['Content-Type'])){ // 在某些响应中,Content-type 是一个数组
$video = strpos($url['Content-Type'][1],'video');
}else{
$video = strpos($url['Content-Type'],'video');
}
if(
$video !== false)
return
true;

return
false;
}

?>
6
cees at cornelisdigitaal dot nl
9 年前
@Jim Greene

如果 URL 不存在,则会返回不完整的报头,导致子字符串默认为垃圾。

垃圾的整数始终为 0。因此,您的低于 400 不总是意味着它存在!
10
Jim Greene
11 年前
我知道您不应该引用其他注释,但真诚地赞扬 Nick at Innovaweb 的评论,我根据他的想法添加了这些内容

如果您使用该函数,它将返回一个字符串,如果您仅检查返回 404 或 200 等的文件,这非常有用。如果您将字符串值转换为整数,则可以在其上执行数学比较。

例如

<?php
function get_http_response_code($theURL) {
$headers = get_headers($theURL);
return
substr($headers[0], 9, 3);
}

if(
intval(get_http_response_code('filename.jpg')) < 400){
// 文件存在,万岁!
}
?>

经验法则是,如果响应小于 400,则文件存在,即使它不返回 200。
2
php at hm2k dot org
14 年前
<?php
/**
* 获取服务器在响应 HTTP 请求(不进行重定向)时发送的所有真实标头
*
* @link https://php.net/function.get_headers
* @link http://bugs.php.net/bug.php?id=50719
*/

function get_real_headers($url,$format=0,$follow_redirect=0) {
if (!
$follow_redirect) {
//设置新的默认选项
$opts = array('http' =>
array(
'max_redirects'=>1,'ignore_errors'=>1)
);
stream_context_get_default($opts);
}
//获取标头
$headers=get_headers($url,$format);
//恢复默认选项
if (isset($opts)) {
$opts = array('http' =>
array(
'max_redirects'=>20,'ignore_errors'=>0)
);
stream_context_get_default($opts);
}
//返回
return $headers;
}
?>
7
Kubo2
11 年前
如果您不想在 get_headers() 函数失败时显示警告,只需在它前面添加一个 at 符号(@)。

<?php

// 失败时,警告将被隐藏并返回 false
$withoutWarning = @get_headers("http://www.some-domain.com");

// 失败时,警告显示,并且也将返回 false
$withWarning = get_headers("http://www.some-domain.com");

// bool(false)
var_dump($withoutWarning);
// bool(false)
var_dump($withWarning);
?>
8
Weboide
14 年前
请注意,get_headers **将遵循重定向**(HTTP 重定向)。如果 $format=0,新的标头将被附加到数组中。如果 $format=1,则每个冗余标头将是多个值的数组,每个重定向一个值。

例如

<?php
$url
= 'http://google.com';
var_dump(get_headers($url,0));
/*array(18) {
[0]=> string(30) "HTTP/1.0 301 Moved Permanently"
[1]=> string(32) "Location: http://www.google.com/"
[2]=> string(38) "Content-Type: text/html; charset=UTF-8"
[3]=> string(35) "Date: Sun, 26 Sep 2010 00:59:50 GMT"
[4]=> string(38) "Expires: Tue, 26 Oct 2010 00:59:50 GMT"
[5]=> string(38) "Cache-Control: public, max-age=2592000"
....
string(15) "HTTP/1.0 200 OK"
[10]=> string(35) "Date: Sun, 26 Sep 2010 00:59:51 GMT"
[11]=> string(11) "Expires: -1"
[12]=> string(33) "Cache-Control: private, max-age=0"
.....
}*/

/*===========================*/

var_dump(get_headers($url,1));
/*array(11) {
[0]=>
string(30) "HTTP/1.0 301 Moved Permanently"
["Location"]=> string(22) "http://www.google.com/"
["Content-Type"]=> array(2) {
[0]=> string(24) "text/html; charset=UTF-8"
[1]=> string(29) "text/html; charset=ISO-8859-1"
}
["Date"]=> array(2) {
[0]=> string(29) "Sun, 26 Sep 2010 01:03:39 GMT"
[1]=> string(29) "Sun, 26 Sep 2010 01:03:39 GMT"
}
["Expires"]=> array(2) {
[0]=> string(29) "Tue, 26 Oct 2010 01:03:39 GMT"
[1]=> string(2) "-1"
}
["Cache-Control"]=> array(2) {
[0]=> string(23) "public, max-age=2592000"
[1]=> string(18) "private, max-age=0"
}
.....
}*/
?>
2
info at marc-gutt dot de
16 年前
应该与原始的 get_headers() 相同。

<?php
if (!function_exists('get_headers')) {
function
get_headers($url, $format=0) {
$headers = array();
$url = parse_url($url);
$host = isset($url['host']) ? $url['host'] : '';
$port = isset($url['port']) ? $url['port'] : 80;
$path = (isset($url['path']) ? $url['path'] : '/') . (isset($url['query']) ? '?' . $url['query'] : '');
$fp = fsockopen($host, $port, $errno, $errstr, 3);
if (
$fp)
{
$hdr = "GET $path HTTP/1.1\r\n";
$hdr .= "Host: $host \r\n";
$hdr .= "Connection: Close\r\n\r\n";
fwrite($fp, $hdr);
while (!
feof($fp) && $line = trim(fgets($fp, 1024)))
{
if (
$line == "\r\n") break;
list(
$key, $val) = explode(': ', $line, 2);
if (
$format)
if (
$val) $headers[$key] = $val;
else
$headers[] = $key;
else
$headers[] = $line;
}
fclose($fp);
return
$headers;
}
return
false;
}
}
?>
5
bunny at bunny dot hu
8 年前
如果 URL 重定向,并且新目标也重定向,我们会在数组中获取 Location。我们还会在数字索引的值中获取 HTTP 代码。

这是一个标头的一部分(不是全部),它在重定向链中(id=4 是目标页面)的样子。
/test.php?id=1 -> /test.php?id=2 -> /test.php?id=3 -> /test.php?id=4

数组
(
[0] => HTTP/1.1 302 Moved Temporarily

[Location] => 数组
(
[0] => /test.php?id=2
[1] => /test.php?id=3
[2] => /test.php?id=4
)

[1] => HTTP/1.1 302 Moved Temporarily
[2] => HTTP/1.1 302 Moved Temporarily
[3] => HTTP/1.1 200 OK
)

在典型情况下,我们只需要目标页面信息,因此这里有一小段代码来获取它。

$result = array();
$header = get_headers($url, 1);
foreach ($header as $key=>$value) {
if (is_array($value)) {
$value = end($value);
}
$result[$key] = $value;
}
6
sidnash56 at gmail dot com
8 年前
要检查 URL 有效性,这对我来说一直运行良好。

function url_valid(&$url) {
$file_headers = @get_headers($url);
if ($file_headers === false) return false; // 当服务器未找到时
foreach($file_headers as $header) { // 解析所有标头
// 当 301/302 重定向导致 200 时,更正 $url
if(preg_match("/^Location: (http.+)$/",$header,$m)) $url=$m[1];
// 获取最后一个 $header $code,以防重定向。
if(preg_match("/^HTTP.+\s(\d\d\d)\s/",$header,$m)) $code=$m[1];
} // 结束 foreach...
if($code==200) return true; // $code 200 == 全部正常
else return false; // 其他所有都失败了,所以这肯定是一个错误的链接。
} // 结束函数 url_exists
5
pegasus at vaultwiki dot org
9 年前
请注意,不要对通过用户输入获取的 URL 使用 get_headers 函数。stream 上下文中 timeout 选项仅影响流中数据之间的空闲时间。它不影响连接时间或请求的总时间。

(不幸的是,timeout 选项的文档中没有提到这一点,但在其他一些代码讨论中已经讨论过,并且我做了自己的测试来确认这些讨论的结论。)

因此,用户很容易提供一个类似于 Slowloris 攻击的 URL - 频繁地向您的 get_headers 函数提供仅一个 header,以避免流超时。

如果您发布您的代码,即使是 default_socket_timeout 也不能保证能解决这个问题,因为它在许多 PHP 版本(除了最新版本)的 HTTPS 协议中都存在问题: https://bugs.php.net/bug.php?id=41631

如果 get_headers 接受用户输入,攻击者很容易使您的所有 PHP 子进程都变得繁忙。

相反,使用 cURL 函数获取用户提供的 URL 的 header,并手动解析这些 header,因为 CURLOPT_TIMEOUT 应用于整个请求。
2
stuart at sixletterwords dot com
19 年前
嗨,几周前我偶然发现了这个函数,并在一个记录我公司拥有的域名信息的应用程序中使用了它,发现它返回的状态大多数情况下都是错误的(对于显然在线的网站,返回 400 错误请求或空)。然后深入研究后,我注意到问题在于它无法获取具有重定向的网站的正确信息。但这并不是全部问题,因为我的服务器上的所有内容都返回了错误的状态。我在 php.net 上搜索了其他信息,发现 fsockopen 的示例效果更好,只需要一些调整。

这是我根据它和一个小改动组合的函数。

<?php
if(!function_exists('get_headers')) {
function
get_headers($url,$format=0,$httpn=0){
$fp = fsockopen($url, 80, $errno, $errstr, 30);
if (
$fp) {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: $url\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!
feof($fp)) {
$var.=fgets($fp, 1280);
}

$var=explode("<",$var);
$var=$var[0];
$var=explode("\n",$var);
fclose($fp);
return
$var;
}
}
}
?>

它返回一个包含 header 的数组(唯一的问题是,如果网站没有正确的 HTML,它也会提取一些内容)。

希望这对其他人有所帮助。
1
Anonymous
18 年前
我注意到了。
某些服务器如果发送的是 'HEAD' 请求而不是 'GET' 请求,则只会返回错误的回复 header。'GET' 请求 header 始终接收最真实的 HTTP header,而不是 'HEAD' 请求 header。但是,如果您不介意使用快速但有风险的方法,那么 'HEAD' 请求更适合您。

顺便说一句……这是带有额外信息的 get header,例如用户、密码和 Referer……
<?php
function get_headers_x($url,$format=0, $user='', $pass='', $referer='') {
if (!empty(
$user)) {
$authentification = base64_encode($user.':'.$pass);
$authline = "Authorization: Basic $authentification\r\n";
}

if (!empty(
$referer)) {
$refererline = "Referer: $referer\r\n";
}

$url_info=parse_url($url);
$port = isset($url_info['port']) ? $url_info['port'] : 80;
$fp=fsockopen($url_info['host'], $port, $errno, $errstr, 30);
if(
$fp) {
$head = "GET ".@$url_info['path']."?".@$url_info['query']." HTTP/1.0\r\n";
if (!empty(
$url_info['port'])) {
$head .= "Host: ".@$url_info['host'].":".$url_info['port']."\r\n";
} else {
$head .= "Host: ".@$url_info['host']."\r\n";
}
$head .= "Connection: Close\r\n";
$head .= "Accept: */*\r\n";
$head .= $refererline;
$head .= $authline;
$head .= "\r\n";

fputs($fp, $head);
while(!
feof($fp) or ($eoheader==true)) {
if(
$header=fgets($fp, 1024)) {
if (
$header == "\r\n") {
$eoheader = true;
break;
} else {
$header = trim($header);
}

if(
$format == 1) {
$key = array_shift(explode(':',$header));
if(
$key == $header) {
$headers[] = $header;
} else {
$headers[$key]=substr($header,strlen($key)+2);
}
unset(
$key);
} else {
$headers[] = $header;
}
}
}
return
$headers;

} else {
return
false;
}
}
?>

此致。
Donovan
1
drfickle2 at yahoo dot com
19 年前
aeontech,以下更改添加了对 SSL 连接的支持。感谢您的代码!

if (isset($url_info['scheme']) && $url_info['scheme'] == 'https') {
$port = 443;
$fp=fsockopen('ssl://'.$url_info['host'], $port, $errno, $errstr, 30);
} else {
$port = isset($url_info['port']) ? $url_info['port'] : 80;
$fp=fsockopen($url_info['host'], $port, $errno, $errstr, 30);
}
2
Backslider
12 年前
需要注意的是,此函数(和其他函数)在失败时不会返回 "false",而是返回一个巨大的 WARNING,如果未关闭错误报告/警告,则会停止脚本的执行。



这简直太疯狂了!任何执行类似获取 URL 的函数,如果 URL 失败的原因不是格式错误,都应该简单地返回 false,而无需发出警告。
1
php dot sirlancelot at spamgourmet dot com
16 年前
我尝试尽可能地复制原生行为,以适用于没有 get_headers() 函数的系统。代码如下
<?php
if (!function_exists('get_headers')) {
function
get_headers($Url, $Format= 0, $Depth= 0) {
if (
$Depth > 5) return;
$Parts = parse_url($Url);
if (!
array_key_exists('path', $Parts)) $Parts['path'] = '/';
if (!
array_key_exists('port', $Parts)) $Parts['port'] = 80;
if (!
array_key_exists('scheme', $Parts)) $Parts['scheme'] = 'http';

$Return = array();
$fp = fsockopen($Parts['host'], $Parts['port'], $errno, $errstr, 30);
if (
$fp) {
$Out = 'GET '.$Parts['path'].(isset($Parts['query']) ? '?'.@$Parts['query'] : '')." HTTP/1.1\r\n".
'Host: '.$Parts['host'].($Parts['port'] != 80 ? ':'.$Parts['port'] : '')."\r\n".
'Connection: Close'."\r\n";
fwrite($fp, $Out."\r\n");
$Redirect = false; $RedirectUrl = '';
while (!
feof($fp) && $InLine = fgets($fp, 1280)) {
if (
$InLine == "\r\n") break;
$InLine = rtrim($InLine);

list(
$Key, $Value) = explode(': ', $InLine, 2);
if (
$Key == $InLine) {
if (
$Format == 1)
$Return[$Depth] = $InLine;
else
$Return[] = $InLine;

if (
strpos($InLine, 'Moved') > 0) $Redirect = true;
} else {
if (
$Key == 'Location') $RedirectUrl = $Value;
if (
$Format == 1)
$Return[$Key] = $Value;
else
$Return[] = $Key.': '.$Value;
}
}
fclose($fp);
if (
$Redirect && !empty($RedirectUrl)) {
$NewParts = parse_url($RedirectUrl);
if (!
array_key_exists('host', $NewParts)) $RedirectUrl = $Parts['host'].$RedirectUrl;
if (!
array_key_exists('scheme', $NewParts)) $RedirectUrl = $Parts['scheme'].'://'.$RedirectUrl;
$RedirectHeaders = get_headers($RedirectUrl, $Format, $Depth+1);
if (
$RedirectHeaders) $Return = array_merge_recursive($Return, $RedirectHeaders);
}
return
$Return;
}
return
false;
}}
?>
该函数将处理最多五个重定向。
享受!
1
rgawenda at gmail dot com
2 个月前
一些注释使用 substr 获取 HTTP/1.X 之后第一个 ([0]) 头部中的响应“代码”(取决于 PHP 版本),但 HTTP/2 已经存在近十年,并且 HTTP/3 现在得到了广泛的支持,因此根据规范,更好的、面向未来的解析方法来提取该代码是

<?php
$headers
= get_headers($url, true);
$response_code = explode(" ", $headers[0])[1];
?>

此外,如果您没有使用 stream_context_create 阻止重定向,请注意 $headers[0] 是请求 URL 的响应代码,而下一个解析并跟随的重定向在 [1] 处,因此您还应该首先查找正确的(最后一个)响应头

<?php
$response_header
= $headers[max(array_filter(array_keys($headers), 'is_int'))];
?>
To Top