SERVER_NAME Versus HTTP_HOST
16 Mar 2006A 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 theHost
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:
<?php
echo "HTTP_HOST [{$_SERVER['HTTP_HOST']}]\n";
echo "SERVER_NAME [{$_SERVER['SERVER_NAME']}]";
?>
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
Request:
GET / HTTP/1.0
Result:
HTTP_HOST []
SERVER_NAME [example.org]
With no Host
, SERVER_NAME
is the value from Apache.
2. Empty Host
, HTTP/1.0
Request:
GET / HTTP/1.0
Host:
Result:
HTTP_HOST []
SERVER_NAME []
With an empty Host
, SERVER_NAME
is empty.
3. Empty Host
, HTTP/1.1
Request:
GET / HTTP/1.1
Host:
Result:
HTTP_HOST []
SERVER_NAME []
With an empty Host
, SERVER_NAME
is also empty with HTTP/1.1.
4. XSS Host
, HTTP/1.1
Request:
GET / HTTP/1.1
Host: <script>alert('XSS')</script>
Result:
HTTP_HOST [<script>alert('XSS')</script>]
SERVER_NAME [<script>alert('XSS')</script>]
With a non-empty Host
, SERVER_NAME
is the HTML-escaped host value.
5. SQL Injection Host
, HTTP/1.1
Request:
GET / HTTP/1.1
Host: chris' --
Result:
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. Fromhttpd.conf
, "With this setting off, Apache will use thehostname:port
that the client supplied, when possible. This also affectsSERVER_NAME
andSERVER_PORT
in CGI scripts."