URL trailing slash policy

URL trailing slash policy

Suppose we have this URL:

learn.techseo.blog/url-trailing-slash-policy

Now, we may have configured our web application to redirect all requests to a certain router, which would also probably allow the following URL:

learn.techseo.blog/url-trailing-slash-policy/

Oops! We now have two different URLs for the same resource → Duplicate content?

This problem is present in many frameworks, such as Laravel or ExpressJS.

What is duplicate content?

Duplicate content generally refers to blocks of content within or across different domains that either completely match other content or are appreciably similar.

Of course, this could include malicious duplicate content, but in most cases, we will be our own enemy by displaying the same content in different URLs on our website.

Enforcing trailing slash in our URLs

One simple way to solve this problem is to redirect all traffic to the same URL, but with a trailing slash.

Force trailing slash in .htaccess

Using .htaccess, we can get this done by adding these new rules:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ /$1/ [L,R=301]

With this instructions, we are telling our Apache server to redirect everything (ignoring existing files) that doesn't have a trailing slash, to the same URL with a trailing slash. This way, this is what will happen to our first example:

GET https://learn.techseo.blog/url-trailing-slash-policy

Response: 301. Location: https://learn.techseo.blog/url-trailing-slash-policy/

Final request: https://learn.techseo.blog/url-trailing-slash-policy/

NOTE: When testing, you can just use [L,R] instead of [L,R=301] to use a temporary 302 redirection. 301 redirections will be cached by your browser.

Force trailing slash in NGINX

We will add a new rule to our NGINX configuration to enforce a trailing slash policy in our URLs:

server {
    server_name example.com;
    location ~ "^/(.*[^/])$" {
        try_files $uri /$1/$is_args$args;
    }
}

Trailing slash policy in Express using NodeJS

We could also configure our web application to perform the redirection, instead of relying directly on the web server. To do this in Express, we could add this simple middleware to our application:

app.use(function (req, res, next) {
  if (req.path.substr(-1) !== '/' && req.path.length > 1) {
    const query = req.url.slice(req.path.length)
    res.redirect(301, req.path.slice + '/' + query)
  } else {
    next()
  }
})

Forcing a non-trailing slash policy

We could also want to enforce a policy in which our URLs do not have a trailing slash.

Non-trailing slash policy in .htaccess

We could do this by changing the regular expression, writing these new rules in our .htaccess file:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]

This rule will redirect automatically unless we're requesting an actual directory.

NOTE: When testing, you can just use [L,R] instead of [L,R=301] to use a temporary 302 redirection. 301 redirections will be cached by your browser.

Non-trailing slash policy in NGINX

We will add a new rule to our NGINX configuration to enforce a non-trailing slash policy in our URLs:

server {
    server_name example.com;
    location ~ "^/(.*)/$" {
        try_files $uri/ /$1$is_args$args;
    }
}

Non-trailing slash policy in Express using NodeJS

We could also configure our web application to perform the redirection, instead of relying directly on the web server. To do this in Express, we could add this simple middleware to our application:

app.use(function (req, res, next) {
  if (req.path.substr(-1) === '/' && req.path.length > 1) {
    const query = req.url.slice(req.path.length)
    res.redirect(301, req.path.slice(0, -1) + query)
  } else {
    next()
  }
})

Summary

We've seen how we can configure our server to automatically redirect URLs to enforce a trailing or non-trailing slash policy, providing a solution for many duplicate content issues.