21

How can I configure nginx to redirect all URL's (not prepended with /api or some static resource eg. JS/images) to index.html? Reason is I am using HTML5 push state URL's with a single page application. Meaning content is changed whether AJAX or JS depending on the URL

My current nginx config looks like:

server {
    listen 2000;
    server_name localhost;

    location / {
        root    /labs/Projects/Nodebook/public;
        index   index.html;
    }

    location /api/ {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://localhost:3000/;
        proxy_redirect off;
    }
}
Jiew Meng
  • 84,767
  • 185
  • 495
  • 805

3 Answers3

28
location / {
  try_files $uri /index.html;
}

This will check if the requested file exists and return it. If the file doesn't exist, it will return index.html.

http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files

mattes
  • 8,936
  • 5
  • 48
  • 73
  • 6
    doesn't this remove your ability to send 404's back for things that are legitimately not there (for instance, if you forgot to include the favicon.ico it would return your index.html instead?!) – aschepis Sep 24 '14 at 17:27
  • 1
    @aschepis: That's correct. Either don't forget the favicon.ico or create a dedicated rule to return 404 if favicon.ico is missing. Or any other file you need to treat separately. – mattes Oct 28 '16 at 00:36
  • For those who are facing "rewrite or internal redirection cycle while internally". Add "root" and "index" param to location context. I resolved it that way. – Juned Khatri May 29 '22 at 06:37
6

mattes answer is almost a solution, however it won't give 404 for missing files (e.g. favicon.icon) as aschepis pointed out.

Nginx will pick the first location that matches. So we can first match for files (which will give 404 if the file does not exist). And after put a location which defaults to index.html for all urls.

    location /.+\..+ { # files (assuming they always have a dot)
        # use eg alias to serve some files here
    }

    location / { # url routed by client, client gives 404 for bad urls
        try_files $uri /index.html;
    }
j-a
  • 1,780
  • 1
  • 21
  • 19
  • If the URL is `example.com/users/my.name`, this solution won't work. – mattes Oct 28 '16 at 00:34
  • First matching location is a bit ambiguous. A good guide is https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms scroll down to matching location blocks part – alexrogers Aug 19 '17 at 13:26
  • @alexrogins what do you mean? it is pattern matching for . and assumes that a dot means a file. You can replace that pattern with whatever is reasonable for your context. – j-a Aug 19 '17 at 14:22
  • @j-a sorry my comment isn't very clear. So, I read your answer before I RTFM and what I got from your answer by *Nginx will pick the first location that matches* is that it goes down the list and picks the first one it matches. The precedence is much more complex than that it seems though. – alexrogers Aug 23 '17 at 11:01
  • @alexrogins I see, yes, if you have other locations you may need to adjust. In the above, it treats both as prefixes and picks the longer one that matches. So if that is correct the order should not matter. If you try plz let me know :) – j-a Aug 23 '17 at 21:01
  • As noted [here](https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms), the first location section will not be treated as a regular expression match. Because it lacks the regex optional modifier `~`. – Ahmad Ahmadi Dec 16 '19 at 11:55
2

You need to add to your nginx config file:

 rewrite ^(.+)$ /index.html last;

Then say you're using Backbone.js just make sure you re-route any non-defined route to a 404 page:

  routes: {
    // Other routes
    "*path"  : "notFound"
  },

  notFound: function(path) {
    // Load 404 template, probably of a cute animal.
  }

Source: http://readystate4.com/2012/05/17/nginx-and-apache-rewrite-to-support-html5-pushstate/

albertpeiro
  • 356
  • 4
  • 14
  • 2
    while this works, you will lose access to regular files that really exist. – mattes Jul 02 '14 at 19:26
  • true @mattes this was a very poor way I found to fix it partially from the client side. Should be better addressed at Nginx level with more rules to handle cases you mention. – albertpeiro Sep 24 '14 at 21:07