312

Nginx is running on port 80, and I'm using it to reverse proxy URLs with path /foo to port 3200 this way:

location /foo {
                proxy_pass http://localhost:3200;
                proxy_redirect     off;
                proxy_set_header   Host $host;
}

This works fine, but I have an application on port 3200, for which I don't want the initial /foo to be sent to. That is - when I access http://localhost/foo/bar, I want only /bar to be the path as received by the app. So I tried adding this line to the location block above:

rewrite ^(.*)foo(.*)$ http://localhost:3200/$2 permanent;

This causes 302 redirect (change in URL), but I want 301. What should I do?

1

5 Answers 5

323

Any redirect to localhost doesn't make sense from a remote system (e.g. client's Web browser). So the rewrite flags permanent (301) or redirect (302) are not usable in your case.

Please try following setup using a transparent rewrite rule:

location  /foo {
  rewrite /foo/(.*) /$1  break;
  proxy_pass         http://localhost:3200;
  proxy_redirect     off;
  proxy_set_header   Host $host;
}

Use curl -i to test your rewrites. A very subtle change to the rule can cause nginx to perform a redirect.

10
  • 3
    The URL path still begins with /foo in my app when I do that... Apr 16, 2012 at 1:48
  • There must be a different problem. I reproduced this scenario successfully, just minutes ago. Original URL: http://development/foo/testme/1234 - REQUEST_URI of a PHP script running on a Apache connected as proxy back-end: '/testme/1234' Apr 17, 2012 at 8:32
  • 19
    The regex should probably be /foo(.*), otherwise example.com/foo wont be matched. (which is probably what jeffreyveon experienced)
    – Benno
    Jan 30, 2014 at 2:11
  • 2
    Using "break" means that the rewriting process stops and the changed request will not be processed by another location. If you are using "last" the rewriting process stops as well, but the changed request will be handled again by Nginx to find a matching location directive. Dec 21, 2019 at 21:06
  • 7
    If you also want to match /foo without the slash: rewrite /foo(/.*|$) /$1 break;
    – dols3m
    Jun 1, 2021 at 21:39
265

Simple location prefix matching works for this without using a rewrite rule as long as you specify a URI in the proxy_pass directive:

location /foo {
  proxy_pass http://localhost:3200/;
}

Notice the additional / at the end of the proxy_pass directive. NGINX will strip the matched prefix /foo and pass the remainder to the backend server at the URI /. Therefore, http://myserver:80/foo/bar will post to the backend at http://localhost:3200/bar.

From the NGINX docs on proxy_pass:

If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive:

11
  • 25
    Works for me than I've added / to location /foo/ {
    – Andrei N
    Nov 26, 2015 at 10:41
  • 1
    While this works, the other answer is more self-documenting. This feels like magic.
    – Eric Rini
    Dec 6, 2016 at 19:21
  • 2
    Took too long to realize the importance of keeping or removing trailing slash.
    – Parvez
    Aug 2, 2017 at 9:25
  • 18
    This will actually pass //xyz to the host if you do that. Mar 6, 2018 at 16:35
  • 1
    While this solution "seems" to win out on all these forums, it should be noted in the answer itself that Nginx will URL decode the URL and pass the decoded URL to the proxied server. So this solution will not work if your URL carries URL encoded parts.
    – coding
    May 8, 2018 at 22:14
144

The absolute most correct way and best practice is usually as follows:

location /foo/ {
    proxy_pass http://localhost:3200/; # note the trailing slash!
}

  • Note the dire importance of the trailing slash in proxy_pass, which automatically alters the $uri variable to have the /foo/ on the front-end correspond with / on the backend. No need for an explicit rewrite directive.

  • Additionally, note that the the trailing / in the location is quite important as well — without it, you risk having weird-looking URLs on your site at one point (e.g., a working /fooen in addition to /foo/en).

    Additionally, the trailing / in the location with proxy_pass also ensures some special handling, as per the documentation of the location directive, to effectively cause an implicit location = /foo {return 301 /foo/;} as well.

    So, by defining a location with the trailing slash as above, you not only ensure that slash-less suffix URLs like /fooen won't be valid, but also that a /foo without a trailing slash will continue to work as well.


Reference documentation:

11
  • 1
    It looks like $args are lost: http://frontend/foo?bar=baz will be proxied to http://backend/. Notice that args are not the part of url
    – Vanuan
    Nov 1, 2017 at 17:47
  • @Vanuan, are you sure about that? I'm pretty certain $args should still be handled appropriately if you use the code above, as they're separate from $uri, and should get assembled back in, unless you're using explicit variables in your proxy_pass.
    – cnst
    Nov 3, 2017 at 6:53
  • 1
    @ArchimedesTrajano, you are incorrect, as there's special handling for /foo to redirect to /foo/, so, unless you're doing something weird on the backend, even /foo requests will still work with the above code. (This is actually already part of the answer, BTW.)
    – cnst
    Mar 7, 2018 at 17:20
  • 11
    While this solution "seems" to win out on all these forums, it should be noted in the answer itself that Nginx will URL decode the URL and pass the decoded URL to the proxied server. So this solution will not work if your URL carries URL encoded parts.
    – coding
    May 8, 2018 at 22:15
  • 1
    This is the best answer and @coding is absolutely right. I didn't know about the decoding. Thanks for pointing that out. It's key information. You can use a rewrite rule to preserve url encoding, and achieve the same functionality (see link below). But if you don't know and you run into it, you'll be running around a while trying to figure out your problem. serverfault.com/questions/459369/… Feb 17, 2019 at 7:15
2

try

location /foo {
    proxy_pass http://localhost:3200/;
    ....

or

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
2
  • 20
    This answer would be good if you give some explanation why it must be configured like above.
    – masegaloeh
    Jan 21, 2015 at 22:36
  • 4
    This will actually pass //xyz to the host if you do that. Mar 6, 2018 at 16:35
1

@Terabuck Sorry for not replying no rep yet.

You should not use localhost because you are depending on the fact that application is running on a server with a hosts file. local host is only a default translation to 127.0.0.1. There is nothing stating that you must have this hosts files. It's just very common to have one.

Having a loopback interface is again another common thing to depend on but you are still dependent on the loopback interface on the networking stack. It's a rare case to not have these two. If you ever worry about this. At least on unix/linux you have the option for sockets. This will eliminate the need to for the network stack to reach the localhost. Use caution with this approach as there are a few factors that will come into place on the host OS. Such as the number of open files etc.

1
  • I would suggest that it's so incredibly common to have a way to map the name localhost to 127.0.0.1 that it's probably fine to do...
    – lindes
    Mar 17, 2021 at 2:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .