About the Author

Chris Shiflett

Hi, I’m Chris: web craftsman, community leader, husband, father, and partner at Fictive Kin.


A question was asked on the New York PHP mailing list concerning $_SERVER['SERVER_NAME'] and $_SERVER['HTTP_HOST']:

Aren't these the same thing?

There were several informative replies within the first few minutes, but there's more to this question than most people realize. In fact, I'm reminded of a blog post from Zeev last year where he warns against $_SERVER['SERVER_NAME']:

And how about $SERVER_NAME ($_SERVER['SERVER_NAME']), which actually depends on the Host header sent by the remote user, and can therefore be spoofed under certain circumstances?

Rasmus disagreed:

No, that isn't affected by the Host header.

If you want to get to the bottom of this debate, follow along with this brief example. Imagine your web server has a default host set up as follows:

UseCanonicalName Off
ServerName example.org

The ServerName directive might seem like the only thing that affects $_SERVER['SERVER_NAME'], but is this a safe assumption? To determine what affect the Host header has, if any, create an index.php in the document root of the default host with the following code:

echo "HTTP_HOST [{$_SERVER['HTTP_HOST']}]\n"; 

You can test several different values for Host easily enough with telnet:

telnet example.org 80

Here are a few of my tests and corresponding results. For each test, I show the exact request and the content of the response, so please feel free to test this yourself.

1. No Host, HTTP/1.0


GET / HTTP/1.0


SERVER_NAME [example.org]

With no Host, SERVER_NAME is the value from Apache.

2. Empty Host, HTTP/1.0


GET / HTTP/1.0



With an empty Host, SERVER_NAME is empty.

3. Empty Host, HTTP/1.1


GET / HTTP/1.1



With an empty Host, SERVER_NAME is also empty with HTTP/1.1.

4. XSS Host, HTTP/1.1


GET / HTTP/1.1
Host: <script>alert('XSS')</script>


HTTP_HOST [<script>alert('XSS')</script>] 
SERVER_NAME [&lt;script&gt;alert('XSS')&lt;/script&gt;]

With a non-empty Host, SERVER_NAME is the HTML-escaped host value.

5. SQL Injection Host, HTTP/1.1


GET / HTTP/1.1
Host: chris' --


