149

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?

169

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.

  • 1
    The URL path still begins with /foo in my app when I do that... – jeffreyveon Apr 16 '12 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' – Jens Bradler Apr 17 '12 at 8:32
  • 9
    The regex should probably be /foo(.*), otherwise example.com/foo wont be matched. (which is probably what jeffreyveon experienced) – Benno Jan 30 '14 at 2:11
  • This kind of works, but my body i'm setting with proxy_set_body is being removed. – Justin Thomas Mar 23 '17 at 17:00
  • rewrite /(.*) /socket.io/ break; SAVE MY DAY FOR SOCKET.IO – user956584 Jul 25 '17 at 18:52
121

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:

  • 12
    Works for me than I've added / to location /foo/ { – Andrei N Nov 26 '15 at 10:41
  • This was precisely what I was looking for! – anbiniyar May 8 '16 at 19:53
  • 6
    This is a very clean solution, I would prefer this to be the canonical answer to the question. – ralien Sep 29 '16 at 22:12
  • 2
    Took too long to realize the importance of keeping or removing trailing slash. – Parvez Aug 2 '17 at 9:25
  • 5
    This will actually pass //xyz to the host if you do that. – Archimedes Trajano Mar 6 '18 at 16:35
59

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:

  • 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 '17 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 '17 at 6:53
  • @cnst Oh, I see. I'm using the host variable. That's counter intuitive. – Vanuan Nov 4 '17 at 18:08
  • 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 '18 at 17:20
  • 2
    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 '18 at 22:15
1

try

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

or

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
  • 13
    This answer would be good if you give some explanation why it must be configured like above. – masegaloeh Jan 21 '15 at 22:36
  • This will actually pass //xyz to the host if you do that. – Archimedes Trajano Mar 6 '18 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.

protected by Community Aug 19 at 23:44

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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