Trailing Slash URLs: Fixing Duplicate Pages on NGINX

In this article
Trailing slash URLs create duplicate pages when /page and /page/ both return a 200 with the same content. The fix is simple. Pick one form and 301 the other one to it, on your own origin NGINX or in your CMS. Does your access log or Search Console show the same page twice, once with the slash and once without? Then this is almost always why. Below: choosing a form, checking what your server actually returns right now, writing the NGINX rules, and where canonical tags fit in (spoiler: they’re backup, not the fix).
Why do /page and /page/ count as two different URLs?
To a crawler, a URL with a trailing slash and one without it are two separate addresses. Even if the content is byte-for-byte identical. There are a few usual reasons the split happens. Web servers have long treated paths ending in a slash as directories and paths without one as files. Then there’s the CMS that generates one form but happily accepts both. And internal links or sitemaps drift over time until they mix the two. The Google guide to consolidating duplicate URLs spells out what this costs you: signals like links get spread across the copies instead of piling up on one preferred URL, and Googlebot burns crawl time on duplicates rather than on new or updated pages. One thing people get wrong, though. Neither form ranks better. The only problem is having both.
Pick one form for your trailing slash URLs and stick to it
Either form is fine. Honestly. What counts is URL consistency across the whole site. I’d start with whatever your CMS already generates, because fighting its permalink structure just means more rules to babysit later. Next, look at which form most of your internal and external links already use. Redirecting the smaller group means touching fewer URLs. Files are their own case: addresses ending in .pdf, .xml or .jpg never get a slash.
Once you’ve decided, everything has to point to the same form:
- permalink settings in the CMS
- internal links in content and templates
- the XML sitemap
- navigation menus, breadcrumbs and footer links
- hreflang annotations, if the site has language versions
How to check what your server returns today
Hit both versions with curl -I and compare the status codes and Location headers. Say, curl -I https://example.com/page, then the same thing with /page/. Two 200s? You’ve got duplicates. A 301 pointing at your chosen form is what you’re after. And a 302 is a temporary redirect, so swap it for a permanent one.
While you’re at it, check whether the “wrong” version serves a thin or empty page with a 200 status. Some fallback rules do exactly that (quietly, of course), and Google can end up filing those under pages Google treats as missing. Last thing: make sure the redirect lands straight on the final URL. If it chains with a separate http-to-https or www hop, you’re adding steps that one rule could have handled.
NGINX rewrite trailing slash rules: adding or removing the slash with a 301
In NGINX, a single rewrite rule in the server block sends every non-preferred version to your chosen form with a 301. Here’s the drill:
- Back up the site configuration, for example by copying the file from /etc/nginx/sites-available/.
- Add one of the rules below inside the
serverblock. - Run
nginx -tto test the syntax. - Reload with
systemctl reload nginx. - Run the curl checks again for both versions of several pages.
To strip the slash, leaving real directories and the root / alone:
if (!-d $request_filename) {
rewrite ^/(.+)/$ /$1 permanent;
}To add the slash, leaving real files and any path with a file extension alone:
if (!-f $request_filename) {
rewrite ^([^.]*[^/])$ $1/ permanent;
}The permanent flag returns a 301, which tells search engines the move is final. A 302, on the other hand, says the original address is still valid. So it doesn’t settle anything about which version to keep. There’s more on the reasoning in 301 rules for URL variants. And per the Google page linked above, all permanent redirect methods have the same effect on Search. The only thing that may differ is how long search engines take to notice them.
Running behind a reverse proxy like Jalvo? Then the rule goes on your origin NGINX or in your CMS. Also make sure the origin can’t be reached under a second hostname. That’s a whole extra set of copies, as described in duplicate hosts behind proxies.
Where a canonical URL fits in
A rel=canonical tag is a hint that backs up the redirect. It doesn’t replace it. The Google page lists three ways to declare a canonical URL. First, a link element in the head of the page. Second, a rel=canonical HTTP header, which is handy for non-HTML files such as PDFs. Third, listing canonical URLs in the sitemap, which Google calls a weaker signal than the other two.
My advice: put a self-referencing canonical on every page, written in your chosen trailing slash form, so it never contradicts the redirect. Set it in the CMS or the page templates on your origin. That way new pages get it automatically and nobody has to remember.
So, keeping trailing slash URLs consistent really comes down to a few habits. One form. One 301 rule on NGINX or in the CMS to enforce it. Canonicals and the sitemap in that same form. After the change, go through your access logs and Search Console and confirm only one version of each page is being requested and indexed. Then do it again after CMS upgrades or server config edits, because those have a way of quietly bringing the other form back.
FAQ
Does a trailing slash affect rankings?
No. Search engines don’t prefer one form over the other. The trouble starts only when both forms return content, which splits signals such as links between two addresses. Redirect one form to the other and the question simply goes away.
Is a canonical tag enough without a redirect?
Not really. A canonical tag is a hint, and search engines may ignore it. A 301 redirect gets rid of the duplicate completely, because the second version stops serving content. Redirect is the fix. Canonical is the support act.
Should the homepage or file URLs get a trailing slash?
The root domain behaves the same either way, since browsers and servers treat example.com and example.com/ as one address. Files like .pdf or .xml should stay slash-free. Both NGINX rules above leave these cases alone anyway.


