What is the difference between HTTP_HOST
and SERVER_NAME
in PHP?
where:
HTTP_HOST
=== $_SERVER[\'HTTP_HOST\'
The HTTP_HOST
is obtained from the HTTP request header and this is what the client actually used as "target host" of the request. The SERVER_NAME
is defined in server config. Which one to use depends on what you need it for. You should now however realize that the one is a client-controlled value which may thus not be reliable for use in business logic and the other is a server-controlled value which is more reliable. You however need to ensure that the webserver in question has the SERVER_NAME
correctly configured. Taking Apache HTTPD as an example, here's an extract from its documentation:
If no
ServerName
is specified, then the server attempts to deduce the hostname by performing a reverse lookup on the IP address. If no port is specified in theServerName
, then the server will use the port from the incoming request. For optimal reliability and predictability, you should specify an explicit hostname and port using theServerName
directive.
Update: after checking the answer of Pekka on your question which contains a link to bobince's answer that PHP would always return HTTP_HOST
's value for SERVER_NAME
, which goes against my own PHP 4.x + Apache HTTPD 1.2.x experiences from a couple of years ago, I blew some dust from my current XAMPP environment on Windows XP (Apache HTTPD 2.2.1 with PHP 5.2.8), started it, created a PHP page which prints the both values, created a Java test application using URLConnection to modify the Host
header and tests taught me that this is indeed (incorrectly) the case.
After first suspecting PHP and digging in some PHP bug reports regarding the subject, I learned that the root of the problem is in web server used, that it incorrectly returned HTTP Host
header when SERVER_NAME
was requested. So I dug into Apache HTTPD bug reports using various keywords regarding the subject and I finally found a related bug. This behaviour was introduced since around Apache HTTPD 1.3. You need to set UseCanonicalName directive to on
in the <VirtualHost>
entry of the ServerName
in httpd.conf
(also check the warning at the bottom of the document!).
<VirtualHost *>
ServerName example.com
UseCanonicalName on
</VirtualHost>
This worked for me.
Summarized, SERVER_NAME
is more reliable, but you're dependent on the server config!
Assuming one has a simple setup (CentOS 7, Apache 2.4.x, and PHP 5.6.20) and only one website (not assuming virtual hosting) ...
In the PHP sense, $_SERVER['SERVER_NAME']
is an element PHP registers in the $_SERVER
superglobal based on your Apache configuration (**ServerName**
directive with UseCanonicalName On
) in httpd.conf (be it from an included virtual host configuration file, whatever, etc ...). HTTP_HOST is derived from the HTTP host
header. Treat this as user input. Filter and validate before using.
Here is an example of where I use $_SERVER['SERVER_NAME']
as the basis for a comparison. The following method is from a concrete child class I made named ServerValidator
(child of Validator
). ServerValidator
checks six or seven elements in $_SERVER before using them.
In determining if the HTTP request is POST, I use this method.
public function isPOST()
{
return (($this->requestMethod === 'POST') && // Ignore
$this->hasTokenTimeLeft() && // Ignore
$this->hasSameGETandPOSTIdentities() && // Ingore
($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}
By the time this method is called, all filtering and validating of relevant $_SERVER elements would have occurred (and relevant properties set).
The line ...
($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')
... checks that the $_SERVER['HTTP_HOST']
value (ultimately derived from the requested host
HTTP header) matches $_SERVER['SERVER_NAME']
.
Now, I am using superglobal speak to explain my example, but that is just because some people are unfamiliar with INPUT_GET
, INPUT_POST
, and INPUT_SERVER
in regards to filter_input_array().
The bottom line is, I do not handle POST requests on my server unless all four conditions are met. Hence, in terms of POST requests, failure to provide an HTTP host
header (presence tested for earlier) spells doom for strict HTTP 1.0 browsers. Moreover, the requested host must match the value for ServerName
in the httpd.conf, and, by extention, the value for $_SERVER('SERVER_NAME')
in the $_SERVER
superglobal. Again, I would be using INPUT_SERVER
with the PHP filter functions, but you catch my drift.
Keep in mind that Apache frequently uses ServerName
in standard redirects (such as leaving the trailing slash off a URL: Example, http://www.example.com becoming http://www.example.com/), even if you are not using URL rewriting.
I use $_SERVER['SERVER_NAME']
as the standard, not $_SERVER['HTTP_HOST']
. There is a lot of back and forth on this issue. $_SERVER['HTTP_HOST']
could be empty, so this should not be the basis for creating code conventions such as my public method above. But, just because both may be set does not guarantee they will be equal. Testing is the best way to know for sure (bearing in mind Apache version and PHP version).
HTTP_HOST
is the target host sent by the client. It can be manipulated freely by the user. It's no problem to send a request to your site asking for a HTTP_HOST
value of www.stackoverflow.com
.
SERVER_NAME
comes from the server's VirtualHost
definition and is therefore considered more reliable. It can, however, also be manipulated from outside under certain conditions related to how your web server is set up: See this This SO question that deals with the security aspects of both variations.
You shouldn't rely on either to be safe. That said, what to use really depends on what you want to do. If you want to determine which domain your script is running on, you can safely use HTTP_HOST
as long as invalid values coming from a malicious user can't break anything.
Please note that if you want to use IPv6, you probably want to use HTTP_HOST
rather than SERVER_NAME
. If you enter http://[::1]/
the environment variables will be the following:
HTTP_HOST = [::1]
SERVER_NAME = ::1
This means, that if you do a mod_rewrite for example, you might get a nasty result. Example for a SSL redirect:
# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/
# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/
This applies ONLY if you access the server without an hostname.