73

I have a reverse proxy setup as follows in Apache:

Server A with address www.example.com/folder is the reverse proxy server.

It maps to: Server B with address test.madeupurl.com

This kind of works. But the problem I have is, on www.example.com/folder, all of the relative links are of the forms www.example.com/css/examplefilename.css rather than www.example.com/folder/css/examplefilename.css

How do I fix this?

So far my reverse proxy has this on Server A (www.example.com):

<Location /folder>
    ProxyPass  http://test.madeupurl.com
    ProxyPassReverse http://test.madeupurl.com
</Location>
HBruijn
  • 72,524
  • 21
  • 127
  • 192
Hard worker
  • 839
  • 2
  • 8
  • 11

3 Answers3

112

The Apache ProxyPassRewrite does not rewrite the response bodies received from http://test.example.com, only headers (like redirects to a 404 page and such).

A number of alternatives:

One) Rewrite the internal app to use relative paths instead of absolute. i.e. ../css/style.css instead of /css/style.css

Two) Redeploy the internal app in a the same subdirectory /folder rather than in the root of test.example.com.

Three) One and two are often unlikely to happen... If you're lucky the internal app only uses two or three subdirectories and those are unused on your main site, simply write a bunch of ProxyPass lines:

# Expose Internal App to the internet.
ProxyPass /externalpath/  http://test.example.com/
ProxyPassReverse /externalpath/  http://test.example.com/
# Internal app uses a bunch of absolute paths. 
ProxyPass /css/  http://test.example.com/css/
ProxyPassReverse /css/  http://test.example.com/css/
ProxyPass /icons/  http://test.example.com/icons/
ProxyPassReverse /icons/  http://test.example.com/icons/

Four) Create a separate subdomain for the internal app and simply reverse proxy everything:

<VirtualHost *:80>
   ServerName app.example.com/
   # Expose Internal App to the internet.
   ProxyPass /  http://test.internal.example.com/
   ProxyPassReverse /  http://test.internal.example.com/
</VirtualHost>

Five) Sometimes developers are completely clueless and have their applications not only generate absolute URL's but even include the hostname part in their URL's and the resulting HTML code looks like this: <img src=http://test.example.com/icons/logo.png>.

A) You can use combo solution of a split horizon DNS and scenario 4. Both internal and external users use the test.example.com, but your internal DNS points directly to the ip-address of test.example.com's server. For external users the public record for test.example.com points to the ip-address of your public webserver www.example.com and you can then use solution 4.

B) You can actually get apache to to not only proxy requests to test.example.com, but also rewrite the response body before it will be transmitted to your users. (Normally a proxy only rewrites HTTP headers/responses). mod_substitute in apache 2.2. I haven't tested if it stacks well with mod_proxy, but maybe the following works:

<Location /folder/>
  ProxyPass http://test.example.com/
  ProxyPassReverse http://test.example.com/ 
  AddOutputFilterByType SUBSTITUTE text/html
  Substitute "s|test.example.com/|www.example.com/folder/|i" 
</Location>
Hernán Eche
  • 135
  • 1
  • 7
