348

How should I read any header in PHP?

For example the custom header: X-Requested-With.

A.L
  • 10,259
  • 10
  • 67
  • 98
Sabya
  • 11,534
  • 17
  • 67
  • 94
  • "Any header" is ambiguous. You can't read "any header" in one operation, but you can read the incoming list of request headers and the current outgoing list of response headers separately. – David Spector Jul 12 '22 at 19:36

16 Answers16

465

IF: you only need a single header, instead of all headers, the quickest method is:

<?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: you run PHP as an Apache module or, as of PHP 5.4, using FastCGI (simple method):

apache_request_headers()

<?php
$headers = apache_request_headers();

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


ELSE: In any other case, you can use (userland implementation):

<?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";
}


See Also:
getallheaders() - (PHP >= 5.4) cross platform edition Alias of apache_request_headers() apache_response_headers() - Fetch all HTTP response headers.
headers_list() - Fetch a list of headers to be sent.

Jacco
  • 23,534
  • 17
  • 88
  • 105
  • 3
    I assume this is only when using the Apache server... might need to let the OP know that :) – alex Feb 13 '09 at 05:48
  • 17
    I don't care about 82% of amateurs. I care about professional installations. No one in right state of mind would try to run high traffic site on mod_php. – vartec Jan 27 '11 at 11:55
  • 14
    @Jacco Yes, and I think that makes a perfect reason for downvoting. At any given time the best answer should be upvoted and bad answers downvoted. This is not a site of historical solutions :-) – Thomas Jensen Jul 29 '12 at 12:23
  • 4
    @ThomasJensen Consider though, that some might be interessted in other or all headers and not esspecially in 'HTTP_X_REQUESTED_WITH'; The answere is absolute correct and Jacco explecitely stated that it only works for apache; That it in some scenarios not the best / most performant solution is no reason for a downvote IMO. – Sebastian Hoffmann Oct 12 '12 at 09:15
  • 2
    @Paranaix A: I don't know what you mean, I haven't criticized the extent of the answer and your reasoning is exactly why I started my answer by answering the specific question and then elaborated with more general knowledge and links for further information. B: I still don't think you should encourage the use of apache_request_headers(). Newbies finding this question will start using it which is a shame IMO when better functions exist. – Thomas Jensen Nov 07 '12 at 18:54
  • 1
    Although it says that getallheaders is allowed via FastCGI, what about PHP-FPM? It seems to say that the function is undefined – CMCDragonkai Nov 11 '13 at 03:21
  • 2
    You only scan for uppercase HTML which is wrong, RFC2616 (HTTP/1.1) states headers are case insensitive, and not just the value part which you have covered. The header name can be all lowercase too. – Glenn Plas Nov 23 '13 at 16:14
  • 1
    getallheaders() is not a cross-platform edition of `apache_request_headers()` - it's an alias thereof. Says so right in the linked manual page. I tested and the function is undefined on nginx. – tvanc Apr 21 '16 at 23:30
  • 1
    PHP converts the http header name to uppercase and changes dashes to underscores. If you have a header called My-Custom-Header you have to read $_SERVER['HTTP_MY_CUSTOM_HEADER']; – Juraj Petrik May 04 '16 at 12:52
  • 1
    `$_SERVER['HTTP_XXXXXX_XXXX']` doesn't work for all headers. In fact it works only for a few of them. – bzim Apr 02 '17 at 00:42
  • Also, no only UPPERCASE, but convert dashes to underscores, too. – nikans Jul 05 '17 at 10:27
  • 1
    @BrunoCorrêaZimmermann Hi. Could you please extend your comment a bit? I'm trying to find a solution to read ALL **request** headers in PHP, without taking in consideration the web server type. That's why I'm asking you. Thank you! –  Nov 02 '17 at 20:12
  • @JurajPetrik: it certainly seems so, but for one, I couldn't find it written in the PHP docs (despite I kinda' vaguely remembered that I had read it somewhere there, but now googling didn't help). Could you please point to something "official"? – Sz. Mar 24 '18 at 22:47
  • 1
    `getallheaders()` is since PHP 7.3 available via the FPM SAPI [too](https://www.php.net/manual/en/function.getallheaders.php#refsect1-function.getallheaders-changelog) and no longer restricted to Apache @tvanc – wbob Sep 23 '19 at 11:33
  • 1
    Still downvoted because @Thomas Jensen was right. Answered in 2009, the web get some improvements since. This answer should not be selected as the real answer actually. – Florian Doyen Jun 05 '20 at 17:09
  • 1
    So what is the correct answer to this in 2022? – Adam B Jun 21 '22 at 08:32
390
$_SERVER['HTTP_X_REQUESTED_WITH']

RFC3875, 4.1.18:

Meta-variables with names beginning with HTTP_ contain values read from the client request header fields, if the protocol used is HTTP. The HTTP header field name is converted to upper case, has all occurrences of - replaced with _ and has HTTP_ prepended to give the meta-variable name.

Quassnoi
  • 413,100
  • 91
  • 616
  • 614
  • 7
    Can I reliably expect any server to put every header into the `$_SERVER` variable? The PHP documentation at http://php.net/manual/en/reserved.variables.server.php is evasive about what we can be sure will be in there. – Mark Amery Oct 17 '13 at 09:38
  • 4
    This will not (always) work, especially in PHP-fpm (or cgi ). This header is not always available from within PHP. – Glenn Plas Nov 23 '13 at 16:15
  • Using this solution I only see some of the request headers, and in this case, i don't see the one I want. Chrome is sending a `cache-control` header, but I do not see it anywhere in `$_SERVER`. I do see several headers prefixed with `HTTP_`, including "HTTP_ACCEPT", and "HTTP_UPGRADE_INSECURE_REQUESTS" and "HTTP_USER_AGENT" (among several others). But nothing for "cache-control" also nothing for "pragma". This is regardless of case or `HTTP_` prefix. Am I missing something? – Evan de la Cruz Mar 23 '16 at 17:34
  • @EvandelaCruz: http://leserged.online.fr/phpinfo.php I see it here: `_SERVER["HTTP_CACHE_CONTROL"] max-age=0` – Quassnoi Mar 23 '16 at 17:46
  • hmmm, thanks... This is on my wamp dev server and I think PHP is running as an Apache module but I'm not sure. Let me check on my prod box with FPM and see if I can figure out why I don't see it here on wamp... – Evan de la Cruz Mar 23 '16 at 17:49
  • @Quassnoi ok nevermind. I am using chrome and I have the f12 dev tools open and I have the box checked that says "Disable cache". And I triple-checked in that same dev tools window that the caching request headers were indeed being sent from the browser. Chrome reported that they were being sent. But the `$_SERVER` did not show them. However, after reading your answer I checked my phpinfo.php and the headers were indeed there! So, I went back to my debug script: `print_r($_SERVER);die();` and did a hard refresh (ctrl+f5) and sure enough, the caching headers are now showing. – Evan de la Cruz Mar 23 '16 at 17:54
  • @Quassnoi So either Chrome has a bug/undocumented feature, or I am starting to lose my mind in my old age and I didn't triple-check the request headers and/or I didn't have the dev tools open. Should I delete my original comment as to not lend support to the wrong voice? Or should I leave it here for accurate historical sake? Anyways thanks for showing me your phpinfo I would have outright doubted you otherwise... saved me some time chasing my tail – Evan de la Cruz Mar 23 '16 at 17:56
  • @EvandelaCruz: if you can reproduce it reliably, that would make a good question. The phpinfo is not mine, it's the first google hit on "phpinfo online" – Quassnoi Mar 23 '16 at 18:00
  • @Quassnoi Looks like it is more likely my fault. I think what happened was this (which is perhaps a UI oversight, but I wouldn't call it a "bug"): I opened dev tools (f12) and I clicked the "disable cache". I refreshed the window and saw my debug output. I then right-clicked the page and clicked "view-source", which opened another tab with the source code (it is formatted better in source view since I didn't wrap the output in a `
    ` and the array is output with newlines, not HTML). Well, the tab that opened did *not* have the dev tools open.  And so presumably, it did not send the...
    – Evan de la Cruz Mar 23 '16 at 18:04
  • ...caching request headers. And so, $_SERVER told the truth it seems. So the issue may be that modern browsers reload the page when showing the source code view (although there are reasons for that too), or that Chrome should have maintained the dev-tools-state when showing the source view. Anyways I'm way off topic now I suppose but I wanted to offer a conclusion. – Evan de la Cruz Mar 23 '16 at 18:08
  • This is way clearer to me than the explanation accepted by OP (even if the solution is the same), and plus, it has an example, it should be the accepted answer. – Cécile Fecherolle Aug 07 '18 at 08:49
61

You should find all HTTP headers in the $_SERVER global variable prefixed with HTTP_ uppercased and with dashes (-) replaced by underscores (_).

For instance your X-Requested-With can be found in:

$_SERVER['HTTP_X_REQUESTED_WITH']

It might be convenient to create an associative array from the $_SERVER variable. This can be done in several styles, but here's a function that outputs camelcased keys:

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

Now just use $headers['XRequestedWith'] to retrieve the desired header.

PHP manual on $_SERVER: http://php.net/manual/en/reserved.variables.server.php

Thomas Jensen
  • 2,635
  • 25
  • 38
  • 3
    The best answer, in my opinion, is Thomas's explanation with Quassnoi's end result. An associative array is usually not what's needed, and it isn't very easy to work out the simple solution from reading the `parseRequestHeaders()` function. If such an associative array is needed, then IMO the apache function is the best option, since it returns exactly the headers received instead of a mangled CamelCase version. (Also note that as of PHP 5.4, it is no longer Apache-only.) – Brilliand Dec 20 '12 at 19:47
  • Would you have answered this 2 years and 11 months faster, this answer would have 200+ upvotes. – DividedByZero Nov 03 '14 at 16:34
  • `apache_request_headers()` or `getallheaders()` doesn't seem to capitalize the Header names when I tested. They are returning exactly as I pass from client side. Then why are you capitalizing header names in such a replacement function? – rineez Nov 18 '16 at 13:04
38

Since PHP 5.4.0 you can use getallheaders function which returns all request headers as an associative array:

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) [...]"
// }

