冷知识 - HTTP请求头与SERVER数组

  • 作者:KK

  • 发表日期:2016.12.11


要点速读

一句话笼统的话囊括:请求头有什么报头,SERVER数组就有什么;请求头没有的,SERVER数组也不会有。所以你想要的SERVER元素可能不存在,取值前先isset判断

除了一些特殊的http请求头,通常情况下,其它所有请求头的名称和值都会默认在$_SERVER数组里对应产生HTTP_请求头大写名称的key

比如请求头中有个user-agent的话,则$_SERVER['HTTP_USER_AGENT']就与之对应

如果再添加个abc的请求头,则PHP底层也会预定义$_SERVER['HTTP_ABC']来让服务端取值


测试代码

用jQuery添加http报头测试:

$.ajax({
	url : '/test.php',
	dataType : 'text',
	beforeSend : function(xhr){
		xhr.setRequestHeader('abc-def', 123);
	},
	success : function(result){
		alert(result);
	}
});

PHP响应代码:

echo $_SERVER['HTTP_ABC_DEF'];

结果提示的值就是123


提示

所以你可以认为大部分HTTP_开头的$_SERVER数组的key都是从请求头里面采集信息得来的

如果请求信息中没有这些请求头字段的话,则PHP无法获取相关的key,这也是为什么$_SERVER['HTTP_REFERER']有时候会不存在的原因,反正请求过来的报头没有的话就没有咯,你叫人家去哪里给你值呢


吓你一跳的真相

你千万别只想着“哦,我知道了,反正获取HTTP_REFERER的时候isset一下就是了”

其实你基本上获取一切HTTP_****的相关下标值都要isset一下,你先在测试web下建立a.phpb.php,在a.php里粘贴以下基本的curl代码:


$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://test/b.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
curl_close($ch);
print_r($output);

然后b.php的代码就是print_r($_SERVER);这样输出整个SERVER数组

好了跑一下a.php,看看b.php响应过来的结果(跟我的差不多吧):

Array
(
    [PATH] => C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;D:\app\Python27\;D:\app\Python27\Scripts;C:\ProgramData\Oracle\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\Program Files\TortoiseSVN\bin;D:\Program Files (x86)\Git\cmd;D:\phpStudy\php\php-7.0.12-nts;C:\ProgramData\ComposerSetup\bin;C:\Users\Administrator\AppData\Local\Google\Chrome\Application;D:\Program Files\nodejs\;C:\sqlite;D:\Program Files\scrt\
    [SYSTEMROOT] => C:\WINDOWS
    [COMSPEC] => C:\WINDOWS\system32\cmd.exe
    [PATHEXT] => .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    [WINDIR] => C:\WINDOWS
    [PHP_FCGI_MAX_REQUESTS] => 1000
    [PHPRC] => D:/phpStudy2/php/php-7.0.12-nts/
    [_FCGI_SHUTDOWN_EVENT_] => 2256
    [HTTP_CONNECTION] => close
    [SCRIPT_NAME] => /b.php
    [REQUEST_URI] => /b.php
    [QUERY_STRING] => 
    [REQUEST_METHOD] => GET
    [SERVER_PROTOCOL] => HTTP/1.1
    [GATEWAY_INTERFACE] => CGI/1.1
    [REMOTE_PORT] => 47514
    [SCRIPT_FILENAME] => E:/projects/test/b.php
    [SERVER_ADMIN] => admin@phpStudy.net
    [CONTEXT_DOCUMENT_ROOT] => E:/projects/test
    [CONTEXT_PREFIX] => 
    [REQUEST_SCHEME] => http
    [DOCUMENT_ROOT] => E:/projects/test
    [REMOTE_ADDR] => 127.0.0.1
    [SERVER_PORT] => 80
    [SERVER_ADDR] => 127.0.0.1
    [SERVER_NAME] => test
    [SERVER_SOFTWARE] => Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
    [SERVER_SIGNATURE] => 
    [SystemRoot] => C:\WINDOWS
    [HTTP_ACCEPT] => */*
    [HTTP_HOST] => test
    [FCGI_ROLE] => RESPONDER
    [PHP_SELF] => /b.php
    [REQUEST_TIME_FLOAT] => 1481472847.2315
    [REQUEST_TIME] => 1481472847
)

亲,你能在这里发现HTTP_USER_AGENT

给你清点一下,比平时浏览器请求过来的一共是少了这些:

HTTP_REFERERHTTP_USER_AGENTHTTP_X_REQUESTED_WITHHTTP_ACCEPT_LANGUAGEHTTP_ACCEPT_ENCODINGHTTP_CONNECTION

因为curl请求只填写了很基本的必须的http请求头,所以服务端并没有收到更多

而在浏览器里面用ajax或a标签跳转产生的请求,被浏览器底层包装一翻后就有了很多相关的报头,在我们“年少”的时候,一直被迷惑,如果你玩PHP有一两个年头,是时候要认清一下HTTP请求信息与PHP的$_SERVER数组的关系了