== How not to use Apache's ProxyPass directive Periodically we need to set up reverse proxies with Apache's ProxyPass directive (to support [[our solution to the multiuser PHP problem UserRunWebservers]]). On the surface doing this fairly simple and straightforward; however, the important devil is in this spotlighted bit in [[the documentation http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypass]]: > If the first argument ends with a trailing _/_, the second argument > should also end with a trailing _/_ and vice versa. Otherwise the > resulting requests to the backend may miss some needed slashes and do > not deliver the expected results. Since I have now stubbed my toe on this thoroughly, here are several ways to *not* use _ProxyPass_ for this, all of which fall afoul of the above warning (some in less than obvious ways). To start with, the basic template of ProxyPass is '_ProxyPass /a/path !http://somewhere/else_'. When Apache sees any URL that starts with _/a/path_, it removes _/a/path_ from the front of the URL, puts whatever remains on the end of the second URL, and tries to fetch the resulting URL. In all of the following examples, we want _/url/_ to be reverse proxied as a directory; the target has a page at the top level with a relative link to _a.html_. First mistake: > _ProxyPass /url/ !http://localhost:8080_ The top level page works and the link to _a.html_ shows as link to _/url/a.html_, but attempts to follow the link fail with Apache being unable to fetch the URL !http://localhost:8080a.html. This shows that Apache is effectively forming the URL by text substitution and then interpreting it later; because there is no _/_ at the end of the second argument, it simply glued the raw text of everything past _/url/_ onto it and the result fails badly. (This also doesn't do anything to handle a request for just '_/url_', but one can get around that with other tricks.) Second mistake: > _ProxyPass /url !http://localhost:8080_ If you request _/url/_ everything works. But if you request just _/url_ you still get the page (instead of a redirection to the version with a version with a _/_ on the end) and the relative link to _a.html_ comes out as a link to _/a.html_ (which doesn't exist and in any case is not reverse proxied) instead of _/url/a.html_, because your browser sees _/url_ as a page in _/_ instead of a directory. This case is the tricky case because it's not obvious that we're breaking the rule from the documentation; after all, everything looks right since neither argument ends with a _/_. The problem is that when you make a bare request for !http://localhost:8080, as you do when you ask for '_/url_', Apache implicitly adds a _/_ on the end (because it has to; it must _GET_ *something* from the server at localhost:8080). This implicit _/_ means you have a _/_ on the end of the second argument but not on the end of the first argument and have thus broken the rule. My belief is that there is no simple way for whatever is behind the reverse proxy to fix this. Without peeking at special request headers that Apache reverse proxying supplies, it cannot tell whether a request for _/_ is from someone who asked for '_/url/_' (and is okay) or someone who asked for '_/url_' (and should get redirected to _/url/_). Third mistake: > _ProxyPass /url !http://localhost:8080/_ If you ask for _/url/_ or anything under _/url/_, the reverse proxied web server receives a request for the (local) URL _//_ or something that starts with that. Many web servers are unhappy about this. If you ask for just _/url_ you get a page, but the relative links on the page are broken as before because it's still not redirected to _/url/_. (However, now a suitably crazy web app can actually tell the difference between the two requests.) As far as I can tell the only proper way to use _ProxyPass_ in this situation is as follows: > _ProxyPass /url/ !http://localhost:8080/_ This follows the rules and does not result in doubled _/_'s. It doesn't handle requests for _/url_ at all, but I believe that you can arrange for _/url_ to be redirected to _/url/_ by having a real _/url_ directory in an appropriate place in your filesystem. (In our environment most of these redirections are for user home pages, where _/~user_ will already get redirected appropriately.)