Note: Making a reverse proxy with nginx is much more straightforward.
A reverse proxy Show archive.org snapshot 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
Show archive.org snapshot
.
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
href
s 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.