Heroku only allows CNAME records for custom domains, but apex domains (yourdomain.com without www) can't use CNAME per RFC 1034. The fix: point www to your Heroku app via CNAME, then redirect the apex to www using ApexToWWW. Two DNS records, free, automatic SSL.
The Heroku Apex Domain Problem
When you add a custom domain to a Heroku app, Heroku gives you a DNS target that looks something like shielded-meadow-12345.herokudns.com or mydomain-abc123.herokudns.com. You're expected to point your domain at that target with a CNAME record. This works perfectly for subdomains like www.example.com, app.example.com, or api.example.com.
But it fails at the apex domain — also called the root domain, naked domain, or bare domain. That's the version without any subdomain prefix: example.com instead of www.example.com.
Why CNAME doesn't work at the apex
RFC 1034 section 3.6.2 states that a CNAME record cannot coexist with any other record type at the same DNS name. The apex of every domain must have NS records (pointing to the authoritative nameservers) and an SOA record. Because those records are mandatory, you cannot legally place a CNAME at the apex. Most DNS providers will simply refuse to save the configuration.
So when Heroku tells you to "create a CNAME pointing to your-app-12345.herokudns.com", you can do that for www but not for the bare example.com. Heroku does not publish a static IP address for your custom domain either, so you can't fall back to an A record pointing at Heroku.
Common errors users see
- "This site can't be reached" when typing
example.comin the browser — there's simply no DNS record at the apex. - "DNS_PROBE_FINISHED_NXDOMAIN" for the naked domain.
- The apex resolves to your registrar's parking page instead of your Heroku app.
- Cloudflare's CNAME flattening seems to "work" but breaks during outages, region failover, or when Heroku rotates IPs.
- Heroku ACM fails to issue a certificate for the apex because the domain doesn't actually point at Heroku.
The Solution: Point WWW at Heroku, Apex at ApexToWWW
The clean fix is a two-part DNS setup. Your www subdomain points at your Heroku app via CNAME (which Heroku fully supports). Your apex domain points at ApexToWWW, which serves a 301 redirect to www. No nameserver changes, no CNAME flattening tricks, no DNS provider lock-in.
Step 1: Add your www subdomain to Heroku
From your project directory, run:
heroku domains:add www.example.com
Heroku will print the DNS target you need:
Adding www.example.com to ⬢ your-app... done
▸ Configure your app's DNS provider to point to the DNS Target
▸ shielded-meadow-12345.herokudns.com
You can also add the domain from the Heroku dashboard under Settings → Domains → Add domain. Either way, copy the herokudns.com target — you'll need it in the next step.
Step 2: Create a CNAME record for www
At your DNS provider, add a CNAME record:
- Type: CNAME
- Name / Host:
www - Value / Target:
shielded-meadow-12345.herokudns.com(your actual Heroku DNS target) - TTL: 300 or Auto
Step 3: Add an A record for the apex
Now create an A record for the apex (host @) pointing to ApexToWWW's IPv4 address:
- Type: A
- Name / Host:
@ - Value:
65.21.184.101
Step 4: Add an AAAA record for the apex
And the matching IPv6 record:
- Type: AAAA
- Name / Host:
@ - Value:
2a01:4f9:c012:a304::1
Step 5: Verify with curl
Wait 5 to 30 minutes for DNS to propagate, then test:
curl -I https://yourdomain.com
HTTP/1.1 301 Moved Permanently
Location: https://www.yourdomain.com/
You should also see your Heroku app respond on the www version:
curl -I https://www.yourdomain.com
# HTTP/1.1 200 OK (or whatever your Heroku app returns)
DNS records summary
Here are all three records you need at your DNS provider, in one table:
DNS Records for a Heroku Custom Domain Setup
| Type | Name / Host | Value / Points To | Purpose |
|---|---|---|---|
CNAME |
www |
your-app-12345.herokudns.com |
Routes www to Heroku |
A |
@ |
65.21.184.101 |
Apex IPv4 → redirect |
AAAA |
@ |
2a01:4f9:c012:a304::1 |
Apex IPv6 → redirect |
Why Not Just Use Heroku's A Records?
This is the most common question developers ask: "Why doesn't Heroku just publish A records I can use at the apex?"
The short answer: Heroku doesn't publish static A records for custom domains. Heroku's routing layer runs on AWS, and the underlying IP addresses change as the platform scales, fails over between availability zones, and rotates load balancers. If you hardcoded an IP at the apex, your site would eventually break without warning.
Some DNS providers offer ALIAS or ANAME records — vendor extensions that resolve a CNAME-like target server-side and return the result as A/AAAA records to clients. Providers that support this for Heroku include:
- DNSimple (ALIAS records)
- DNS Made Easy (ANAME records)
- easyDNS (ANAME records)
- Cloudflare (CNAME flattening at the apex, but only if Cloudflare is your authoritative DNS)
- Route 53 (alias records, but only for AWS resources — not Heroku)
If you happen to use one of those providers, ALIAS/ANAME records are a valid alternative. But they lock you into a specific DNS vendor, add an extra resolution hop on every request, and don't work with the majority of registrars (GoDaddy, Namecheap, Google Domains, Porkbun, and most resellers all lack ALIAS support).
ApexToWWW works with any DNS provider because it only needs plain A and AAAA records — the most basic record types in DNS, supported universally. Your apex resolves to a fixed pair of IPs that we maintain, and we serve the 301 redirect.
Works With Other Platforms Too
The same setup works for any host that requires (or recommends) a www subdomain. If you're considering moving off Heroku or running a multi-cloud setup, ApexToWWW handles the apex redirect identically across providers:
- Vercel apex domain redirect — Vercel recommends www and uses CNAME for custom domains.
- Netlify apex redirect — Same CNAME pattern as Heroku and Vercel.
- GitHub Pages apex setup — A records exist but lack IPv6 and SSL nuances; many users still prefer www.
- Firebase Hosting — Firebase issues a CNAME target for custom domains.
- Render, Fly.io, Cloud Run — All issue CNAME targets and benefit from the same redirect pattern.
Frequently Asked Questions
Does this work with Heroku's automated certificate management (ACM)?
Yes. Heroku ACM provisions a TLS certificate for your www subdomain automatically. ApexToWWW provisions its own Let's Encrypt certificate for the apex. The two operate independently, so visitors who hit example.com get a secure HTTPS 301 redirect from us, and the actual page is served over HTTPS by Heroku. You don't have to upload, manage, or renew certificates anywhere.
Do I need to change my nameservers?
No. Keep using whatever DNS provider you already have — GoDaddy, Namecheap, Cloudflare, Google Domains, Porkbun, AWS Route 53, or anything else. You only need to add three records (a www CNAME and two apex records). There are no nameserver changes and no proxy in front of your traffic.
What about the Heroku SNI endpoint?
Heroku uses SNI (Server Name Indication) for SSL on all custom domains by default, including paid SNI endpoints on legacy plans. Your www CNAME points directly at the Heroku DNS target, so SNI works exactly as Heroku expects. ApexToWWW handles SNI on its own end for the apex — we look at the SNI hostname in the TLS handshake to serve the right Let's Encrypt certificate before issuing the redirect.
Will it work with Heroku Enterprise or Private Spaces?
Yes. Heroku Enterprise, Shield Private Spaces, and standard Private Spaces all use herokudns.com CNAME targets for custom domains. The setup is identical: www CNAME to your Private Space DNS target, then an A and AAAA record on the apex pointing at ApexToWWW.