74

All over the Internet, included even here at Stack Overflow, people state that a good way to check if a request is AJAX or not is to do the following:

if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) {...}

However, I don't see $_SERVER['HTTP_X_REQUESTED_WITH'] in the official PHP documentation

And when I try to do the following:

echo $_SERVER['HTTP_X_REQUESTED_WITH'];

Nothing is outputted.

Am I doing something wrong? Because I'd really like to be able to use $_SERVER['HTTP_X_REQUESTED_WITH'] if it's available.

brasofilo
  • 25,496
  • 15
  • 91
  • 179
Hank
  • 1,151
  • 2
  • 10
  • 9

11 Answers11

66

The variables in $_SERVER are not really part of PHP, which is why you won't find them in the PHP documentation. They are prepared by the Web server which passes them on to the scripting language.

As far as I know, the X-Requested-With is sent by the Ajax functions of most major Frameworks but not all (Dojo, for example, added it only two years ago: #5801). As such, and taking into considerations @bobince' comments, it's safe to say it's not generally a 100% reliable method to determine whether a request is an AJAX request or not.

The only 100% secure way is to send a pre-defined flag (e.g. a GET variable) along with the request and for the receiving page to check for the presence of that flag.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • 4
    I don't think it's a reliable method either. Firewall/proxy tools may futz with it, and you could be returning the wrong response to proxied users if you don't combine use of this header with `Vary: X-Requested-With` in the response... which in turn screws up cacheing in IE. Lots of people do use `X-Requested-With`, but I think it's a terrible idea. Better to pass a flag in the query parameters to signal you want an XMLHttp-style (or JSON) response. – bobince Apr 05 '10 at 15:45
  • 1
    @bobince is it really that likely that a firewall is going to mess with it? I'm building a web app which is going to be used mainly from one location and occasionally remotely. It would be really nice to be able to abstract away from the type of request so my controller could return a whole page or an html snippet depending on whether it was an ajax request... – rgvcorley Jun 12 '12 at 21:15
  • @rgvcorley: a typical modern proxy won't arbitrarily remove headers (there are some nasty creaky broken ones used by mobile networks though). But the `Vary` problem *will* screw with you if you have anything but `nocache` responses. It's not worth the hassle, for the very minor improvement in prettiness of a header; go with a simple parameter. – bobince Jun 13 '12 at 22:53
  • Thanks for the reply - yeah you're right. I guess for that little extra effort of a GET variable you get the benefit of knowing that it's guaranteed to work! – rgvcorley Jun 14 '12 at 16:13
  • 6
    Dojo is NOT a _framework_!! It's a library. And jQuery is a **library** too. Unfortunately, the word "framework" evolved into a buzzword that's often improperly used. – Sk8erPeter Apr 26 '13 at 00:52
27

don't forget that you can easily spoof any header with cURL like so

curl_setopt($ch,CURLOPT_HTTPHEADER,array("X-Requested-With : XMLHttpRequest"));
19

$_SERVER keys that start with HTTP_ are generated from HTTP request headers. In this case, the X-Requested-With header.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • So do all browsers set this header? Also, how is this header set if it's a AJAX call? – Hank Apr 05 '10 at 15:42
  • XHR can send arbitrary headers in a request. – Ignacio Vazquez-Abrams Apr 05 '10 at 15:43
  • @Hank What's wrong with AJAX call? You think it uses not HTTP request? Which one then? – Your Common Sense Apr 05 '10 at 15:45
  • 1
    I'm trying to prevent people from directly access my web service but allow XHR – Hank Apr 05 '10 at 15:49
  • 9
    @Hank telling apart XHR from "normal" requests is absolutely, fundamentally useless as a security measure, as everyone can simulate a XHRequest without problems. You need to protect your Ajax scripts using solid authentication measures (or whatever else you already have in place for your main application). – Pekka Apr 05 '10 at 16:24
18

This header is a standardization-in-progress from all of the AJAX libraries out there.

It won't be documented in the php documentation per-se, but rather in the different AJAX libraries that set this header. Common libraries do sent this header: jQuery, Mojo, Prototype, ...

Usually these library will set the header using

xhrobj.setRequestHeader("X-Requested-With", "XMLHttpRequest");
brasofilo
  • 25,496
  • 15
  • 91
  • 179
Jerome WAGNER
  • 21,986
  • 8
  • 62
  • 77
6

Here's a quick function with example usage:

function isXmlHttpRequest()
{
    $header = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? $_SERVER['HTTP_X_REQUESTED_WITH'] : null;
    return ($header === 'XMLHttpRequest');
}

// example - checking our active call
if(!isXmlHttpRequest())
{
    echo 'Not an ajax request';
}
else
{
    echo 'is an ajax request';
}
tfont
  • 10,891
  • 7
  • 56
  • 52
  • 2
    This doesn't answer the question whatsoever. It is code snippets like this that the OP is asking about. S/he wants to know _why_ this works, i.e. where this `$_SERVER['HTTP_X_REQUESTED_WITH']` comes from, where it's set, and if/why we can really rely on it. – osullic Nov 14 '19 at 11:10
  • Even though I agree with oscullic, I cane here looking for a reason for which I was getting an error. This seems to have solved it. – PythonSnek Mar 14 '21 at 21:43
3
echo $_SERVER['HTTP_X_REQUESTED_WITH'];

What'd you expect from such a code? Assume you're running it directly from the browser, not using AJAX request. So, how come this header could be set?

Well the Answer to the Ultimate Question of Life, the Universe, and Everything - an HTTP sniffer! Get yourself one and forget of printing $_SERVER variable.

Firebug has one, or you may want to use Fiddler HTTP proxy or LiveHTTPHeaders Mozilla plugin. I'm bored to make links but it easily googled.

So, with HTTP sniffer you can be sure of any HTTP header ever.

Note that you can't prevent any "direct access" by using XHR, as every HTTP request to your server is already "direct".

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
3

You have to set it specifically in your ajax request object (that is if you are not using a framework like jQuery), but core Javascript; like so:

xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

Where xhr is your request object.

Then, PHP will now receive and set it in the global variable $_SERVER like so:

$_SERVER['HTTP_X_REQUESTED_WITH']

Otherwise $_SERVER['HTTP_X_REQUESTED_WITH'] will always be null.

Note: In your javascript, Please make sure you set headers after the request is open. I mean after xhr.open() method.

Nicholas Mberev
  • 1,563
  • 1
  • 14
  • 14
1

I agree Pekka. There is no reliable native method between front side and back side that can auto-detect if a client is really calling an endpoint using AJAX.

For my own use, I have few main ways to check if a client is requesting one of my endpoint:

  1. I can use HTTP_X_REQUESTED_WITH when I'm not in cross domain context.

  2. instead of checking "X-requested-with", I'm checking $_SERVER['HTTP_ORIGIN'] (that is sent from AJAX request) intending to handle cross domain permissions. Most of time, the main reason why I'm checking if a request is an AJAX request, is especially because of cross domain permissions, using this PHP code: header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']); // If this "HTTP_ORIGIN" is in my white list

  3. my APIs expect from the client to explicit, in few cases, the datatype (JSON, HTML etc.) into a GET or a POST var. For example, I check if $_REQUEST['ajax'] is not empty or equal to an expected value.

FragBis
  • 73
  • 6
1

You can also blame some browser bugs - see this question and its solution for Firefox

Firefox does not preserve custom headers during Ajax request redirect: an ASP.NET MVC solution

IE also having caching issue which is more serious then detection of request method.

You anyway needs to add cache busters to avoid caching, so why not use another flag to specify the ajax call - or more better you can use different URL like http://ajax.mysite.com/endpoint/sevice?params

Community
  • 1
  • 1
EGL 2-101
  • 1,203
  • 8
  • 17
0

The best solution to make sure if an HTTP request is truly sent via AJAX is using SESSION checking , you send session_id in a get parameter and you check this session if it's allowed or not !

0
$headers = apache_request_headers();
$is_ajax = (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest');
revoke
  • 529
  • 4
  • 9