Earlier this function worked only when PHP was running as an Apache/NSAPI module.

Octopus
  • 8,075
  • 5
  • 46
  • 66
Salman A
  • 262,204
  • 82
  • 430
  • 521
7

Pass a header name to this function to get its value without using for loop. Returns null if header not found.

/**
 * @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;
}

Note: this works only with Apache server, see: http://php.net/manual/en/function.getallheaders.php

Note: this function will process and load all of the headers to the memory and it's less performant than a for loop.

osh
  • 42
  • 7
Milap Kundalia
  • 1,566
  • 1
  • 16
  • 24
6

strtolower is lacking in several of the proposed solutions, RFC2616 (HTTP/1.1) defines header fields as case-insensitive entities. The whole thing, not just the value part.

So suggestions like only parsing HTTP_ entries are wrong.

Better would be like this:

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();
}

Notice the subtle differences with previous suggestions. The function here also works on php-fpm (+nginx).

YakovL
  • 7,557
  • 12
  • 62
  • 102
Glenn Plas
  • 1,608
  • 15
  • 17
  • 1
    Where exactly does RFC 2616 state that field values are case-insensitive? It explitly states that "HTTP-date is case sensitive" — and that goes into `Date` header — and that "Parameter values [text in Content-Type after semicolon] might or might not be case-sensitive". So given there are at least two headers with case-sensitive values, it seems that you're wrong. – Joker_vD Jun 26 '15 at 14:37
  • 1
    ```HTTP header fields, which include general-header (section 4.5), request-header (section 5.3), response-header (section 6.2), and entity-header (section 7.1) fields, follow the same generic format as that given in Section 3.1 of RFC 822 [9]. Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.``` So I guess you're wrong. – Glenn Plas Jun 26 '15 at 16:44
  • 5
    Field *names* are case-insensitive. There is nothing about field *values* in this paragraph, while other parts of the document explicitly tell about case-sensitive field values. – Joker_vD Jun 26 '15 at 23:49
  • 1
    Why ya all replacing underline to space then space to dash? wouldn't this just work: $headers[ucwords(strtolower(substr($name, 5)))] = $value; ? – temirbek Apr 11 '19 at 14:34
6

I was using CodeIgniter and used the code below to get it. May be useful for someone in future.

$this->input->get_request_header('X-Requested-With');
Rajesh
  • 934
  • 12
  • 23
  • It was. Knew about get_request_header() method though, but wasn't sure I could use the header name as is, i.e. without having to change hyphens to underscores. – Valkay Nov 10 '19 at 05:40
6

if only one key is required to retrieved, For example "Host" address is required, then we can use

apache_request_headers()['Host']

So that we can avoid loops and put it inline to the echo outputs

Dickens A S
  • 3,824
  • 2
  • 22
  • 45
4

To make things simple, here is how you can get just the one you want:

Simple:

$headerValue = $_SERVER['HTTP_X_REQUESTED_WITH'];

or when you need to get one at a time:

<?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' );

The other headers are also in the super global array $_SERVER, you can read about how to get at them here: http://php.net/manual/en/reserved.variables.server.php

b01
  • 4,076
  • 2
  • 30
  • 30
  • 1
    Comparing to other answers it seems that you function will not work as it does not prepend `HTTP_` to the `$headerKey` – EECOLOR Jun 13 '17 at 06:41
1

Here's how I'm doing it. You need to get all headers if $header_name isn't passed:

<?php
function getHeaders($header_name=null)
{
    $keys=array_keys($_SERVER);

    if(is_null($header_name)) {
            $headers=preg_grep("/^HTTP_(.*)/si", $keys);
    } else {
            $header_name_safe=str_replace("-", "_", strtoupper(preg_quote($header_name)));
            $headers=preg_grep("/^HTTP_${header_name_safe}$/si", $keys);
    }

    foreach($headers as $header) {
            if(is_null($header_name)){
                    $headervals[substr($header, 5)]=$_SERVER[$header];
            } else {
                    return $_SERVER[$header];
            }
    }

    return $headervals;
}
print_r(getHeaders());
echo "\n\n".getHeaders("Accept-Language");
?>

It looks a lot simpler to me than most of the examples given in other answers. This also gets the method (GET/POST/etc.) and the URI requested when getting all of the headers which can be useful if you're trying to use it in logging.

Here's the output:

Array ( [HOST] => 127.0.0.1 [USER_AGENT] => Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 [ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 [ACCEPT_LANGUAGE] => en-US,en;q=0.5 [ACCEPT_ENCODING] => gzip, deflate [COOKIE] => PHPSESSID=MySessionCookieHere [CONNECTION] => keep-alive )

en-US,en;q=0.5
Michele La Ferla
  • 6,775
  • 11
  • 53
  • 79
Jonnycake
  • 568
  • 4
  • 10
1

This work if you have an Apache server

PHP Code:

$headers = apache_request_headers();

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

Result:

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

Here is an easy way to do it.

// echo get_header('X-Requested-With');
function get_header($field) {
    $headers = headers_list();
    foreach ($headers as $header) {
        list($key, $value) = preg_split('/:\s*/', $header);
        if ($key == $field)
            return $value;
    }
}
kehers
  • 4,076
  • 3
  • 30
  • 31
  • Isn't that headers that get sent out? – CMCDragonkai Oct 08 '13 at 07:13
  • @CMCDragonkai No. "headers_list() will return a list of headers to be sent to the browser/client" - http://www.php.net/manual/en/function.headers-list.php – kehers Oct 08 '13 at 10:19
  • @OpeyemiObembe so yes. – Michael Leaney Oct 09 '13 at 00:32
  • 3
    Yea. That's what I meant. – CMCDragonkai Oct 09 '13 at 01:22
  • @MivhaelLeaney Yes - headers to be sent TO the client, i.e incoming headers, not headers to be sent out from the client. – kehers Oct 11 '13 at 19:03
  • 1
    The question is asking for headers sent to the server. They are the request headers. – CMCDragonkai Oct 15 '13 at 09:11
  • 1
    The source and destination, when using terms like "sent", "out", "to", "from", is relative to the context in which those terms are used. In this answer, the example shows PHP running on the *server*. And, the OP's question also referenced server-side PHP. Therefore, @CMCDragonkai and MichaelLeany are correct. This is a bad answer. "sent out", in this context, means "HTTP response headers sent from the server". kehers is commenting from the point-of-view of the client, but both his answer and the OP's question are from the point of view of the server. – Evan de la Cruz Mar 23 '16 at 17:44
0

This small PHP snippet can be helpful to you:

<?php
foreach($_SERVER as $key => $value){
echo '$_SERVER["'.$key.'"] = '.$value."<br />";
}
?>
Technolust
  • 162
  • 2
  • 8
0
function getCustomHeaders()
{
    $headers = array();
    foreach($_SERVER as $key => $value)
    {
        if(preg_match("/^HTTP_X_/", $key))
            $headers[$key] = $value;
    }
    return $headers;
}

I use this function to get the custom headers, if the header starts from "HTTP_X_" we push in the array :)

ZiTAL
  • 3,466
  • 8
  • 35
  • 50
0

PHP 7: Null Coalesce Operator

//$http = 'SCRIPT_NAME';
$http = 'X_REQUESTED_WITH';
$http = strtoupper($http);
$header = $_SERVER['HTTP_'.$http] ?? $_SERVER[$http] ?? NULL;

if(is_null($header)){
    die($http. ' Not Found');
}
echo $header;
Scaffold
  • 587
  • 6
  • 14
-2

Below code works for me to get any specific data submitted in the header

foreach (getallheaders() as $name => $value) {
   if($name=='Authorization') //here you can search by name
   $Authorization= $value ;
}