A smartphone showing an error message with a sad face and the message “Unable to connect to the proxy server”. 🅭

How to use same Host header for origin and proxy in Apache mod_proxy

An origin server should use the same hostname as any [caching] reverse-proxy gateways that are made available to the public web. Otherwise, you can end up leaking the hostname or IP address of the origin server, and get unexpected results in error pages and interpreted output.

The solution discussed in this article is specifically for the Apache Web Server’s mod_proxy module, but the concepts apply to other reverse proxy servers such as Nginx and Traffic Server as well.

The configuration discussed in this article can be applied on a Server or VirtualHost blocks in your Apache configuration file.

A tale of two hosts responding to different names

Let us assume you’ve got a reverse proxy setup in front of your backend origin server. Both are running Apache, and the reverse proxy gateway responds to the hostnames example.com and www.example.com, and the backend origin server responds to wphost1.example.com. A simple configuration for this could look like:

Proxy:

ServerName "www.example.com"
ServerAlias "example.com"
ProxyPass "/" "https://wphost1.example.com/"

Origin:

ServerName "wphost1.example.com"

In this setup, requests to the server at www.example.com would be transparently relayed to the server at wphost1.example.com. This simple setup is the starting point for a caching proxy that can work as a load balancing and geographical distribution of server load — see mod_proxy documentation for details.

Incoming requests to the proxy will arrive with the HTTP Host header set to either Host: example.com or Host: www.example.com. The relayed requests sent from the proxy to the origin server will be rewritten as Host: wphost1.example.com, and also gets a second header called X-Forwarded-Host: www.example.com.

The origin server is thus not aware that the preferred hostname is www.example.com, and the request will be processed as it was sent to wphost1.example.com instead. Error pages and interpreters (like PHP) will be configured to use the processed hostname and be unaware of the X-Forwarded-Host header.

Same name, an alias, and name canonicalization

Let us next look at a bit more complex setup where both the proxy and origin will treat requests as they were sent to www.example.com:

Proxy:

ServerName "www.example.com"
ServerAlias "example.com"
UseCanonicalName on
ProxyPreserveHost on
ProxyPass "/" "https://wphost1.example.com/"

Origin:

ServerName "www.example.com"
ServerAlias "wphost1.example.com"
UseCanonicalName on

The configuration options are named kind of self-explanatory, but I’ll go through each of them, in turn, to explain how they work together.

The reverse proxy server is made aware that the ServerName is the canonical name of the server and all requests, even to the server’s IP, is to be treated as if they were made to the canonical name. This behavior is managed by the UseCanonicalName option.

Next, the ProxyPreserveHost option tells Apache to not use the proxy server’s hostname in requests to the proxy, but rather to make a connection to that host and then relay the original hostname in the HTTP Host header. The UseCanonicalName option rewrites the incoming Host header to its canonical version before relaying, meaning that the example.com alias, is treated as www.example.com as well.

The origin server is altered similarly, setting the actual hostname as an alias and the canonical name to the public-facing hostname. The UseCanonicalName option is used to ensure error pages and interpreters will always use the preferred hostname.

Alternatively, fix it on the origin

You can always fix this on the origin by rewriting all incoming requests so that they appear to have the correct Host header.

Origin:

RequestHeader set "Host" "www.example.com"

This approach can cause issues for the rewrite module, caching module, GnuTLS module, as well as other modules that work with the actual hostname submitted to the server. This requires a lot of testing to verify that it doesn’t cause any problems.

Leaking information about the origin server

A blog post by Chris Knight reminded me of the importance of hiding the hostname and IP of the origin server. He wrote about it in the context of Cloudflare, which is a commercial caching reverse-proxy service provider, but the principle also applies to non-Cloudflare proxies.

For example, if this website came under attack, I could turn off the forward-facing reverse proxies that handle incoming requests and change my DNS to hide behind a commercial proxy service.

It’s important to keep any potential attack directed at the proxy servers so that the origin server can remain relatively isolated. Chris Knight goes through other measures you can take to hide your origin server in his blog post.

Apache’s mod_cache will by default expose the origin server’s IP or hostname on any error page. It’s thus important to not serve any error pages from the origin server with its actual hostname exposed.

Hostname canonicalization, as discussed above, can hide the hostname on any 4xx client errors. However, 5xx server errors can possibly leak the IP address as these kinds of errors can appear when Apache isn’t correctly configured and your options thus isn’t in effect.

You can mitigate this somewhat if you use mod_cache with mod_proxy on your reverse proxies. By enabling the CacheStaleOnError on option, Apache will serve cached variants of requests if the origin server responds with a 5xx error code.