Hammerill Light Logo

Trailing Slashes & Nginx

Trailing Slashes & Nginx

June 5, 2024

While using nginx and serving plain HTML with it, you might encounter a common problem: you need URI to behave in a specific way you defined. Otherwise, your site might not work properly or even throw a crash at user.

For example, you've made a static export with Next.js with a parameter "no trailing slashes", so your users are expected to access "/page". Thus, you'll serve your site with this nginx directive: try_files $uri $uri.html $uri/ =404;.

And suddenly, accessing "/page/" will throw 404 at user!

Of course we need to prevent that. Let me tell how.

(Yes. I've actually written a whole article about it. However, you can learn some regex with me here!)

Noslash to Slash URI

In order to force URIs to end with a slash in nginx, use this redirect:

# Redirect trailing slashes (e.g. ".../page" -> ".../page/")
location ~ ^(.+[^/])$ {
    return 301 $scheme://$host$1/;
}

This one captures all the URIs matching the regex ^(.+[^/])$ (looks uncomprehensible but I'll break it down for ya):

  • ^ and $ mean start and the end of the URI;
  • (), or simply brackets, mean capturing the value inside them in a "regex group". As it's the first (and only one) group, it's accessible in nginx via $1;
  • .+ means sequence of any character and any length (almost the whole URI let's say);
  • and [^/] is any other character not being a slash. In nginx, you don't have to escape it like [^\/].

That way, all the requests for URIs not ending with a slash will enter in and so execute the "return 301" directive, effectively telling to client that page resides at another URL (with an original URI fetched from regex as group 1 or $1 + an added slash to the end).

I remind you that it's up to you to handle your routes! Especially the root, the /. You could configure this regex for your needs (like use .* instead of .+ to also capture the case when there is nothing at the left of the slash. Again, it's up to you to go and try out).

This technique is based on the redirect. You can use "rewrite" directive instead (found online elsewhere). I just give you the example of what I think is more correct.

Now, accessing "/page" will take us to "/page/". Accessing "/page/" directly won't do anything else and page will be just loaded.

Slash to Noslash URI

In order to force URLs to not end with a slash in nginx, use this redirect:

# Redirect trailing slashes (e.g. ".../page/" -> ".../page")
location ~ ^(.+)/$ {
    return 301 $scheme://$host$1;
}

This one captures all the URIs matching the regex ^(.+)/$:

  • ^ and $ mean start and the end of the URI;
  • (.+) means sequence of any character and any length (almost the whole URI let's say). By putting dot (any char) and a plus (any times from 1) in brackets, we capture this value as regex group 1 or $1 in nginx;
  • and / is the slash at the end of the string we're hunting for. In nginx, you don't have to escape it like \/. Note how It's outside the brackets. It does not take part of the group 1.

That way, all the requests for URIs ending with a slash will enter in and so execute the "return 301" directive, effectively telling to client that page resides at another URL (with a URI not containing a slash fetched from regex as group 1 or $1).

So your final server block might look like:

...
root /var/www/html/;
index index.html;

# Redirect trailing slashes (e.g. ".../page/" -> ".../page")
location ~ ^(.+)/$ {
    return 301 $scheme://$host$1;
}

location / {
    try_files $uri $uri.html $uri/ =404;
}
...

And now, accessing "/page" won't do anything and just load the page. Accessing "/page/" will take us to "/page", thus preventing, for example, the 404 error I told about in the intro.

Sorry, there won't be Apache guide since I'm not experienced with it. However if you are, you can contribute here by emailing me.

Ah, did you see my secret?

Bye!