# Trailing slashes, www and https: pick one and redirect the rest

> Every page can be reached at up to eight addresses. Pick one form, send the other seven to it with one 301, and keep canonical, sitemap and links on it. Configs for nginx, Apache, Caddy and WordPress.

Updated 2026-09-25 · Technical SEO · HTML version: https://getreport.app/guides/trailing-slashes-www-and-https-pick-one

Take any page on your site, say `/shop/`. It can be reached with or without `https`, with or without `www`, with or without the slash: two by two by two, eight addresses for one page. Search engines treat each as its own URL until you tell them otherwise, and every link, share and bookmark lands on whichever form the person happened to use. This guide gets you to one form: how to choose it, how to redirect the other seven in one hop, and how to keep the canonical tag, the sitemap and your own links on it. Budget an hour, most of it testing.

## Quick answer

- Choose one form: `https://` always, then `www` or bare host (either is fine), then slash or no slash (match what your CMS already does).
- Redirect every other combination straight to the final form with **one 301**, never through a chain (`http://example.com/shop` → `https://www.example.com/shop/` in one response).
- Put the chosen form in the canonical tag, the XML sitemap and every internal link.
- In WordPress, **Site Address** decides the host and **Settings → Permalinks** decides the slash; the server handles `http` → `https`.
- Test all eight variants of one inner page with the [redirect checker](https://getreport.app/tools/redirect-checker) or `curl`; each should take exactly one hop or none.

## Why one form matters

To Google, `http://example.com/shop`, `https://www.example.com/shop/` and the six others are different URLs. It will crawl each one it discovers, pick one to index, and quietly discount the rest. Links from other sites point at whichever form the linking site used, so the ranking signal for one page is split across several addresses. Google merges some of it through canonical tags and redirects, but only when those are consistent, and it is your consistency that makes the merge reliable.

The cost is not only search. Cookies are set per host, so a visitor who logs in on `www` and follows a link to the bare host is logged out. Analytics counts two sessions. Browser caches and CDNs keep two copies. Ad platforms verify one form and reject the other. And `http://` is a plain security problem: without a redirect, an old link or a typed address lands on an unencrypted page that anyone on the network can read or alter. The redirect to `https://` is the one part of this guide that is not optional.

Trailing slashes are the variant people forget. Google says a trailing slash on the host does not matter (`example.com` and `example.com/` are the same), but on a path it does: `/shop` and `/shop/` are different URLs. Most CMSs redirect one to the other, which is fine, as long as your own links do not point at the redirected form on every page.

## How getReport checks it

> **Free tool:** [Free SEO audit](https://getreport.app/tools/seo-audit): Every on-page and technical SEO check in one run: title and description, headings, canonical, robots and sitemaps, indexability, links, images and mobile readiness — each with a fix.

The audit fetches the page and follows its redirects to the final URL. It then makes two extra `GET` requests: the same URL with the other scheme (`http://` for an `https://` page) and the same URL with the other host (`www.` added or removed). Each is followed through its redirects. If the variant ends on your host, the finding passes and names the status code; if the variant answers 200 on its own host, both copies are live and the finding warns. A variant that does not resolve at all is treated as "does not exist" and is not held against you.

> **Check: Only one of www and non-www serves the page.** When the www and non-www addresses both return the page, search engines see two copies of every URL and split links and rankings between them.
>
> 1. Pick one host as canonical and 301-redirect the other to it, keeping the path (most hosts and CDNs have a one-click setting for this).
> 2. Make sure the canonical tag on every page uses the chosen host.

> **Check: http:// redirects to https://.** Visitors who type your address without https, or follow an old link, land on the unencrypted page. Every one of those visits can be read or altered on the network.
>
> 1. Redirect every http:// URL to the same path on https:// with a 301.
> 2. nginx: return 301 https://$host$request_uri; Apache: RewriteRule in .htaccess; most hosts and Cloudflare have a "Always use HTTPS" switch.

For the slash, the audit does not request `/shop` and `/shop/`; it reads your internal links. Every same-host link whose path looks like a directory (no file extension in the last segment) is sorted into "ends with a slash" or "does not". If both sets are non-empty, the finding warns and lists up to five examples of each, which is exactly the list to fix in your templates.

> **Check: Internal links use trailing slashes consistently.** /shop/ and /shop are two different URLs to a search engine. Linking to both styles creates duplicate pages and splits their signals.
>
> 1. Choose one style (with or without trailing slash) and redirect the other to it with a 301.
> 2. Update internal links and the sitemap to use the chosen style everywhere.

![The SEO audit panel of the example shop with the canonical finding passing and the URL consistency findings open: the www variant answering 200 on its own host, and internal links mixing paths with and without a trailing slash](https://getreport.app/guides/img/trailing-slashes-www-and-https-pick-one/seo-panel.webp "The audit shows the host and slash findings next to the canonical finding, because the three have to agree.")

The best-practices module runs a similar host check under `www-redirect`, worded for visitors rather than crawlers (cookies, analytics, caches). Both pass once the redirect is in place.

## Step by step

### 1. Decide the one form

Write it down before touching a config file, because every later step copies it:

| Decision | Options | How to choose |
| --- | --- | --- |
| Scheme | `https://` | No decision to make |
| Host | `www.example.com` or `example.com` | Keep whatever has more links and history; if equal, `www` is slightly easier with CDNs and cookies on subdomains, bare is shorter |
| Slash | `/shop/` or `/shop` | Match your CMS: WordPress with the default permalink structure ends in `/`; most static-site and framework hosts do not |

Changing the host or slash on an established site is a migration. The redirects make it safe, but expect a few weeks of movement in search while Google recrawls. Changing `http` to `https` is always worth it; changing `www` to bare for taste alone usually is not.

### 2. Map the eight addresses

Pick one inner page with a directory-style path and test every combination. From a terminal:

```bash
for u in http://example.com/shop http://example.com/shop/ \
         http://www.example.com/shop http://www.example.com/shop/ \
         https://example.com/shop https://example.com/shop/ \
         https://www.example.com/shop https://www.example.com/shop/; do
  printf "%-40s " "$u"
  curl -sL -o /dev/null -w "%{num_redirects} hops -> %{url_effective}\n" "$u"
done
```

The goal: seven lines with `1 hops` ending at the final form, one line with `0 hops`. Anything with `2` or more is a chain to remove; anything ending somewhere other than the final form is a variant still serving its own copy. The [redirect checker](https://getreport.app/tools/redirect-checker) shows the same hops with their status codes if you prefer a page over a terminal.

### 3. Add the redirects at the server, in one rule

The rule that keeps it to one hop: any request that is wrong in *any* way is sent to the *fully* correct URL. Do not fix the scheme in one place and the host in another.

**nginx.** In `/etc/nginx/sites-available/example.com`, two small `server` blocks catch everything wrong and one serves the site. This example keeps `www`; swap the hosts to prefer the bare domain:

```nginx
# Plain http on either host: one hop to https on the canonical host
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

# https on the wrong host: same single hop
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    return 301 https://www.example.com$request_uri;
}

# The canonical host serves the site and owns the slash decision
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    root /var/www/example;

    # /shop/ -> /shop (only for sites routed through index.php or a framework,
    # not for real directories on disk, which nginx would redirect back)
    rewrite ^/(.+)/$ /$1 permanent;
}
```

`$request_uri` keeps the path and query string; `permanent` makes the rewrite a 301 and nginx appends the query string itself. To *add* slashes instead, replace the `rewrite` line with `if ($request_uri ~ "^([^?.]*[^/?])(\?.*)?$") { return 301 $1/$2; }`. The certificate must cover both hosts or the browser errors before it sees the redirect. Test with `nginx -t`, then `systemctl reload nginx`.

**Apache.** In `.htaccess` at the site root, above the `# BEGIN WordPress` block if there is one. First block fixes scheme and host in one hop; second adds the slash to directory-style paths on the canonical host:

```apache
RewriteEngine On

# Wrong scheme or wrong host: one hop to the final form, path and query kept
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]

# Add a trailing slash to paths that are not files and have no extension
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{1,5}$
RewriteRule ^(.*)$ https://www.example.com/$1/ [R=301,L]
```

To remove slashes instead, replace the second block with:

```apache
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ https://www.example.com/$1 [R=301,L]
```

Behind Cloudflare or a load balancer that ends TLS, `%{HTTPS}` is always `off` and the first rule loops; use `RewriteCond %{HTTP:X-Forwarded-Proto} !https` instead.

**Caddy.** Caddy already redirects `http` to `https` permanently. Declare the wrong host so it goes to the final one in a single hop. In the `Caddyfile`:

```caddy
example.com {
    redir https://www.example.com{uri} permanent
}

www.example.com {
    root * /var/www/example
    # Remove trailing slashes (drop this block to keep them)
    @slash path_regexp dir ^/(.+)/$
    redir @slash /{re.dir.1} permanent
    file_server
}
```

`permanent` matters: `redir` without it sends a 302.

**Cloudflare.** SSL/TLS → Edge Certificates → **Always Use HTTPS** handles the scheme. For the host, Rules → Redirect Rules → create a rule with the expression `http.host eq "example.com"`, a dynamic target of `concat("https://www.example.com", http.request.uri.path)`, status 301 and **Preserve query string** on. Then remove the origin's own host redirect, or `http://example.com/` takes two hops (edge fixes the scheme, origin fixes the host). Set SSL mode to Full (strict) so the origin never sees plain `http` and redirects it back.

### 4. WordPress: two settings do the work

Settings → General has **WordPress Address (URL)** and **Site Address (URL)**. Set both to the final scheme and host, `https://www.example.com`, with no trailing slash. WordPress then generates every internal link on that host and redirects requests on the other host to it. If the server *also* redirects the host, make sure both agree, or the browser reports "too many redirects".

Settings → Permalinks decides the slash. A structure ending in `/` (`/%postname%/`, the default) makes WordPress add the slash and redirect `/shop` to `/shop/`; a structure without it does the reverse. WordPress handles this redirect itself, so on a WordPress site you can leave the slash rules out of the server config and keep only scheme and host there. That keeps each decision in one layer, which is what prevents chains.

After changing the Site Address on an existing site, the content still contains the old form in hard-coded links and image sources. Run a search and replace (the Better Search Replace plugin, or WP-CLI `wp search-replace 'http://example.com' 'https://www.example.com' --dry-run` first) so posts stop linking to the redirected form.

### 5. Canonical, sitemap and links on the chosen form

The redirect catches links you do not control. Three things you do control must already use the final form:

- **Canonical tag.** `<link rel="canonical" href="https://www.example.com/shop/">` on every page, matching scheme, host and slash. A canonical on the redirected form asks Google to index a URL that redirects, which it resolves by ignoring the tag. [Canonical tags explained](https://getreport.app/guides/canonical-tags-explained) covers the rest.
- **XML sitemap.** Every `<loc>` in the final form. SEO plugins and most generators read the Site Address, so this fixes itself once step 4 is right; check a few entries anyway.
- **Internal links.** Menus, footers, in-content links, `og:url`, hreflang alternates. The audit's slash finding lists mixed examples; the broken link checker's "links go through a redirect" finding lists every link on a page that hits one of your new redirects, which is the fastest way to find the leftovers page by page.

### 6. Turn on HSTS last

Once every host answers on `https` in one hop, add `Strict-Transport-Security` so returning browsers skip the `http` request entirely. It is a one-line header, but it is hard to undo, so do it after the redirects are proven, not before: [HSTS safely and the preload list](https://getreport.app/guides/hsts-safely-and-the-preload-list).

## Platform notes

### WordPress

Covered in step 4. One extra: if a cache plugin or the host's page cache serves the old host from cache, purge it after changing the Site Address, or the redirect and the cached page disagree for a while.

### Shopify

Settings → Domains: one domain is primary and the others are set to redirect to it. Shopify serves `https` and redirects `http` itself. Shopify URLs have no trailing slash; do not add one in your own links.

### Static sites / custom

Netlify redirects domain aliases to the primary domain and `http` to `https` automatically; the slash is controlled by "Pretty URLs" under asset optimisation, and by `_redirects` for exceptions. Vercel does the same per domain in the project settings. Next.js has a `trailingSlash: true` option in `next.config.js` that generates links *and* redirects the other form; set it once and every page follows.

## Verify

- The eight-variant `curl` loop from step 2 prints seven `1 hops` lines ending at the final form and one `0 hops` line.
- In the [SEO audit](https://getreport.app/tools/seo-audit), the www finding reads "Only one of www and non-www serves the page", `http://` redirects to `https://`, and internal links use trailing slashes consistently.
- The canonical finding shows a URL identical to the address in the browser bar, slash included.
- Google Search Console: add a **Domain** property, which covers all hosts and schemes, and watch "Page with redirect" grow (expected) while "Duplicate without user-selected canonical" shrinks.

## Common mistakes

- **Two layers, two hops.** Cloudflare fixes `http`, the server fixes `www`, WordPress fixes the slash: three hops for `http://example.com/shop`. Symptom: the redirect checker shows a chain. Make one layer responsible for scheme and host, and let the CMS handle only the slash.
- **Site Address disagrees with the server.** The server forces `www`, WordPress's Site Address has none, and the browser shows "too many redirects". Set both to the same host.
- **Canonical on the redirected form.** After moving to `www`, the theme still prints `https://example.com/...` canonicals. Google ignores canonicals that redirect; fix the Site Address or the SEO plugin's canonical setting.
- **Slash redirect on real directories.** `rewrite ^/(.+)/$ /$1` on a static site loops: nginx removes the slash, then adds it back because `/shop` is a directory on disk. Use it only for CMS-routed paths, or keep the slash.
- **302 instead of 301.** `header('Location: …')` in PHP, `redir` in Caddy and mod_rewrite without `R=301` all default to 302, which tells Google the move is temporary. Check the status code in the redirect checker, not just the destination.
- **Testing in your own browser.** Browsers cache 301s. After changing a rule you still see the old behaviour. Use `curl` or a private window.
