Posted almost 6 years ago. Visible to the public.

Reverse-proxying web applications with Apache 2.4+

Note: Making a reverse proxy with nginx is much more straightforward.

A reverse proxy is a "man in the middle" server that tunnels requests to another server. You can use for things like:

  • Expose a local service that you cannot directly reach over the internet
  • "Change" the domain or path of a web application by rewriting them on the fly
  • Instantly change servers that respond to a name or IP, without relying on DNS TTL

The following describes various Apache 2.4+ VHost directives to reverse-proxy an application that does not want to be reverse-proxied. The example we'll use is a proxy that responds to http://public-host/public-path and secretly tunnels all requests to http://internal-domain/internal-path.

Basic setup

For this to work you need the following Apache modules:

  • headers
  • substitute
  • proxy
  • proxy_http
  • filter
  • ssl

The basic VHost setup looks like this:

<VirtualHost *:80> ServerName public-host ProxyPass /public-path/ http://internal-host/internal-path/ ProxyPassReverse /public-path/ /private-path/ </VirtualHost>

In the block above, ProxyPass will setup the reverse proxy.
ProxyPassReverse will make Apache rewrite 302 redirects like Location: /private-path/foo to Location: /public-path/foo.

If the internal server is on HTTPS

If you're tunneling to a server that's on HTTPs, you need to use SSLProxyEngine on:

ProxyPass /public-path/ https://internal-host/internal-path/ SSLProxyEngine on

Note that it doesn't make your proxy available over https://, it just makes sure that it can fetch from HTTPS internally. You've basically built your custom SSL-stripping MITM attack server.

If your proxy server should be accessible over HTTPS, use your regular Apache directives (SSLEngine and friends) for this.

Rewriting cookies

If the internal web app is hard-wiring cookie paths or domains, you need to rewrite those.
You can do this with the following directives:

ProxyPassReverseCookiePath /internal-path/ /public-path/ ProxyPassReverseCookieDomain internal-domain public-domain

Rewriting asset paths

If you are changing paths (/internal-path/foo to /public-path/foo) /while proxying, the proxied app will probably be broken:

  • all absolute paths in your HTML's link hrefs point to the wrong URL
  • any CSS that contains directives like background-image: url(/internal-path/wallpaper.png) will be break
  • Referenced javascripts or stylesheets will probably attempt to load from the wrong URL

The solution to this involves grepping and replacing all proxied text content. You also need to disable compression so you can find/replace text in the response. All of this is a little screwed up and might have unintended side effects, so make sure you really, absolutely, positively need to change the path while reverse-proxying. If you have control over the internal web application, prefer to deploy it to the same path that you expose publicly.

So anyway, here goes nothing:

# Disable compression RequestHeader unset Accept-Encoding Substitute "s|/internal-path/|/public-path/|n" FilterDeclare NEWPATHS FilterProvider NEWPATHS SUBSTITUTE "%{Content_Type} =~ m|^text/html|" FilterProvider NEWPATHS SUBSTITUTE "%{Content_Type} =~ m|^text/css|" FilterProvider NEWPATHS SUBSTITUTE "%{Content_Type} =~ m|^text/javascript|" FilterProvider NEWPATHS SUBSTITUTE "%{Content_Type} =~ m|^application/javascript|" FilterChain NEWPATHS

Note that code above isn't aware of HTML or CSS in any way, it does a brute find-and-replace. You can now no longer use the string /internal-path/ anywhere in your views.

Also note that the FilterProvider syntax has changed in recent Apache versions, so make sure your Apache version (apache2ctl -v) is 2.4 or higher.

Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Owner of this card:

Henning Koch
Last edit:
over 5 years ago
by Henning Koch
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more