I have a running web-application at http://example.com/, and want to "mount" another application, on a separate server on http://example.com/en. Upstream servers and proxy_pass seem to work, but for one issue:

upstream luscious {
 server lixxxx.members.linode.com:9001;
}

server {
  root /var/www/example.com/current/public/;
  server_name example.com;

  location /en {
    proxy_pass http://luscious;
  }
}

When opening example.com/en, my upstream application returns 404 not found /en. This makes sense, as the upstream does not have the path /en.

Is proxy_path the right solution? Should I rewrite "upstream" so it listens to /en instead, as it root path? Or is there a directive that allows me to rewrite the path passed along to upstream?

up vote 92 down vote accepted

This is likely the most efficient way to do what you want, without the use of any regular expressions:

location = /en {
    return 302 /en/;
}
location /en/ {
    proxy_pass http://luscious/;  # note the trailing slash here, it matters!
}
  • 1
    As far as I know, the last part would still pass "/en" as path along to the proxy, not? – berkes Dec 21 '13 at 13:17
  • 13
    @berkes, no, it won't -- the trailing slash in proxy_pass is what makes a difference. Also, this answer is more correct than the one you came up with, because it also ensures that proxy_redirect stays at default, so, you could still use 302 et al within your backend, and have it work correctly everywhere. – cnst Dec 21 '13 at 18:27
  • 4
    Ah, I was missing the trailing slash :( – Vanuan May 2 '16 at 23:54
  • 2
    AAARGH! TRAILING SLASH ! – barrymac May 26 '17 at 16:27
  • 1
    3 hours of searching, and yeah... It was the trailing slash. Thanks mate! – Lucas P. Oct 15 at 16:09

I'd like to address a newer regex-based answer that's been rising in popularity.

location ~ ^/en(/?)(.*)$ {  # OOPS!
  proxy_pass http://luscious/$2$is_args$args;  # OOPS!
}

The solution may seem more cute at first glance, but it's wrong for multiple reasons.

  • The above regex would match a request uri of /enjoy, redirecting it to /joy upstream. Is this really intended?

  • A request for /en will not result in any redirects, directly serving a / from the upstream (almost as if a request for /en/ was made instead, but not quite). If you use relative URIs within your root page upstream (otherwise, why wouldn't you have the /en/ prefix right there within the upstream URIs?), e.g. src="style.css" (which might reference a language-specific url("menu.png"), for example), then the browser will request that as /style.css instead of /en/style.css. (Or even if you use absolute URIs everywhere, what if someone references an obscure semi-optional resource relatively?) Oops, suddenly the site may not work, but only sometimes or in edge cases.

  • As per my earlier advice at another question already mentioned by the OP's own answer, using regular expressions prevents the proxy_redirect directive from having the default value of default, turning it down to off instead. This means that if the upstream replies with Location: http://127.0.0.1:8080/en/dir/ when a request for /en/dir is made, then that's what the client will see, which obviously won't work correctly. (Which would have been especially ironic for a /en request which prompts regex use in the first place, yet this specific implementation instead suffers from another problem as already mentioned above.) Plus, if you're already using the upstream directive, then it might get extra ugly if you just try to go with a custom one, especially if you may have more than one upstream server — how do you have a separate proxy_redirect for each one of those? You could use regular expressions within proxy_redirect, too, maybe even to match any host, but then what if you decide to give a cross-domain redirect in the future?

To try to address some of the above points with a single regex-based location, we could do the following (note that in proxy_pass we also had to drop the reference to a server from an upstream-based directive, to make proxy_redirect more straightforward):

location ~ ^/en/?((?<=/).*)?$ {
  location = /en { return 302 /en/; }
  proxy_pass http://127.0.0.1:8080/$1$is_args$args;
  proxy_redirect http://127.0.0.1:8080/ /en/;
}

So, if you ask me, the original solution with the two sibling top-level locations would still be a better idea than digging yourself into a rabbit hole by going the regex route instead.

  • This causes an exception: nginx: [emerg] location "/en" is outside location "^/en/?((?<=/).* – Athlan Jul 5 '16 at 11:41
  • 1
    @Athlan, that's because you shouldn't really be using that in the first place! If you still want to, you can put that location outside of the regexp. – cnst Jul 5 '16 at 11:59
  • puting localation = /en outside works really fine! thanks – Sebastian Webber Jan 12 at 13:16

So, I found the answer on stackoverflow:

upstream luscious {
 server lixxxx.members.linode.com:9001;
}

server {
  root /var/www/example.com/current/public/;
  server_name example.com;

  location ~ ^/en(/?)(.*) {
    proxy_pass http://luscious/$2;
  }
}

Basically: passing a regex into location and passing the backref along to the proxy_pass url.

  • I think "proxy_pass luscious/$;" should be "proxy_pass luscious/$2" – Zafer Mar 19 at 12:51
  • 1
    @Zafer is right, the above answer was giving me an error – franck Sep 28 at 10:50
  • I've changed the answer, but don't have a server at hand where I can try this, ATM, so it is not verified. – berkes Oct 2 at 8:09

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.