Problem
You built a site, pushed it to a GitHub repository, and GitHub Pages serves it happily at your-username.github.io. Then you point your own domain at it, wait, and get a blank page, a 404, a certificate warning, or a Pages settings box that says the domain “is not properly configured.” The DNS looks right to you. It usually isn’t quite — and the reasons are the same two or three every time.
GitHub Pages custom domains fail in a small, predictable set of ways: a CNAME on the apex where an A record belongs, records that resolve to the wrong place, or HTTPS that never finishes provisioning. None of them are mysterious once you know what GitHub actually expects at the DNS layer.
What GitHub Pages needs from DNS
GitHub Pages serves your custom domain from a fixed set of anycast addresses. For an apex domain (example.com, no subdomain in front) you publish A records to all four of these IPv4 addresses:
185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
and, so IPv6-only clients can reach you, the four matching AAAA records:
2606:50c0:8000::153
2606:50c0:8001::153
2606:50c0:8002::153
2606:50c0:8003::153
For a subdomain — www.example.com, blog.example.com, docs.example.com — you don’t use those addresses. You publish a single CNAME pointing at your GitHub Pages hostname:
www.example.com. CNAME your-username.github.io.
Note the target is your-username.github.io (or your-org.github.io for an organization site), not the repository name and not a made-up hostname. GitHub routes by the CNAME file it stores for the site, so the DNS just has to get traffic to GitHub’s front door.
The apex CNAME trap
This is the single most common way the connection fails. Your host or a tutorial tells you to “CNAME your domain to your-username.github.io,” you enter that at the bare example.com, and either the panel rejects it or the site never comes up.
The bare apex cannot hold a CNAME. It’s not a GitHub restriction — it’s RFC 1034 §3.6.2: a CNAME can’t coexist with any other record at the same name, and the apex of every zone is required to carry SOA and NS records. A CNAME there would collide with them, so a correct nameserver refuses it.
So at the apex you have two legitimate options:
- The four A records (plus AAAA) listed above. This is what GitHub documents and what works everywhere.
- ALIAS / ANAME / CNAME flattening, if your DNS provider offers it — Cloudflare calls it CNAME flattening, Route 53 calls it an ALIAS record, others call it ANAME. You enter
your-username.github.ioas the target and the provider resolves it to A records at serve time, giving you CNAME-like behavior without an illegal apex CNAME.
Subdomains have no such rule. www and friends take the CNAME directly. That asymmetry — CNAME on www, addresses on the apex — is exactly the thing people get backwards.
Verify the domain first, then set it
Do this in the right order and you avoid a nastier problem than a broken site: someone else claiming your domain.
GitHub lets you verify a domain (in your account or organization settings, under Pages) before you attach it to a repository. Verification proves you own the name so that no other GitHub user can point their Pages site at it. Verify first, then configure the custom domain on the repository once DNS resolves. Skipping verification isn’t fatal, but it leaves a takeover gap, and there’s no reason to leave it open.
When you set the custom domain in Settings → Pages, GitHub writes a file named CNAME into your repository containing the domain. If you also keep a CNAME file in your source and the two disagree — say a build overwrites it — the site flips between domains or drops the custom one entirely. Pick one source of truth: either manage it through the Settings box or commit the file, not both fighting each other.
The HTTPS wait is normal (up to a point)
Once DNS points at GitHub, GitHub requests a Let’s Encrypt certificate for your domain automatically. Until that certificate is issued, the Enforce HTTPS checkbox is greyed out and the site may briefly serve a certificate for the wrong name. This is expected and can take up to 24 hours. It is not something you fix by clicking harder.
Two things make it stall past a day:
- A CAA record that excludes Let’s Encrypt. CAA records restrict which certificate authorities may issue for your domain. If you have any CAA record and it doesn’t list
letsencrypt.org, GitHub’s issuance quietly fails. Either add Let’s Encrypt to the CAA set or remove the record. - A proxying CDN in front of GitHub. If you put Cloudflare’s proxy (orange cloud) in front of the records, it can intercept the domain-validation challenge and the certificate never issues. Set the records to DNS-only during provisioning; you can revisit proxying afterward, knowing it changes how HTTPS and caching behave.
If DNS is correct, there’s no CAA blocking Let’s Encrypt, nothing is proxying, and it’s still stuck after a day: remove the custom domain in Settings, save, wait a minute, and re-add it to force a fresh provisioning attempt.
Check it with DechoNet
- DNS Lookup reads the live A, AAAA, and CNAME records for your name so you can confirm the apex actually carries all four GitHub IPs (and the AAAA set), that
wwwis a CNAME toyour-username.github.io, and that you didn’t accidentally leave a CNAME on the apex. It also shows any CAA record that might be blocking the certificate. - DNS Propagation checks the records across resolvers in different networks at once — the way to tell “I made a mistake” apart from “it just hasn’t spread yet” after you edit records at your registrar.
- SSL Check shows which certificate is actually being served and for which names, so you can confirm the Let’s Encrypt certificate finished provisioning and covers both the apex and www rather than a stale or mismatched one.
Resolution Checklist
- At the apex, publish all four A records (
185.199.108.153,.109.153,.110.153,.111.153) and the four AAAA records. Never a CNAME on the bare apex. - At www (or any subdomain), publish a single CNAME to
your-username.github.io— not the repo name, not an IP. - If your provider has ALIAS/ANAME/flattening and you’d rather not hand-copy IPs, use it at the apex targeting
your-username.github.io. - Verify the domain in GitHub before attaching it, then set the custom domain in Settings → Pages and let GitHub manage the
CNAMEfile (don’t also commit a conflicting one). - Confirm the records resolve to GitHub across several resolvers before deciding anything is wrong; wait out the TTL.
- Leave Enforce HTTPS alone for up to 24 hours; if it stalls, check for a CAA record that excludes
letsencrypt.organd remove any proxying CDN during provisioning.
When to Escalate
- If DNS resolves to all four GitHub IPs, propagation agrees, and the site still 404s, the problem has moved off DNS: check that the repository’s Pages source (branch and folder) is set and that a build actually published — a green DNS path can’t help an unpublished site.
- If the certificate is still unissued after a day with correct DNS, no blocking CAA, and no proxy in the way, that’s the point to open a support thread — provisioning that never completes on a clean configuration is a GitHub-side fault, and they’ll want the exact domain and the timestamp you configured it.
- If two names disagree — the apex works but
wwwdoesn’t, or vice versa — you’re missing one of the two records, not misconfiguring the one that works. Add the missing apex A/AAAA or the missingwwwCNAME rather than changing the good one.
Check your own domain now
Free, no sign-up. Runs the exact check this guide describes and shows what to fix.