Nginx – Redirect respecting port in the host header

nginxrewrite

I'm often working with tunneled ports where I'll SSH forward a remote port 80 to a local port 8080.

One of the interesting problems I run into is that when I have a rule like this in NGINX:

rewrite ^/(.+)/$ /$1 permanent;

The rewrite will take a request that looks like this:

curl -is -X GET http://localhost:8080/one/two/three/

And redirect it to a URL like this:

http://localhost/one/two/three

It strips the port from the host header and I get redirected to port 80, which is unbound and breaks things.

Can I configure NGINX to respect the host port (as present in the Host header) when doing redirects?

it does

As can be seen in the request headers, the Host header includes the port and I'd like NGINX to use this value for all redirects to maintain the original port the client used to access the server.

My NGINX site config looks like this:

server {
  listen *:80;
  server_name           _;

  index  index.html index.htm;

  access_log            /var/log/nginx/default.access.log combined;
  error_log             /var/log/nginx/default.error.log;

  location / {
    root      /vagrant/_site;
    index     index.html index.htm index.php;
    try_files $uri $uri.html $uri/ =404;
  }

  port_in_redirect on;
  server_name_in_redirect off;
}

The exact steps to reproduce look like this:

$ curl -is http://localhost:8080/2015/08/from-hell-flying-united-airlines
HTTP/1.1 301 Moved Permanently
Server: nginx/1.8.0
Date: Wed, 02 Sep 2015 19:43:10 GMT
Content-Type: text/html
Content-Length: 184
Location: http://localhost/2015/08/from-hell-flying-united-airlines/
Connection: keep-alive

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.8.0</center>
</body>
</html>

I'm not sure why it's redirecting to append a slash in the first place, but the redirect is breaking everything.

Best Answer

I fixed the issue in an interesting way that doesn't immediately make sense to me. It seems that try_files is messing things up, so I did the following to my config to make everything work:

server {
  listen *:80;
  server_name           _;
  port_in_redirect on;
  server_name_in_redirect off;

  index  index.html index.htm index.php;

  access_log            /var/log/nginx/default.access.log combined;
  error_log             /var/log/nginx/default.error.log;

  location / {
    rewrite ^/(.+)/+$ $scheme://$http_host/$1 permanent;
    root      /vagrant/_site;
    index     index.html index.htm index.php;
    try_files $uri $uri/index.html $uri/ =404;
  }
}

My goal, if not immediately clear, is to have posts not end in a slash.

My actual served directory structure looks like this:

/vagrant/_site/2015/
`-- 08
    `-- from-hell-flying-united-airlines
        `-- index.html

Therefore, I tell NGINX that for every request, I want it to try locating a file at $uri, $uri/index.html, and $uri/. If none of these things work, return a 404.

Related Topic