8

This seems ridiculous but I've not found a working answer in over an hour of searching.

I have a static website running off nginx (which happens to be behind Varnish). The index file is called index.html. I want to redirect anyone who actually visits the URL mydomain.com/index.html back to mydomain.com.

Here is my nginx config for the site:

server {
  listen  8080;
  server_name  www.mydomain.com;
  port_in_redirect  off;

  location / {
    root   /usr/share/nginx/www.mydomain.com/public;
    index index.html;
  }

  rewrite /index.html http://www.mydomain.com/ permanent;
}

http://www.mydomain.com/index.html responds as expected with a 301 with the location http://www.mydomain.com/ but unfortunately http://www.mydomain.com/ also serves a 301 back to itself so we get a redirect loop.

How can I tell nginx to only serve the 301 if index.html is literally in the request?

Ade
  • 2,961
  • 4
  • 30
  • 47

2 Answers2

10

Add a new location block to handle your homepage, and use try_files directive (instead of "index index.html;") to look for the index.html file directly. Note that try_files requires you to enter at least 2 choices. So I put the same file twice.

location = / {
  root   /usr/share/nginx/www.mydomain.com/public;
  try_files /index.html /index.html;
}

Looks good based on my experiment:

curl -iL http://www.mydomain.com/index.html
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 16 Mar 2013 09:07:27 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.mydomain.com/

HTTP/1.1 200 OK
Server: nginx
Date: Sat, 16 Mar 2013 09:07:27 GMT
Content-Type: text/html
Content-Length: 4
Last-Modified: Sat, 16 Mar 2013 08:05:47 GMT
Connection: keep-alive
Accept-Ranges: bytes

[UPDATE] The root cause of the redirect loop is the 'index' directive, which triggers nginx to do another round of location match again. That's how the rewrite rule outside the location block gets executed again, causing the loop. So the 'index' directive is like a "rewrite...last;" directive. You don't want that in your case.

The trick is to not trigger another location match again. try_files can do that efficiently. That's why I picked it in my original answer. However, if you like, another simple fix is to replace

  index index.html;

by

  rewrite ^/$ /index.html break;

inside your original "location /" block. This 'rewrite...break;' directive will keep nginx stay inside the same location block, effectively stop the loop. However, the side effect of this approach is that you lose the functionality provided by 'index' directive.

[UPDATE 2]

Actually, index directive executes after rewrite directive. So the following also works. Note that I just added the rewrite...break; line. If the request uri is "/", nginx finds the existing file /index.html from the rewrite rule first. So the index directive is never being triggered for this request. As a result, both directives can work together.

  location / {
    root   /usr/share/nginx/www.mydomain.com/public;
    index index.html;
    rewrite ^/$ /index.html break;
  }
Chuan Ma
  • 9,754
  • 2
  • 45
  • 37
  • Thanks, that works. I don't really understand why that stops the redirect loop but it does. `try_files` is a bit magical maybe. Can you explain why putting the `try_files /index.html /index.html;` line into the _existing_ location block does not work? (I tried that). Cheers. – Ade Mar 19 '13 at 10:37
  • @Ade I'll explain later tonight. – Chuan Ma Mar 19 '13 at 13:38
  • @Ade add more explanation. Not sure how you did it your 2nd test. Note you have to remove your 'index' directive to make it work. – Chuan Ma Mar 19 '13 at 17:32
  • @ChuanMa Thank you so much for the explanation - I'm relatively new to nginx and now starting to understand it a little better. Thank you! – Ade Mar 19 '13 at 22:34
  • @Ade np. I updated the answer again. You asked a good question. Glad to see it works for you! – Chuan Ma Mar 20 '13 at 01:31
-1

Looks like you really don't want index.php to show up in the address bar, is that correct?

If you add a rewrite directive to the nginx config, you'll get a redirect loop, as you have experienced. If you are open to a javascript solution, you can place this anywhere in your index.html to silently rewrite the address bar:

<script>
    history.pushState(null, '', '/');
</script>

For more information

Keep in mind that while most modern browsers support the history API, not all do (namely, most versions of IE).

Julian H. Lam
  • 25,501
  • 13
  • 46
  • 73
  • Thanks; yes I know of several application-level ways to do the redirect but wanted this to be configured at server level as it's due to be reused across a number of applications. – Ade Mar 19 '13 at 10:40