The complete, beginner-friendly guide to pointing your custom domain at your site and redirecting every URL variant (http, https, www, non-www) to a single canonical URL, using Cloudflare DNS and Vercel.

✨ Features
- Step-by-step Cloudflare DNS + Vercel domain setup with exact field values
- Explains what a canonical URL is and why it matters for SEO
- Covers the full redirect matrix: http/https x www/apex
- Verification commands (curl, nslookup) to prove it works
- Troubleshooting: greyed-out save buttons, orange-cloud proxy conflicts, SSL errors, stale caches
- Canonical
<link> tags and trailing-slash notes for the app layer
- Provider notes: legacy vs newer Vercel values, other hosts
🚀 Quick Start
- Pick your canonical URL (recommended:
https://yourdomain.com — https, non-www, no trailing slash)
- In Cloudflare DNS add two records (DNS only / grey cloud):
- A record: Name
@, IPv4 76.76.21.21
- CNAME: Name
www, Target cname.vercel-dns.com
- In Vercel: add both domains, set apex as production, www to redirect
- Verify with curl — jump to Step 6
🎯 Who Is This For?
- Developers deploying a personal site or app to Vercel with a custom domain
- Anyone confused about which DNS records to add and where
- People whose site loads on one variant but not another
- Anyone who wants clean, SEO-friendly single-URL behaviour
📖 What You'll Learn
- What a canonical URL is and why every site should have exactly one
- The difference between A and CNAME records, and when each is used
- Why "DNS only" (grey cloud) matters when Cloudflare sits in front of Vercel
- How redirect chains work, and why a two-hop redirect is perfectly fine
- How to verify DNS and redirects from the command line with nslookup and curl
- How to set app-layer canonical
<link> tags so search engines get a consistent signal
📋 Table of Contents
What is a Canonical URL?
Out of the box, your site is reachable at four (or more) different addresses:
http://example.com
https://example.com
http://www.example.com
https://www.example.com
They all show the same content, but to browsers, search engines, and analytics tools they are four different URLs. A canonical URL is the single one you choose as the "real" address. Every other variant should permanently redirect (301 or 308) to it.
Why bother?
- SEO: search engines treat each variant as a separate page. Without redirects, ranking signals get split across duplicates and your pages compete with themselves.
- Consistent analytics: one URL means one row in your reports, not four fragments to add up.
- Clean sharing: links people copy, share, and bookmark all point to the same address.
- Simpler cookies and CORS: one origin means fewer edge cases with cookies, local storage, and cross-origin rules.
Choosing Your Canonical Form
This guide recommends https + non-www (apex) + no trailing slash, for example https://yourdomain.com. It is short, modern, and the most common choice for personal sites and small projects.
Choosing www as canonical is equally valid — just flip the redirect direction in the instructions below. Some large sites prefer www for cookie and CDN control, but for most projects the apex is the simpler pick.
With https://yourdomain.com as canonical, here is what each variant should do:
| Variant | What should happen |
|---|
http://yourdomain.com | Redirects to canonical |
http://www.yourdomain.com | Redirects to canonical |
https://www.yourdomain.com | Redirects to canonical |
https://yourdomain.com | Serves the site (200) |
Prerequisites
- A domain with DNS managed by Cloudflare (the free plan is fine). This guide uses Cloudflare + Vercel as the worked example, but the concepts transfer to any DNS provider and host.
- A site deployed on Vercel
- Access to both the Cloudflare and Vercel dashboards
- Optional: a terminal with
curl and nslookup for verification
Step 1: Add Your Domains in Vercel
- Open the Vercel dashboard → your project → Settings → Domains
- Add
yourdomain.com → assign it to production
- Add
www.yourdomain.com → choose "Redirect to yourdomain.com" (308 Permanent)
- Vercel now shows the DNS values it wants you to set
⚠️ Important: Vercel may show either the legacy values (A 76.76.21.21, CNAME cname.vercel-dns.com) or newer project-specific values (A 216.198.79.1, CNAME like xxxxxxxx.vercel-dns-0xx.com). Both work. Copy whatever your dashboard displays, character for character. This guide's examples use the legacy values.
Step 2: Add the A Record (Apex)
In the Cloudflare dashboard → your zone → DNS → Records → Add record, and fill in:
| Field | Value |
|---|
| Type | A |
| Name | @ (means the apex, yourdomain.com itself) |
| IPv4 address | 76.76.21.21 (or the IP your Vercel dashboard shows) |
| Proxy status | DNS only (grey cloud — click the orange cloud to toggle it off) |
| TTL | Auto |
⚠️ Proxy status MUST be DNS only. With the orange cloud on, Cloudflare sits in front of Vercel: SSL provisioning fails or loops, and Vercel cannot verify the domain.
Step 3: Add the CNAME Record (www)
Same flow: DNS → Records → Add record:
| Field | Value |
|---|
| Type | CNAME |
| Name | www |
| Target | cname.vercel-dns.com (or the project-specific target your Vercel dashboard shows) |
| Proxy status | DNS only |
| TTL | Auto |
Step 4: Verify DNS Propagation
Cloudflare DNS changes are usually live within seconds to a few minutes. To get an instant, cache-free answer, query your zone's own nameserver directly (find your two nameservers on the Cloudflare zone overview page):
# Query your zone's own nameserver for an instant answer
nslookup yourdomain.com your-ns.ns.cloudflare.com
nslookup -type=CNAME www.yourdomain.com your-ns.ns.cloudflare.com
Expected output for the A record:
Name: yourdomain.com
Address: 76.76.21.21
Expected output for the CNAME:
www.yourdomain.com canonical name = cname.vercel-dns.com
Step 5: Confirm in Vercel
Back in Vercel → Settings → Domains: both entries should flip to valid (checkmark) within a few minutes. SSL certificates (Let's Encrypt) are issued automatically for both hostnames; the www certificate can take a couple of extra minutes after the apex.
Step 6: Test the Redirect Matrix
This is the money step: prove that all four variants behave correctly. Run one command per variant:
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://yourdomain.com
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://www.yourdomain.com
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" https://www.yourdomain.com
curl -s -o /dev/null -w "%{http_code}\n" https://yourdomain.com
Expected results:
| URL | Expected |
|---|
http://yourdomain.com | 308 → https://yourdomain.com/ |
http://www.yourdomain.com | 308 → https://www.yourdomain.com/ (then hops to apex) |
https://www.yourdomain.com | 308 → https://yourdomain.com/ |
https://yourdomain.com | 200 |
Note the two-hop chain from http://www (http→https first, then www→apex) is normal and fine; browsers follow it instantly. To confirm the whole chain lands on the canonical URL, follow the redirects:
curl -sL -o /dev/null -w "final: %{http_code} %{url_effective}\n" http://www.yourdomain.com
# final: 200 https://yourdomain.com/
Step 7: Set the Canonical Tag (App Layer)
DNS redirects handle wrong hostnames; the canonical <link> tag tells search engines the preferred URL for each individual page. Next.js App Router example:
// app/layout.tsx
export const metadata = {
metadataBase: new URL("https://yourdomain.com"),
alternates: { canonical: "./" },
};
The "./" value resolves per-page against metadataBase, so every page gets its own correct canonical URL automatically.
Plain HTML example:
<link rel="canonical" href="https://yourdomain.com/about" />
A brief note on trailing slashes: pick one form and stick with it. The Next.js default is no trailing slash, and it 308-redirects /about/ → /about automatically, so you rarely need to think about it.
Troubleshooting
Cloudflare Save button is greyed out
Validation is silently failing. Type the IP by hand instead of pasting (paste can bring invisible characters along), check every field has a value, then try a hard refresh, another browser, or disabling extensions.
Vercel shows "Invalid Configuration"
The DNS records were not saved or contain wrong values. Re-check the name and target exactly against what Vercel displays, confirm both records are DNS only (grey cloud), then wait a few minutes and hit Refresh in Vercel.
Redirect loop or SSL errors (ERR_TOO_MANY_REDIRECTS)
Almost always the orange cloud: Cloudflare's proxy sits in front of Vercel and the SSL modes mismatch, so each side keeps bouncing the request back. Set both records to DNS only. If you must proxy, set Cloudflare's SSL/TLS mode to Full (Strict) — but DNS only is the supported, simpler path.
https://www gives a certificate error right after setup
Vercel issues certificates per hostname, and the www certificate can lag the apex by a few minutes. Wait, then retest. Verify with:
curl -sv https://www.yourdomain.com -o /dev/null 2>&1 | grep -iE "subject|issuer"
It works in curl but my browser still fails
Stale cache: browsers cache 308 redirects and HSTS aggressively. Hard refresh (Ctrl+F5), try a private window, or clear the cache entry for the domain.
One variant returns 404 or the wrong site
Either the domain is not attached to the right Vercel project, or an old A/CNAME record for the same name still exists in Cloudflare. Delete any leftovers — you should have exactly one A record for @ and one CNAME for www.
Vercel recommends different values than this guide
Expected — newer projects get 216.198.79.1 and project-specific CNAME targets. Vercel's dashboard is authoritative for your project; the legacy values still work, but you may see a "Recommended update" notice until you match them.
Other Providers
The same pattern maps onto other hosts:
| Host | Apex record | www record |
|---|
| Netlify | A 75.2.60.5 (or apex-loadbalancer.netlify.com), or use Netlify DNS | CNAME to your *.netlify.app site |
| GitHub Pages | 4 A records (185.199.108.153 etc.) | CNAME to username.github.io |
| Cloudflare Pages | Native (CNAME to your *.pages.dev) | CNAME to your *.pages.dev |
The pattern is always the same — an apex record, a www record, and a host-level redirect to your canonical form.
Reference Commands
All the verification commands in one place:
# DNS (instant answers from your zone's nameserver)
nslookup yourdomain.com your-ns.ns.cloudflare.com
nslookup -type=CNAME www.yourdomain.com your-ns.ns.cloudflare.com
# Redirect matrix
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://yourdomain.com
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://www.yourdomain.com
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" https://www.yourdomain.com
curl -s -o /dev/null -w "%{http_code}\n" https://yourdomain.com
# Follow the full chain to the canonical URL
curl -sL -o /dev/null -w "final: %{http_code} %{url_effective}\n" http://www.yourdomain.com
# Certificate check for www
curl -sv https://www.yourdomain.com -o /dev/null 2>&1 | grep -iE "subject|issuer"
Security & SEO Notes
- Use 301/308 permanent redirects (Vercel does this by default); avoid 302/307 for canonicalisation
- Consider HSTS once everything is stable (
Strict-Transport-Security header) — Vercel sets it on https responses
- Submit your sitemap under the canonical origin in Google Search Console, and add the property for the canonical form
- Keep the canonical tag consistent with the redirect target — mixed signals confuse crawlers
FAQ
Q: Should I pick www or non-www?
Either works; the important thing is to be consistent. The apex (non-www) is common for personal sites because it is shorter. Some large sites prefer www for cookie and CDN control.
Q: Why 308 and not 301?
A 308 is a 301 that also preserves the request method (POST stays POST). They are equivalent for SEO purposes; Vercel uses 308.
Q: Do redirects hurt SEO?
No — permanent redirects pass ranking signals through. What hurts is serving the same content on multiple URLs with no redirect at all.
Q: Can I keep Cloudflare's orange-cloud proxy on?
With Vercel, DNS-only is recommended. Proxying doubles the CDN layers and complicates SSL provisioning and domain verification.
Q: How long does propagation take?
For Cloudflare-hosted zones: seconds to minutes locally. Resolvers elsewhere may hold older cached answers up to the previous TTL, worst case 24-48 hours.
Q: What about the trailing slash?
Pick one form; most frameworks 308-redirect the other form automatically. Keep your sitemap and canonical tags consistent with whichever you choose.
Q: Does this work for subdomains like blog.yourdomain.com?
Yes — add a CNAME for the subdomain pointing to your host's target and add the domain in the host's dashboard. The canonical choice applies per hostname.
Changelog
Current version: v1.0 (2026-07-20). See CHANGELOG.md for the full history.
🤝 Contributing
Contributions are welcome! Fork the repo, create a branch (feature/improvement), commit your changes, and open a pull request. See CONTRIBUTING.md for details.
Especially helpful contributions:
- Instructions for other DNS providers (Namecheap, Route 53, Porkbun)
- Instructions for other hosts (Netlify, Fly.io, Render)
- Screenshots of the dashboard steps
- Translations
📝 License
This project is licensed under the MIT License — see the LICENSE file for details.
Additional Resources
📬 Support
Done! Every variant of your domain now lands on one canonical URL. 🎉
If this guide helped you, please consider giving it a ⭐ on GitHub!
Made with ❤️ for the open-source community