DISCLAIMER
This is assuming a standard setup:
- URL is either naked or www, not a subdomain
- Site is a single WP instance, not a multisite
If you’re working on a subdomain or a multisite, testing and implementation will vary.
WHAT IS CANONICAL
A website can live at many addresses based on HTTP/S and naked/www:
http://website.com
https://website.com
http://www.website.com
https://www.website.com
Canonical is the “correct” version of a domain that we want everyone to go to
WHY IT MATTERS
Prevent user confusion
Prevent SEO confusion, if naked/www aren’t redirected Google views those as 2 different sites and they’ll compete against each other for rank and be penalized for duplicate content
Caching only works for the domain in WP, visitors to the non-canonical will be served dynamic pages instead of cached ones, increasing load on the server
WHAT WE WANT
All variants to redirect to canonical in a single hop
Single hop matters for SEO
TESTING
https://httpstatus.io/
Tests all variants of the domain
Enter your base domain (website.com), check “Canonical domain check”, set user agent to “httpstatus/2.0” to bypass Cloudflare blocking, and click Check Status
Do they all redirect to the canonical in one hop? If not we need to conform
CONFORMING
First, make sure all these are correct:
DNS records:
A | @ | IP address
CNAME | www | @
Serverpilot:
Both naked and www domains added to the app
AutoSSL enabled
Make sure “Redirect to HTTPS” is NOT active! This is fine on staging, but on prod we need to combine this action with the canonical domain variant so we can achieve single-hop.
WP is set to the canonical domain (Settings > General)
The path now forks depending if the DNS is at Cloudflare or not.
If DNS is on Cloudflare:
We want to enforce everything at Cloudflare for operational consistency and lowest latency
Use redirect rules
Rules > Overview > Create rule > Redirect Rule
Replace “website.com” with your domain
Always check “Preserve query string”
When saving, you may get a warning about www not being proxied, which is bogus. Ignore and deploy.
If canonical is naked:
- Custom filter expression
-
(not ssl) or (http.host eq "www.website.com")
- Dynamic
concat("https://website.com", http.request.uri.path)
If canonical is www:
Custom filter expression
(not ssl) or (http.host eq "website.com")
Dynamic
concat(“https://www.website.com”, http.request.uri.path)
If DNS is NOT on Cloudflare:
Can’t enforce at edge, need to do at server, which means htacess
SFTP, edit htaccess
If canonical is naked
# CANONICAL: NAKED
<IfModule mod_rewrite.c>
RewriteEngine On
# Remove www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
# Force HTTPS (behind nginx proxy)
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
If canonical is www
# CANONICAL: WWW
<IfModule mod_rewrite.c>
RewriteEngine On
# If host is NOT www, redirect to https://www.<host>/...
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# If host IS www but scheme is not HTTPS, redirect to https://<same-host>/...
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>