Apache reverse proxy

June 2, 2013

As I’ve written before, I run a lot of my servers virtually on my laptop or on a server at home. I don’t have a DNS server running at home for the moment. The site, as many others like Magento and WordPress needs to be configured for a specific domain. This is easily setup without a DNS server, just configure a local domain name for the site, in my example, hosts.domain.dev running on a server with the IP address 10.0.0.10, and then add a row to your local machine’s hosts file.

10.0.0.10	hosts.domain.dev

When you computer makes a DNS look up, the first thing it does is check your hosts file, if it doesn’t find a match, it will query the DNS server that you’ve either entered in the network settings or the domain server address that you’ve received from the DHCP.

Now in my case I needed to test the responsiveness of a site that needs to have a domain configured, there’s a lot of CMS’s out there that rely on a pre-configured domain name, like WordPress or Magento. The problem that occurs when you want to test a site one a mobile device is that they usually don’t let you make changes to the internal hosts file. You could probably find some kind of obscure app to fix this, but I opted to instead work with the Apache configs that were serving the site.

So what I wanted to do was tell Apache, if a visitor comes in on this IP, fetch this page from this domain, strip out all the links in the markup and replace them with the ip address, rewrite possible headers, rewrite the referrer header and reconfigure the cookie domain paths. In other words a want Apache to act as a reverse proxy. As wikipedia explains:

A reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers.
– Wikipedia on Reverse proxy

This does at first sound a little daunting, and I spent quite some time before I got everything setup the way that I wanted. The first thing you need to do is add the fake domain name to the hosts file on the server. If you run Apache, like I do, on a Linux system, you’ll find the file under /etc/hosts, open the file with you favorite text editor and add the domain name and point it against the localhost (127.0.0.1 is universally understood as localhost).

Once you’ve done that, you need to create a new virtual host for your site. Apache stores the virtualhosts configs under /etc/apache2/sites-available/, create a new file, with the external IP address (you can name it what ever you want, I usually go with the same name as the ServerName directive).

$ vim /etc/apache2/sites-available/10.0.0.10

Now you need to add these following lines in the config file. I’ve documented each row inline in the config. If you copy the file, replace hosts.domain.dev with your own domain and change 10.0.0.10 with the server IP.

<VirtualHost *:80>
        ServerName 10.0.0.10

        # Set url to proxy
        ProxyPass / http://hosts.domain.dev/
        ProxyPassReverse / http://hosts.domain.dev/

        # Set location
        <location />
                # Set url
                ProxyPass http://hosts.domain.dev/

                # Ouput html from proxy filter
                SetOutputFilter proxy-html

                # Add referer hack (if the site uses referrer as security)
                Header add referer "http://hosts.domain.dev"
                RequestHeader set referer "http://hosts.domain.dev"

                # Overwrite html, exchange urls with proxied (like href, src, etc.)
                ProxyHTMLExtended On
                ProxyHTMLURLMap http://hosts.domain.dev  http://10.0.0.10

		# You can add more url maps to overwrite if there are subdomains
		# that you want/need to override
                ProxyHTMLURLMap http://static.domain.dev  http://10.0.0.10

                # Rewrite Cookie domain if the site uses sessions as an identifier
		# i.e. PHPSESSID
                ProxyPassReverseCookieDomain .domain.dev 10.0.0.10
        </location>
</VirtualHost>

In order for Apache to handle the directives, you’ll need to install and enable a few Apache modules. Some of these might already be installed and enabled on your machine.

proxy
proxy_html
proxy_http
headers

After you’ve saved the file, you’ll need to activate the config and restart Apache.

$ a2ensite 10.0.0.10
$ /etc/init.d/apache2 restart

Now pick up the device and point the browser against the server IP. You might be greeted with a http status error code 330 content decoding failed.

error_content_encoding_330

Error 330 (net::ERR_CONTENT_DECODING_FAILED): Unknown error.

This is because the site that you’ve fetched has compressed the output, either via Apache’s module deflate or through an internal mechanism which gzip’s the output. This error occurs because the content-length header does not match the actual length of the document (which we’ve changed). The easiet fix for this is to either modify the virtual host config file for the “real” domain and disable compression in the file (or just deactivate the deflate module), or disabled the internal compression of the site (there’s usually a config flag or an option under admin that you can tick to false/off).

The config is also available as a gist on github.

Tags