How do you put a GitHub Pages site on a custom apex domain with HTTPS, and what DNS records and safeguards does that involve?
answer
- the apex cannot take a CNAME
- two configurations that must agree
- a file in the branch remembers the domain
- a certificate you never requested
- TXT verification stops dangling-DNS takeover
basics
~20 sPoint the apex at GitHub's documented Pages A and AAAA addresses (or an ALIAS record to <owner>.github.io), set the domain in the repository's Pages settings, wait for GitHub to provision a Let's Encrypt certificate, then enable Enforce HTTPS. Verify the domain to block takeover.
solid answer
~50 sFor an apex like `example.com` you cannot use CNAME, so you publish GitHub's documented Pages **A** records (and their **AAAA** equivalents for IPv6), or an **ALIAS/ANAME** record to `<owner>.github.io` if your DNS provider supports flattening. A `www` subdomain instead takes a plain **CNAME** to `<owner>.github.io` — the user or org host, never the repository path. Then set the domain under Settings → Pages, which on a branch source commits a `CNAME` file to the publishing source. Once DNS resolves, GitHub requests a Let's Encrypt certificate automatically; when it is issued, tick **Enforce HTTPS** so plain HTTP redirects. Two operational points separate a good answer: verify the domain for your user or organization with the `_github-pages-challenge-<owner>` TXT record so a stale DNS entry cannot be claimed by someone else's repository, and remember that a deploy which rewrites the branch and drops the `CNAME` file silently clears the custom domain.
code
bash · 10 lines# Check both halves of a Pages custom-domain setup
dig +short example.com A
dig +short example.com AAAA
dig +short www.example.com CNAME # expect <owner>.github.io.
dig +short TXT _github-pages-challenge-myorg.example.com
# What the branch publishing source must contain
cat CNAME # -> example.com
curl -sI http://example.com | head -n 3 # expect a 301 to https when enforcedgo deeper
Know the two record shapes: a subdomain takes a CNAME to <owner>.github.io, and an apex takes A/AAAA or an ALIAS record. Know that GitHub obtains the TLS certificate for you at no cost.
Explain how the pieces connect — the DNS record, the custom domain setting, the CNAME file written to a branch source, and the automatic Let's Encrypt provisioning that must finish before Enforce HTTPS can be enabled.
Show operational judgment: diagnose why a certificate never issued, explain how a force-pushed deploy wipes the CNAME file, and raise dangling-DNS takeover with the _github-pages-challenge TXT verification as the fix.
Own domain hygiene as policy: verified domains at the organization level, a decommissioning checklist that removes DNS in the same change as the repository, and a clear rule that anything needing custom security headers does not belong on Pages at all.
## The DNS half A custom domain on Pages is two independent configurations that must agree: DNS pointing at GitHub, and GitHub knowing which repository that hostname belongs to. Either one alone gives you a broken site. **Subdomain (`www.example.com`, `docs.example.com`).** One `CNAME` record pointing at `<owner>.github.io`. The target is the *account* host, not `<owner>.github.io/<repo>` — a CNAME's right-hand side is a hostname and cannot carry a path. GitHub resolves which repository to serve from the domain configured in that repository's settings. **Apex (`example.com`).** DNS forbids a CNAME at the zone apex, because the apex must also hold `SOA` and `NS` records. Two legal options: - `A` records to GitHub's published Pages addresses, plus `AAAA` records to their IPv6 counterparts. GitHub documents the exact set; take the values from the current Pages documentation rather than from memory or an old blog post, since the anycast set has changed before. - An `ALIAS`, `ANAME` or "CNAME flattening" record to `<owner>.github.io`, if your DNS provider offers one. This is usually the better choice: the provider resolves the target for you, so an address change on GitHub's side never requires an edit to your zone. A reasonable production layout is the apex handled by A/AAAA or ALIAS and `www` as a CNAME, with GitHub configured for one of them — it redirects between the two forms automatically. ## The GitHub half Enter the hostname in **Settings → Pages → Custom domain**. With a branch publishing source, saving that writes a file literally named `CNAME` — containing just the hostname — to the root of the publishing source. That file is the durable record of the setting, and it is the thing that breaks: any deploy that replaces the branch wholesale (a force-pushed `dist/` directory, a generator that cleans the branch) removes it, and the custom domain silently reverts. Fix it in the build, by emitting the `CNAME` file into the output directory, not by re-typing the domain after every deploy. GitHub then runs a DNS check. Until it passes, the site is not served on the custom name, and the settings panel says why — the error messages distinguish "not pointed at Pages" from "pointed at Pages but claimed elsewhere". ## HTTPS Once DNS resolves correctly, GitHub requests a certificate from **Let's Encrypt** on your behalf, with no action from you. Provisioning is not instant — it can take up to roughly 24 hours — and until it completes, the **Enforce HTTPS** checkbox is unavailable. Enabling it makes Pages redirect plain HTTP to HTTPS; leaving it off means the site is reachable over unencrypted HTTP, which is a finding in any review. Renewal is automatic thereafter. The usual reasons provisioning fails are worth knowing: the record still points somewhere else or a stale record lingers; the domain sits behind a proxying CDN in full-proxy mode so the ACME challenge never reaches GitHub; or a `CAA` record on the zone forbids Let's Encrypt from issuing. Changing the custom domain resets the process, so removing and re-adding it is the standard way to force a fresh attempt after fixing DNS. ## The safeguard people forget: domain takeover This is the part that turns the question from configuration into judgment. If `docs.example.com` points at `<owner>.github.io` but no repository in your account currently claims that hostname, someone else can create a repository, set that domain, and serve their content on your name — a classic dangling-DNS takeover. It is a real, exploited class of bug, and it fires exactly when a team archives or deletes a docs repository without cleaning up DNS. The defence is **domain verification**: in your user or organization settings, add the `_github-pages-challenge-<owner>` TXT record that GitHub gives you. Once verified, only repositories inside that account may serve Pages on the domain and its subdomains. The operational rule that goes with it is simple — when you retire a Pages site, remove the DNS record in the same change that removes the repository, and never leave a record pointing at a host you no longer claim. ## What HTTPS on Pages does not give you You get a valid certificate and a redirect. You do not get control over response headers: no custom `Content-Security-Policy`, `Strict-Transport-Security`, or cache-control tuning, because there is no server configuration surface. If your threat model or compliance requirement needs specific headers, Pages is the wrong host and a CDN or static host with edge configuration is the right one.
- Why prefer an ALIAS or ANAME record over A records for the apex?Because the provider resolves `<owner>.github.io` at query time, your zone keeps working if GitHub changes the address set — no manual edit, no window where the site is down because you were copying stale IPs from an old article. A records are the fallback when the DNS provider offers no apex-flattening record type.
- The custom domain keeps resetting to blank after every deploy. What is the cause?The deploy replaces the publishing source branch and deletes the `CNAME` file that stores the hostname. Have the build write that file into the output directory — most static generators support a static/public passthrough folder for exactly this — or move to the Actions publishing source, where the domain lives in the Pages settings rather than in the branch.
- What is a Pages domain takeover, and what stops it?A DNS record still points at `<owner>.github.io` after the repository serving that hostname is gone, so an attacker creates their own repository claiming the domain and serves content on your name. Verifying the domain via the `_github-pages-challenge-<owner>` TXT record restricts it to your account, and removing retired DNS records closes the gap entirely.
- Enforce HTTPS is greyed out hours after DNS was fixed. What do you check?Whether the certificate has actually been issued: confirm DNS resolves only to GitHub with no leftover records, check for a `CAA` record blocking Let's Encrypt, and disable full CDN proxying so the ACME challenge reaches GitHub. Removing and re-saving the custom domain restarts provisioning once the underlying problem is fixed.
saying these in an interview costs you the question
- Putting a CNAME record at the zone apex
- Pointing a CNAME at owner.github.io/repo, which is not a hostname
- Assuming you must buy or upload a TLS certificate
- Leaving Enforce HTTPS off once the certificate exists
- Leaving DNS pointing at Pages after deleting the repository