What is the difference between HTTP_HOST and SERVER_NAME in PHP?
Asked Answered
N

11

578

What is the difference between $_SERVER['HTTP_HOST'] and $_SERVER['SERVER_NAME'] in PHP?

When would you consider using one over the other and why?

Neuropsychiatry answered 19/2, 2010 at 15:29 Comment(3)
"I normally go for HTTP_HOST, so that the user stays on the exact host name they started on. For example if I have the same site on a .com and .org domain, I don't want to send someone from .org to .com, particularly if they might have login tokens on .org that they'd lose if sent to the other domain." - This and some other interesting points from #1460239Vesicant
@Yarin, Don't forget to whitelist-verify the results of HTTP_HOST. Otherwise an attacker can put in any value in the HTTP's Host: request and make the server accept it.Lykins
Beginners: This question is referring to values typically obtained via $_SERVER['HTTP_HOST'] or $_SERVER['SERVER_NAME']Izzard
S
823

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 the ServerName, 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 the ServerName 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!

Spyglass answered 19/2, 2010 at 15:32 Comment(10)
Okay, this solves my problem, which is unrelated to the OP but relevant. I was very concerned about security issues by using anything that a browser could supply. This answer was a HUGE help. Thank you for taking the time to put it together.Sapotaceous
Why do you say HTTP_HOST is not reliable? Yes it is supplied by the user, but if the user give some bogus value, your server config would automatically return 503 and your PHP script wouldn't even be run!Lykins
@Pacerier: at the time of writing this answer, it did not. Versions are mentioned in the answer. I don't keep up with PHP anymore, so I can't say if it has indeed changed in a newer version.Spyglass
An easy way to trick Apache from WinXP is to add a line to 'hosts' file stating that the IP of the server is assigned to another domain, like this: "127.0.0.1 mydomain.com". I've used this a lot of times to show a local website tricking my audience to think I've got internet connection and site loaded really fast. You could go the other way, and trick Apache to think it's running locally, with "173.194.41.5 localhost", so you should never trust SERVER_NAME fully unless you are sure your Apache is well configured.Button
I just want to add, that NGINX+PHP-FPM returns the value set by the server_name directive. Especially if no server_name is set also _SERVER["SERVER_NAME"] will be empty.Feminize
does SERVER_NAME also captures the values of ServerAlias set in VirtualHost file ?Mellon
@BalusC, No that's not my point. I'm referring to your last paragraph where you say SERVER_NAME is more reliable. That's not true. Both are equally reliable and both depends on the server config. Indeed, HTTP_HOST may actually be more reliable because its behavior is more well-defined than $_SERVER['SERVER_NAME'].Lykins
I do not experience the phenomenon you refer to with Apache 2.4.16 and PHP 5.6.20, but I will do some more testing. That which I have done already does not coincide with bobince's past finding that "PHP would always return HTTP_HOST's value for SERVER_NAME." Specifically, when setting up SSL, I had to make sure that SERVER_NAME was set correctly or the whole thing would fall apart. Relying on the assumption of HTTP 1.1 user agents questionable.Crary
If your configuration is like <VirtualHost *.example.com>, clients accessed your site as admin.example.com and site.example.com, on PHP+Apache you get domain names in SERVER_NAME, but in PHP+Nginx you get *.example.com in SERVER_NAME. If you do anything programmatically depending on that, you need to consider this.Controller
The summary is a bit misleading. You first wrote that it depends what you are using it for. When you need it as a redirect url for a single sign on request you might need the host the client requested and not the server name. And it is not necessary unreliable (see my answer)Furriery
T
74

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.

Transsonic answered 19/2, 2010 at 15:35 Comment(5)
Yes but a request asking for a HTTP_HOST value of www.stackoverflow.com would be rejected by most HTTP servers up front so the PHP script wouldn't even see the request!Lykins
@Lykins true, but not always if the server is not properly configured.Transsonic
As mentioned in BalusC's post, when you access an Apache virtualhost by IP, both of these variables contain the IP (by default), not the actual server name. You have to use UseCanonicalName on in httpd.conf to force SERVER_NAME to be the actual server name.Dactylic
@Pekka웃, If the server is not properly configured, $_SERVER['SERVER_NAME'] wouldn't work as well. A badly configured server will set$_SERVER['SERVER_NAME'] based on the value of the client's Host: request. Both are equal.Lykins
Good answer, but I would not assume virtual hosting.Crary
D
63

As I mentioned in this answer, if the server runs on a port other than 80 (as might be common on a development/intranet machine) then HTTP_HOST contains the port, while SERVER_NAME does not.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(At least that's what I've noticed in Apache port-based virtualhosts)

Note that HTTP_HOST does not contain :443 when running on HTTPS (unless you're running on a non-standard port, which I haven't tested).

