When both www.example.com and example.com serve content as separate sites, Google sees two duplicate sites — ranking signals split, link equity diluted, indexing confused. The fix is picking one canonical hostname, 301-redirecting the other, then making the choice consistent across internal links, sitemaps, rel=canonical tags, and Search Console. Either www or non-www works for SEO; what matters is committing and being consistent.
curl -I https://www.example.com/ curl -I https://example.com/ # Expected (correctly canonicalised): # One returns 200 OK # The other returns 301 with Location: https://[canonical]/ # Problem state: # Both return 200 OK (content served independently) # Or chains exist
site:example.com and site:www.example.com separately in Google. If both return separate page sets, you have two indexed sites. If only one returns results, you're already canonicalised.
Either is fine for SEO. Decision factors:
| Choose www | Choose non-www |
|---|---|
| DNS flexibility (CNAME to CDN) | Shorter, looks modern |
| Cookie scoping cleaner across subdomains | Marketing prefers brand without www |
| Existing backlinks point to www | Existing backlinks point to non-www |
| Site has multiple subdomains (blog, app, api) | Site has only the root domain |
If existing backlinks heavily favour one version, pick that one to minimise migration impact. Use site: Google search or a backlink tool to check which has more.
server {
listen 80;
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
# serves content
root /var/www/html;
}
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
listen 443 ssl;
server_name example.com;
root /var/www/html;
}
RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
Rules → Redirect Rules → Create rule
When: Hostname equals "example.com" (or "www.example.com")
Then: Static redirect
URL: https://www.example.com${http.request.uri.path}${...}
Status: 301
Preserve query string: enabled
Hardcoded internal links to the non-canonical version cause unnecessary redirect hops on every navigation.
# WP-CLI search-replace (test with --dry-run first) wp search-replace 'https://example.com' 'https://www.example.com' \ --dry-run --skip-columns=guid # After confirming, run live wp search-replace 'https://example.com' 'https://www.example.com' \ --skip-columns=guid
# Find affected files
grep -rl "https://example\.com" ./public
# Replace (test on one file first)
sed -i 's|https://example\.com|https://www.example.com|g' file.html
# Bulk
find ./public -name "*.html" -exec sed -i \
's|https://example\.com|https://www.example.com|g' {} +
# MySQL UPDATE wp_posts SET post_content = REPLACE(post_content, 'https://example.com', 'https://www.example.com') WHERE post_content LIKE '%https://example.com%'; # Backup database BEFORE running.
Every URL in sitemap.xml should use the canonical host. Regenerate from CMS or manually update.
<url> <loc>https://www.example.com/page</loc> <lastmod>2024-01-15</lastmod> </url>
<link rel="canonical" href="https://www.example.com/page" />
Canonical tags should match the actual served URL. CMS should output canonical based on the canonical host setting.
https://www.example.com and https://example.com as URL-prefix properties in Search Console. OR add a single Domain property example.com that covers all variants.
// Google Analytics 4
// Set "Hostname" as a custom dimension to spot stragglers
// Filter reports to canonical hostname only
// gtag.js setup
gtag('config', 'G-XXXX', {
cookie_domain: 'www.example.com' // or '.example.com' for shared
});
curl -IL https://example.com/page # Expected: 301 → https://www.example.com/page → 200 curl -IL https://www.example.com/page # Expected: 200 directly, no redirect
Verify single-hostname canonicalisation site-wide.
Run Redirect Checker →