Nginx Load Balancing – Fix 301 Redirect Issue from Backend

apache-2.2load balancingnginxreverse-proxy

I am running into an odd problem for which I am not sure if it is a configuration issue or a bug in nginx. My setup is a nginx reverse proxy which has Apache2 backend servers. The load balancer is pretty basic similar to the example from the wiki, e.g. simplified:

http {
  upstream myproject {
    server 127.0.0.1:8000;
  }

  server {
    listen 80;
    location / {
      proxy_pass http://myproject;
    }
  }
}

Now, a problem arises for example when I try to request a directory in Apache, without the trailing slash at the end of the url. For example, a client requests:

http://apache.myserver.com/somedirectory

Apache will reply with a HTTP 302 to redirect the client to

http://apache.myserver.com/somedirectory/

Note the url has a slash in the end to note that it is a directory. Also note that Apache is "smart" by using the incoming request hostname in the redirect header. All fine so far. However, when using nginx with load balancing, this 301 is sent to the client without translating the nginx upstream name back to an actual server/domain. So the client will receive the following:

GET http://nginx.myserver.com/somedirectory
HTTP 301 Moved Permanently
...
Location: http://myproject:8000/somedirectory/

The myproject is the name of the nginx upstream backend. It is not an actual host that the client can resolve. It seems to me that instead the client should have been redirected to

http://nginx.myserver.com/somedirectory/  

I.e. the name of the upstream backend should have been replaced in the response header. Is this a bug in nginx or am I doing something wrong?

Best Answer

Turns out nginx has quite a lot of proxy redirect options to deal with this sort of problems. I ended up using something like this:

location / {
    proxy_pass  http://myproject;
    proxy_set_header Host myproject;
    proxy_redirect http://myproject/ $scheme://$host/;
    proxy_redirect http://myproject:8000/ $scheme://$host/;
}

This basically replaces the upstream name with the $host and removes the port. This worked in my case because I am hosting nginx on the default ports for HTTP/HTTPS. If nginx is running on a non-default port, something like this is needed:

    proxy_redirect http://myproject/ $scheme://$host:$server_port/;
    proxy_redirect http://myproject:8000/ $scheme://$host:$server_port/;