Canonical URLs, redirects and the www question
A single page can usually be reached at several addresses: with and without www, over HTTP and HTTPS, with and without a trailing slash, with tracking parameters on the end. Google groups addresses that show the same content and picks one of them as the canonical. If your signals disagree, it may pick one you did not intend. The remedy is to make one address answer with the page and to point every other variant at it.
Three kinds of signal, in order of strength
Google's documentation on specifying a canonical lists the methods in order of how strongly they influence the choice:
| Method | Strength according to Google | Use it for |
|---|---|---|
| Redirects | Strong signal that the target should become canonical | Duplicates that should not be reachable at all |
rel="canonical" | Strong signal that the named URL should become canonical | Duplicates that must stay reachable |
| Sitemap inclusion | Weak signal | Confirming your preferred addresses |
Google adds that these methods can stack, that none is required, and that stating a preference is a hint, not a rule. The instruction it repeats is to avoid contradiction, for example naming one URL in the sitemap and a different one in rel="canonical". Pick the preferred address first, then make every mechanism agree with it.
What Google does with HTTP and HTTPS
Google prefers an HTTPS page over the equivalent HTTP page as the canonical, with exceptions it lists: an invalid certificate, insecure dependencies other than images, an HTTPS page that redirects through an HTTP page, and an HTTPS page whose rel="canonical" names the HTTP version. It says a bad certificate or an HTTPS-to-HTTP redirect makes it prefer HTTP very strongly, and that HSTS cannot override that. A certificate issued for the wrong host variant counts too, so check that it covers both the apex and www names.
Redirects: which status code
Server-side redirects are the most reliable way to move a visitor and Google to the right place. Google's redirect documentation orders its methods by how likely Google is to interpret them correctly, and puts server-side redirects first. For permanent moves there are two codes to know:
- 301 Moved Permanently is the long-standing choice.
- 308 Permanent Redirect carries the same meaning for Google, but it was added later to say unambiguously that the request method must be preserved.
RFC 9110 explains why both exist. Early browsers split on whether to follow a redirected POST as a POST or as a GET, practice converged on GET, and a user agent may now change POST to GET after a 301. The 307 and 308 codes were added for cases where the method must survive. For a visitor loading a page with GET, you will not see a difference.
Google treats 301 and 308 alike: the indexing pipeline uses either as a signal that the redirect target should be canonical, and the target is what search results show. It treats 302, 303 and 307 as temporary: Googlebot follows them, but they are not used as a canonical signal, and the target may still be indexed if other signals point there. Two oddities: an instant meta refresh (0 seconds) counts as permanent and a delayed one as temporary, and a JavaScript redirect is a last resort, because rendering can fail and Google might never see it.
What Vercel sends
Hosts differ in which code their "permanent" option emits, so read the docs for yours. In vercel.json, the permanent field defaults to true. Vercel's configuration reference says true gives status 308 and false gives 307, and that a statusCode field overrides both when you want 301 or another value. Its redirects page adds that a redirect created through a dashboard firewall rule is a temporary 307. A rule using the default therefore sends 308:
{
"redirects": [
{ "source": "/old-page", "destination": "/new-page", "permanent": true }
]
}
Vercel's docs also cover the duplicate that catches many new projects: the generated your-project.vercel.app hostname serving the same pages as the custom domain. They suggest a host-scoped permanent redirect to the custom domain that keeps the path, or an absolute rel="canonical" on each page if both must stay public. Duplicates bring no automatic penalty, they say, but can split signals.
Keep chains short: redirect every variant directly to the final canonical URL, not through intermediate ones.
The www question
There is no technical winner between www.example.com and example.com. The decision rests on practicalities.
| Consideration | Bare domain (apex) | www |
|---|---|---|
| Shorter to say and print | Yes | No |
| Can use a CNAME at the DNS level | No (needs A/AAAA or a provider alias) | Yes |
| Easier to move between hosts or CDNs | Harder | Easier |
The apex cannot hold a standard CNAME record. RFC 1034 says that when a CNAME record is present at a node, no other data should be present, and the apex of a zone has to carry other records. How DNS works for someone who just bought a domain covers this in detail. A host that wants you to CNAME its address may therefore push you toward www, or offer an alias record for the apex. Either is fine.
Cookies are not a reason to choose. Per MDN, a cookie set without a Domain attribute stays on the host that set it, and Domain=example.com makes it available to subdomains too. If other subdomains will exist later, leave Domain off the cookies that should not travel to them.
Whatever you pick:
- Both names resolve in DNS.
- Both have a valid HTTPS certificate, or the redirect from the second one will show a warning before it fires.
- The non-preferred name redirects permanently to the preferred one, preserving the path.
- Links, sitemaps,
rel="canonical", Open Graph URLs and structured data all use the preferred form.
Be cautious with HSTS and its includeSubDomains option. MDN states that the policy then applies to every subdomain of that host, so once a browser has the header, every subdomain must support HTTPS.
Trailing slashes, case and index files
To a server these can be distinct addresses:
/journaland/journal//About/and/about//pageand/page/index.html
The rule underneath is in RFC 9110. Scheme and host compare case-insensitively, every other component case-sensitively, and the only path normalisation it defines is that an empty path equals /. Anything else is a different string, so to a strict reader a different URL. Google's URL structure guidance agrees on case: /APPLE and /apple are distinct URLs to it, and it recommends converting to one case if your server treats them the same.
The root is the exception, and the reason is written down. RFC 9112 section 3.2.1 says that when the target URI's path is empty, the client must send / as the path in the request. Every request to the bare origin therefore reaches the server as a request for /, and https://example.com and https://example.com/ are one URL.
Servers and frameworks vary in how they treat the rest. On Vercel, the trailingSlash setting in vercel.json makes a mismatched request answer with a 308 to the form you chose, and leaving it undefined means no redirect either way. Its CDN does not lowercase paths, so /About is a 404 if only /about exists. Decide on a convention, apply it in the redirect or build configuration, and generate every internal link in that form, as Google advises.
Parameters and other duplicate sources
| Duplicate source | Example | Fix |
|---|---|---|
| Tracking parameters | ?utm_source=newsletter | Canonical tag on the page pointing at the clean URL |
| Session or filter parameters | ?sort=newest | Canonical to the unfiltered list, or keep filters out of crawlable links |
| Print or AMP-style copies | /page/print | Canonical to the main version |
| HTTP and HTTPS versions | http://example.com/page | Redirect to HTTPS |
| Staging or preview hosts | preview.example.dev | Password protection, or a host-scoped X-Robots-Tag: noindex as Vercel suggests |
| Translations | /fr/page | Separate pages when the body text is translated; use hreflang |
On translations, Google counts language versions as duplicates only when the primary content is in the same language. A page with a translated header and footer but an untranslated body is a duplicate.
Writing rel=canonical correctly
<link rel="canonical" href="https://www.example.com/journal/sample-post/">
What Google's documentation asks for, in summary:
- Use an absolute URL. Relative paths work but Google recommends against them, partly because a testing site can end up crawled.
- Put a self-referencing canonical on the canonical page itself.
- Do not name a URL fragment as canonical.
- The element is only accepted inside a valid
<head>. - Use either the HTML element or the HTTP
Linkheader, not both. Both are supported, but using both is more error-prone, because the two can name different URLs. - Do not use robots.txt or the URL removal tool to choose between duplicates. A disallowed URL can still be indexed without its content, and removal hides every version.
- Do not use
noindexto pick a canonical within one site. It removes the page from Search entirely, andrel="canonical"is the preferred tool. - Annotations carrying
hreflang,lang,mediaortypeattributes are not used for canonicalization.
For non-HTML files such as PDFs, the HTTP header form is the option that works. Google supports it for web search results.
Canonical mistakes that undo the work
- Every page canonicalised to the home page. A template with a hard-coded canonical tells Google that all pages are duplicates of one. Vercel's docs call this out directly. Generate the value from each page's own address.
- Two canonical tags. Plugins and templates sometimes both add one. Google's JavaScript SEO guidance says conflicting or multiple tags may lead to unexpected results, so there should be exactly one per page.
- A canonical that exists only after JavaScript runs. Google will pick up an injected tag when it renders the page, but recommends the HTML. It also says JavaScript should never change a canonical already in the HTML to a different value, and that if you cannot set it in HTML, set it only with JavaScript.
- HTTP in the canonical on an HTTPS site. Google lists this among the signals that can make it prefer the HTTP page.
- A canonical that points at an address that redirects. Google does not list this case; as house practice, name the final URL that answers 200.
None of these produce an error you can see on the page. They show up as Google choosing a different canonical than the one you declared. Google's troubleshooting guidance says to check its selection with the URL Inspection tool, look for causes such as an incorrect canonical element from a CMS plugin, and be patient: pages can stay in a duplicate cluster for up to two weeks after a fix.
Verify with curl
Run these checks against your real hostnames. Each should end at exactly one final URL with status 200, in as few hops as possible.
curl -sIL http://example.com/ | grep -i -E '^(HTTP|location)'
curl -sIL https://example.com/ | grep -i -E '^(HTTP|location)'
curl -sIL http://www.example.com/journal | grep -i -E '^(HTTP|location)'
curl -s https://www.example.com/journal/ | grep -i canonical
curl -sI https://www.example.com/downloads/guide.pdf | grep -i '^link'
A shorter way to count hops is curl's write-out option, which prints the number of redirects followed and where it ended:
curl -s -o /dev/null -L -w 'hops=%{num_redirects} final=%{url_effective} status=%{http_code}\n' http://example.com/journal
A healthy result for the preferred address https://www.example.com/ is one permanent redirect from each of the other three variants straight to it, then a 200. If you see two or more hops, or a 200 on a non-preferred form, there is a duplicate. The last command in the first block applies to non-HTML files and usually prints nothing for HTML pages, which is expected.
In Search Console, the URL Inspection tool reports which URL Google chose as canonical for a page. If it differs from your declared one, look for a conflicting signal.
One page, four variants (fictional)
The fictional site example.com has live variants of its journal page: HTTP and HTTPS, each with and without www, and the page itself at /journal with no slash. Before any rules, a request for http://example.com/journal walks three hops: to HTTPS, then to www, then to /journal/. Only https://www.example.com/journal/ should exist as a 200.
The owner sets rules so that every variant redirects in one step to that final address, then adds a self-referencing canonical and lists only the preferred form in the sitemap. A curl run per variant now prints hops=1, and the run against the preferred URL prints hops=0 with status 200.
Keep this
- Choose www or apex once, and the trailing-slash convention once.
- Redirect every other variant straight to the preferred URL with a permanent redirect.
- Add an absolute self-referencing
rel="canonical"to each page. - Make links, sitemaps, canonicals and structured data agree.
- Re-run the curl checks after every host or configuration change.
Questions and answers
Should I use www or the bare domain?
Either works. Pick one, make it the only address that answers with 200, and redirect the other to it with a permanent redirect, keeping the path. Consistency matters more than the choice.
Is a 301 or a 308 better for SEO?
Google lists both as permanent redirects, and its indexing pipeline uses either as a signal that the target should be canonical. They differ for non-GET requests: 308 keeps the method, while a client may turn a POSTed 301 into a GET. For ordinary page visits the difference does not show.
Is rel=canonical a command?
No. Google describes a canonical preference as a hint, not a rule, and calls rel=canonical a strong signal. It can still choose another URL, so keep redirects, internal links, sitemaps and canonicals consistent.
Do trailing slashes matter?
Yes for everything except the root. HTTP compares paths case-sensitively and only an empty path is equivalent to a single slash, so /about and /about/ are different URLs unless your server treats them the same. Choose one form and redirect the other.
Sources
- How to specify a canonical URL with rel=canonical and other methods (Google Search Central) developers.google.com
- What is URL canonicalization (Google Search Central) developers.google.com
- Redirects and Google Search developers.google.com
- RFC 9112 section 3.2.1, origin-form rfc-editor.org
- Redirects on Vercel (Vercel docs) vercel.com