HBruijn
  • 72,524
  • 21
  • 127
  • 192
  • 6
    Holy cow good answer. Haven't even tried any of these yet just wanted to say thanks for the write-up! Helps a million. Going to test some of these ideas out now and will report back shortly :) – Hard worker Dec 17 '13 at 13:38
  • Quick question please, for point 2 if I understand you correctly sir you are suggesting I redeploy the adpp to test.madeupurl.com/folder? Would this require any changes to my apache config file? This looks like the quickest solution – Hard worker Dec 17 '13 at 13:47
  • Additionally, sorry to bother you but with point 1 when I try what you suggest the problem I describe in my question still persists. For example here I used: and for the link address in the browser it is outputting http://test.madeupurl.com/css/bootstrap.css rather than http://test.madeupurl.com/folder/css/bootstrap.css. Would you have any suggestions on this it would be most helpful – Hard worker Dec 17 '13 at 13:56
  • Often when you redeploy an application that is now installed in the DocumentRoot to a subdirectory like /folder the stylesheets, icons etc will be deployed in /folder/css and /folder/icons etc. Then links in the HTML output will become like `` which in turn will be caught by the `ProxyPass /folder/ http://test.madeupurl.com/folder/` directive. – HBruijn Dec 17 '13 at 13:57
  • The test.madeupurl.com/content/index.html page wants to include test.madeupurl.com/css/custom.css. In that location you should use the relative URL `href="../css/custom.css"` and not `href="/css/custom.css"`. When the internet user retrieves the page the URL will be www.example.com/folder/content/index.html. The URL for the css will then be: `www.example.com/folder/content/../css/custom.css` which is actually `www.example.com/folder/css/custom.css` which will be forwarded to `test.madeupurl.com/css/custom.css`. – HBruijn Dec 17 '13 at 14:22
  • `ProxyPass /folder/ http://test.madeupurl.com/` is functionally equivalent to ` ProxyPass http://test.madeupurl.com/ ` . To foward `/css` you can use either. Order of the /css and /folder ProxyPass statements does not matter in this case. – HBruijn Dec 17 '13 at 14:33
  • For a similar approach with nginx look at this Q&A https://serverfault.com/a/932636/546643 – Bob Apr 01 '21 at 10:11
13

As a complement to HBruijn's answer, if you opt for solution (3) "ProxyPass", you may have to also use mod_proxy_html to rewrite some URLs in your HTML pages.

cf. How to handle relative urls correctly with a reverse proxy for some examples.

As an applied example, here is how you can configure Apache using the ProxyHTMLURLMap rule to forward everything at your-domain-name.com/pad to your Etherpad instance running locally on port 9001:

<Location /pad>
  ProxyPass http://localhost:9001 retry=0
  # retry=0 => avoid 503's when restarting etherpad-lite
  ProxyPassReverse http://localhost:9001
  SetOutputFilter proxy-html
  ProxyHTMLURLMap http://localhost:9001
</Location>
RewriteRule ^/pad$ /pad/ [R]
Andrew Schulman
  • 8,561
  • 21
  • 31
  • 47
Lucas Cimon
  • 253
  • 2
  • 6
  • 3
    Be aware though that mod_proxy_html is only included from Apache 2.4 and up, where the original question above is for Apache 2.2 – HBruijn Oct 10 '14 at 05:45
  • Your answer is impeccable. However, I met a case where the content is not html, it is rather pdf. Using ProxyHTMLURLMap didn't work for me. Any other suggestions? – Mohamed Ennahdi El Idrissi Dec 14 '15 at 21:48
  • 2
    If your content is a PDF, you do not need to rewrite URLs in it ! Unless you want your users to click on links in the PDF to access other pages of your website, but this sounds tricky. To disable URL rewriting, simply omit the last 2 directives: `SetOutputFilter` & `ProxyHTMLURLMap`. – Lucas Cimon Dec 15 '15 at 13:54
  • 1
    You might need to add **RequestHeader unset Accept-Encoding** to avoid encoding errors – zar3bski Feb 09 '19 at 14:01
  • @zar3bski Yes, that's because substitution can't work on compressed content from the original server. Alternatively, you can decompressing incoming content before substituting, and then compress again after, using just `SetOutputFilter INFLATE;DEFLATE` – lorenzo-s Sep 28 '21 at 08:55
11

You can use following way to make a reverse proxy:
1. Install mod_proxy_html

    yum install mod_proxy_html
  1. Load mod_proxy_html module

    LoadModule proxy_html_module modules/mod_proxy_html.so
    
  2. And use following setting

    ProxyRequests off  
    ProxyPass /folder/  http://test.madeupurl.com  
    ProxyHTMLURLMap http://test.madeupurl.com  /folder  
    
    <Location /folder/>  
        ProxyPassReverse /  
        ProxyHTMLEnable On  
        ProxyHTMLURLMap  /  /folder/  
        RequestHeader    unset  Accept-Encoding  
    </Location>  
    

Hope this help.

ThanhHH
  • 211
  • 2
  • 3