Custom Domains for Next.js: The Cloudflare + Vercel Setup That Works in 2026
You shipped a Next.js app to Vercel. It's live at `your-project-xxx.vercel.app`. Everything works, but the URL looks like a throwaway demo.
Getting a real domain is a 30-minute task, but it's easy to make mistakes that cost you SSL certificates, SEO, or deliverability.
Here's the exact flow I used to move my template business from a vercel.app URL to my own domain. Production-tested, 2026-current.
The Stack
- **Registrar**: Cloudflare Registrar (at-cost pricing)
- **DNS**: Cloudflare DNS (free, fastest propagation)
- **Hosting**: Vercel
- **Email Routing**: Cloudflare Email Routing (free receiving)
- **Email Sending**: Loops.so
Zero monthly cost except the domain itself (~$12/year for .dev).
Step 1: Buy the Domain on Cloudflare
Use Cloudflare Registrar, not Namecheap or GoDaddy. Prices are wholesale, WHOIS privacy is free, and DNS is managed in the same dashboard.
1. dash.cloudflare.com → Domain Registration → Register Domain
2. Search your domain
3. 2 years with auto-renew ON
4. Pay. Domain is yours in ~60 seconds
Step 2: Add the Domain to Vercel
1. Open your Vercel project → **Settings → Domains**
2. Click **Add Domain**
3. Enter your domain
4. **Uncheck** "Redirect to www" — apex as canonical is cleaner in 2026
5. Save
Vercel shows **Invalid Configuration**. Expected.
Step 3: Get DNS Records
Click **Edit** in Vercel → **Manual setup**. Vercel shows:
```
Type: CNAME
Name: @
Value: <project-hash>.vercel-dns-xxx.com
```
Vercel is migrating from the old A record (76.76.21.21) to CNAME-based records.
Step 4: Add to Cloudflare DNS
Cloudflare → your domain → **DNS → Records → Add record**:
```
Type: CNAME
Name: @
Target: <Vercel-provided value>
Proxy: DNS only (gray cloud) ← CRITICAL
TTL: Auto
```
**Why DNS only matters**: Cloudflare proxy ON intercepts HTTPS, so Vercel can't issue SSL. Keep it gray.
Step 5: Wait for Verification
Vercel auto-detects propagation in 2-10 minutes. Green check = done.
The Apex CNAME Gotcha
DNS spec (RFC 1912) doesn't allow CNAMEs on apex. Cloudflare solves this with **CNAME Flattening** — internally resolves to IP, returns A record. You don't do anything special.
AWS Route 53 calls this "ALIAS". Google Cloud DNS has similar. If your DNS provider doesn't support this, use A record with Vercel's IP instead.
301 Redirects from Old URL
```ts
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
async redirects() {
return [
{
source: "/:path*",
has: [{ type: "host", value: "your-project-xxx.vercel.app" }],
destination: "https://yourdomain.com/:path*",
permanent: true,
},
];
},
};
export default nextConfig;
```
`permanent: true` emits 308 (Google treats as 301). SEO equity transfers.
Step 6: Update Your Code
```bash
grep -r "your-project-xxx.vercel.app" . --include="*.ts" --include="*.tsx" --include="*.md"
```
Replace everywhere: layout metadata, sitemap, robots, OG image URLs, marketing copy, email templates.
Step 7: Update External Services
- Google Search Console: new property + sitemap
- Social bios: X, LinkedIn, GitHub, Gumroad
- Dev.to articles: `canonical_url` front-matter
Free Email on Your Domain
Cloudflare Email Routing:
1. Cloudflare → Email → Email Routing → Enable
2. Add destination (your Gmail)
3. Verify
4. Create routing rule: `hello` → your inbox
Now `hello@yourdomain.com` works. Customer-support-ready.
Total Time
Under 45 minutes.
Common Failures
**SSL Error**: Cloudflare proxy is ON. Turn it OFF.
**NXDOMAIN**: DNS not propagated. Wait 5-10 min.
**www doesn't work**: Add separate CNAME for www.
**Old URL still in Google**: 301 takes 2-8 weeks to propagate in search results. Submit sitemap via Search Console.
Ship Faster
Every [Craftly template](https://getcraftly.gumroad.com) includes a `metadataBase` setup that's one variable change from any domain. Next.js 16.2 + Tailwind v4, deploy in 10 minutes.
If you're shipping a side project, do the domain first. The psychological shift from "demo" to "real product" is worth the $12/year.