HTTP_HOST [chris' --] 
SERVER_NAME [chris' --]

As you can see by the results, Zeev was right. Under certain circumstances, the Host header can affect $_SERVER['SERVER_NAME']. The ServerName directive is used when the Host header is absent, and apparently $_SERVER['SERVER_NAME'] is escaped with something like htmlentities().

Sometimes, it's hard to tell whether a particular element in $_SERVER can be affected by the HTTP request (ask Sean about PHP_SELF), so I find it easier to treat everything from $_SERVER just as if it were something like $_GET or $_POST.

As Dan Cech points out, the UseCanonicalName directive affects this behavior. From httpd.conf, "With this setting off, Apache will use the hostname:port that the client supplied, when possible. This also affects SERVER_NAME and SERVER_PORT in CGI scripts."

About this post

SERVER_NAME Versus HTTP_HOST was posted on Thu, 16 Mar 2006. If you liked it, follow me on Twitter or share:


1.S said:

I agree that it's risky to treat anything in $_SERVER as anything but tainted, but is there really a way to exploit this?

I can't think of one. You'd have to REALLY mangle a user's DNS to make this work.

The closest I can think of is a site that posts to $_SERVER['SERVER_NAME']/script.php.. and create a host that points there. You'd have to somehow trick the user's browser into using a different IP on the 2nd request (the post).

Am I just missing something? I noticed that a LOT of sites happily embed the $_SERVER['HTTP_USER_AGENT'] without escaping, but it's nearly impossible to trick a user into modifying his/her USER_AGENT string to something exploitable. Isn't this the same?


Thu, 16 Mar 2006 at 21:22:25 GMT Link

2.dbevfat said:

/Usually/ this would be quite hard to explot, because on named hosts you're not even served with the right script if you don't post a proper Host.

Fri, 17 Mar 2006 at 00:05:42 GMT Link

3.S said:

Just talked to Chris.. and cleared a thing or two up.

As far as I can tell, the only real risk lies in a scenario where the user's SERVER_NAME is stored on the server and replayed (without escaping) to a future user.

This is a legit problem.

Same goes for USER_AGENT.

I was thinking first-person.. and that's not possible (again, AFAIK).


Fri, 17 Mar 2006 at 01:34:41 GMT Link

4.DerelictMan said:

I could see potential for an exploit in a situation where someone is using a wildcard DNS setup (http://en.wikipedia.org/wiki/Wildcard_DNS_entry) as an easy way to support "vanity domains" for users of an application, especially if no validation is done on the host name in the request headers, and that information is used to generate href attributes. Of course, this isn't exactly a common setup, but still...

Fri, 17 Mar 2006 at 01:51:13 GMT Link

5.Chris Shiflett said:

I should have been clearer, but there are actually lots of ways to exploit this sort of thing, and it depends on how you use the tainted data. I think it's better to realize that it's never safe to rely upon an attacker's lack of creativity and err on the side of caution by never doing anything important with tainted data.

I do want to point out that no DNS trickery is required. For example, if I want to exploit your site, and it has an IP of, I can simply do this:

telnet 80

Once it's connected, I enter my attack:

GET / HTTP/1.1

Host: <script>alert('XSS')

Now, if you only echo it, I'm attacking myself, but hopefully the basic idea is clear. More clever attacks would use JavaScript to trick a victim into sending such a request, combing a few different types of attacks.

Fri, 17 Mar 2006 at 02:01:23 GMT Link

6.DerelcitMan said:

Agreed with the "never do anything important with tainted data" part. However, speaking for me personally, your specific example will never affect me because I use name-based virtual hosts on all my servers. (dbevfat mentioned this in a previous comment.) Since "<script>alert('XSS')" isn't a valid name for any of my virtual hosts, Apache would route a request such as that to the default host for my IP address. In my case, the default IP-only host is basically a black hole; I serve up an empty response and I dump the request into a log file that is basically ignored. I set this up LONG ago mainly as a way to keep the noise from automated worm exploit attempts out of my actual log files.

When I mentioned the whole wildcard DNS thing, I guess I was just assuming that most people were like me and used only name-based hosts, and that the need to support a wildcard DNS setup would be the only legitimate reason to actually place a working application on the default host for a given IP address. I've been using the "default host > /dev/null" approach for so many years that I forgot it isn't necessarily what everyone else does. :) I would definitely expect most people on shared hosting to be on a name-based host though.

At any rate, though, you're right, it will never hurt to treat everything in $_SERVER as tainted, no matter what your setup.

Fri, 17 Mar 2006 at 06:50:26 GMT Link

7.Bigtree said:

There's one important difference that's not mentioned here. If Apache is configured so that a virtual host has domain aliases, and a page is requested for an alias, the requested domain will be in HTTP_HOST. The SERVER_NAME variable will hold the virtual host's main name.

Fri, 17 Mar 2006 at 08:11:52 GMT Link

8.test said:


Test :-)

Fri, 17 Mar 2006 at 12:15:39 GMT Link

9.Mike Willbanks said:

As said before, this would only work on a website that uses an ip for itself, which any ecommerce website with an SSL certificate would.

My only and primative usage of SERVER_NAME is to feed my urls. But I also take care to do strip_tags and htmlspecialchars on all of the $_SERVER related data. So with that I would suppose that helps out a great deal. Since this is only used for output I would assume that there is not going to be many issues.

I suppose an additional check for server name could be a whitelist array:

$allowed_servername = array('host.this.com', 'this.host.com');

if (!in_array($_SERVER['SERVER_NAME'], $allowed_servername)) {

header('HTTP/1.0 400 Bad Request');



Fri, 17 Mar 2006 at 16:04:38 GMT Link

10.Leonid said:

It's very usefull! Thanks, Chris!

Sat, 15 Apr 2006 at 14:06:24 GMT Link

11.Taras said:

So, if this BUG (as I thinks it's realyy bug that SERVER_NAME depends on headers ) will be fixed in future versions of PHP?

Mon, 14 Aug 2006 at 05:05:19 GMT Link

12.Paul said:

Changing SERVER_NAME behavior could break a lot of scripts. However, its name, and the fact that it is stored in a global array called "_SERVER" leads one to believe it is data that comes from the server and not a mix of the server and the HTTP request. I will would suggest that future versions of PHP should not populate any user touchable data in variables called "_SERVER".

Maybe there should be a "_HEADER" global array where [insert protocol here] headers could be stored and retrieved.

Wed, 27 Jun 2007 at 14:02:53 GMT Link

13.Felix said:

it depends on UseCanonicalName


Wed, 10 Oct 2007 at 14:24:57 GMT Link

14.Thibaut Allender said:

Very interesting but as pointed out by DerelcitMan, it's practically impossible to exploit because 95% websites are using vhost directives to be served.

I personally only use this kind of variable to test of which website the user is (in case of wildcards) or to write correct header('Location: ...') redirects.

No big deal then.

Tue, 24 Nov 2009 at 08:22:13 GMT Link

15.Alberto Lepe said:

If you use only one hostname with several subdomains (using wildcard), these can be done to reduce the risk:

1) Set "UseCanonicalName On" and set your "ServerName".

2) Ensure that $_SERVER["HTTP_HOST"] is not empty and does not contains any unexpected characters, something like:


3) Check whether the value of $_SERVER["HTTP_HOST"] is contained in $_SERVER["SERVER_NAME"], for example: subdomain.example.com in example.com


I guess that this is enough. Or I am missing something?

Mon, 04 Oct 2010 at 02:45:40 GMT Link

16.Nicolas Froidure said:

@Alberto Lepe : use


instead ;)

Fri, 05 Nov 2010 at 09:09:25 GMT Link

17.Julio Potier said:


Each time i try to hack the host header in a request, each time the server response is "400 Bad Request", tested on 8 differents websites hosting.

So, what is the setting that allow me to do this kind of request? It seems that @thibaut said about VHOST is the thing but not sure, and if yes, again, what is the configuration to do your tests?


Sun, 19 Apr 2015 at 23:58:28 GMT Link

18.Chris Shiflett said:

Julio, can you share a screen shot or a paste or something that shows what's happening?

Also, this post might be helpful:


Fri, 15 May 2015 at 21:44:26 GMT Link

Hello! What’s your name?

Want to comment? Please connect with Twitter to join the discussion.