As others have noted, the two also differ when using IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Dactylic answered 20/8, 2013 at 0:22 Comment(1)
When will they fix this insidious behavior?Lykins
R
30

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.

Rohde answered 27/2, 2012 at 1:24 Comment(1)
SiteGround, in their inhouse http to https redirect code, use https://%{SERVER_NAME}%{REQUEST_URI}Pluralize
B
8

If you want to check through a server.php or whatever, you want to call it with the following:

<?php
    phpinfo(INFO_VARIABLES);
?>

or

<?php
    header("Content-type: text/plain");

    print_r($_SERVER);
?>

Then access it with all the valid URLs for your site and check out the difference.

Bartlett answered 24/5, 2010 at 7:58 Comment(0)
M
6

Depends what I want to find out. SERVER_NAME is the host name of the server, whilst HTTP_HOST is the virtual host that the client connected to.

Marquesan answered 19/2, 2010 at 15:31 Comment(3)
Not exactly true Rowland, SERVER_NAME is usually the name of the VirtualHost, not the server itself. And in Apache, SERVER_NAME is often populated with the same value as HTTP_HOST (see BalusC's answer).Dactylic
@Simon, Since mosts hosts are now VirtualHost, what would you mean by the name of "the server itself"?Lykins
If you are running a virtual private server (VPS) with one website, you do not need to assume that SERVER_NAME applies to a virtual host. However, one can still use a virtual host setup for one site. Many people use shared hosting, so I see your point.Crary
G
4

$_SERVER['SERVER_NAME'] is based on your web servers configuration. $_SERVER['HTTP_HOST'] is based on the request from the client.

Gaby answered 14/3, 2020 at 5:16 Comment(0)
N
2

It took me a while to understand what people meant by 'SERVER_NAME is more reliable'. I use a shared server and does not have access to virtual host directives. So, I use mod_rewrite in .htaccess to map different HTTP_HOSTs to different directories. In that case, it is HTTP_HOST that is meaningful.

The situation is similar if one uses name-based virtual hosts: the ServerName directive within a virtual host simply says which hostname will be mapped to this virtual host. The bottom line is that, in both cases, the hostname provided by the client during the request (HTTP_HOST), must be matched with a name within the server, which is itself mapped to a directory. Whether the mapping is done with virtual host directives or with htaccess mod_rewrite rules is secondary here. In these cases, HTTP_HOST will be the same as SERVER_NAME. I am glad that Apache is configured that way.

However, the situation is different with IP-based virtual hosts. In this case and only in this case, SERVER_NAME and HTTP_HOST can be different, because now the client selects the server by the IP, not by the name. Indeed, there might be special configurations where this is important.

So, starting from now, I will use SERVER_NAME, just in case my code is ported in these special configurations.

Necrology answered 16/2, 2012 at 21:8 Comment(1)
then your code might not work on environments where SERVER_NAME is not the Host the client should request (for example an internal host for the load balancer)Furriery
C
2

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).

Crary answered 9/3, 2017 at 5:20 Comment(3)
HTTP_HOST does not have to be equal to SERVER_NAME to be valid. That depends on your setupFurriery
@Furriery "But, just because both may be set does not guarantee they will be equal"Crary
but you are suggesting that they should be equal to have a valid requestFurriery
F
1

I'm unhappy with all the answers. Some of them are correct but don't tell the whole story and don't make the issue clear.

Regardless of which http server you use the HTTP_HOST should contain the original value sent from the client in the HTTP header Host. And therefore it is user controlled data that should not be trusted.

The variable SERVER_NAME is something that is configured in your server configuration and might not lead to the correct URL. For example there could be a revers proxy in front of your web servers and SERVER_NAME is server1 and server2 but you don't want to redirect your user to server1 but to the user friendly host.

As such the HTTP_HOST is the more reliable variable as you might want the host the client requested to reach your PHP application. You don't need to compare them to make sure this is a valid value (they are not necessarily equal). There are two ways to ensure this value is valid:

  1. compare the value to a list of valid values (you need to know the valid values)
  2. ensure your web server returns an error if the value is not correct

The first one is easy to understand but can be problematic in real scenarios (in your dev environment it is this, on staging it is this, on production that... etc.). That means you need to know in PHP what is valid for this environment.

The second one is something for the server configuration: VirtualHost is a concept for http servers to deliver multiple websites from the same server. As the virtual host is chosen by the http header Host (case insensitive) the client can not control which virtual host is used unless modifying the host. When only one virtual host is configured every value will use this virtual host. You need to configure a second virtual host that is the default (if no other virtual host matches) and always returns an error (for example "not found" or "forbidden").

Furriery answered 5/10, 2022 at 9:17 Comment(0)
N
0

As balusC said SERVER_NAME is not reliable and can be changed in apache config , server name config of server and firewall that can be between you and server.

Following function always return real host (user typed host) without port and it's almost reliable:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
Noeminoesis answered 20/5, 2016 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.