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?

up vote 119 down vote accepted

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
  • 8
    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

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:

  • 10
    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
  • 4
    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
  • 1
    Took too long to realize the importance of keeping or removing trailing slash. – Parvez Aug 2 '17 at 9:25
  • 2
    This will actually pass //xyz to the host if you do that. – Archimedes Trajano Mar 6 at 16:35

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:

  • 2
    This is the best answer here! – Mo Friedrich Oct 18 '17 at 12:37
  • 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
  • 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 at 17:20
  • 1
    This is the best answer! This must move up. – phegde Mar 30 at 7:51

try

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

or

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
  • 10
    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 at 16:35

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.