GitHub Pages ties a custom domain to your site through two settings that are easy to blur. Account-level domain verification proves you control the domain and stops other GitHub accounts from publishing to it. The repository’s Pages setting binds one hostname to one site, while DNS records only route traffic and prove nothing to GitHub.
The documented addresses, checked against a live Pages site
Live capture, 26 September 2026. pages.github.com is itself served by GitHub Pages from a custom subdomain. Its A and AAAA answers, and the registration of the address block:
$ dig +nocmd @1.1.1.1 pages.github.com A +noall +answer
pages.github.com. 2079 IN CNAME github.github.io.
github.github.io. 2079 IN A 185.199.111.153
github.github.io. 2079 IN A 185.199.110.153
github.github.io. 2079 IN A 185.199.109.153
github.github.io. 2079 IN A 185.199.108.153
$ dig +nocmd @1.1.1.1 pages.github.com AAAA +noall +answer
pages.github.com. 3468 IN CNAME github.github.io.
github.github.io. 3468 IN AAAA 2606:50c0:8003::153
github.github.io. 3468 IN AAAA 2606:50c0:8000::153
github.github.io. 3468 IN AAAA 2606:50c0:8002::153
github.github.io. 3468 IN AAAA 2606:50c0:8001::153
$ curl -s https://rdap.db.ripe.net/ip/185.199.108.153 | jq -r '"\(.handle) \(.name) \(.country)"'
185.199.108.0 - 185.199.111.255 US-GITHUB-20170413 US
What to read from it:
- The subdomain is a CNAME to
github.github.io, the Pages domain of thegithuborganization, with no repository path. That is exactly the documented subdomain pattern. - RIPE registers the whole 185.199.108.0 to 185.199.111.255 block to GitHub, so an A record inside it at least points at GitHub’s network. It doesn’t tell you which site will answer; the repository binding decides that.
All eight addresses match the list in GitHub’s documentation today:
| Family | Addresses documented by GitHub | Seen on 26 September 2026 |
|---|---|---|
| IPv4 (A) | 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 | All four |
| IPv6 (AAAA) | 2606:50c0:8000::153, 2606:50c0:8001::153, 2606:50c0:8002::153, 2606:50c0:8003::153 | All four |
Live capture, 26 September 2026. The HTTP response, filtered to the lines that identify Pages:
$ curl -sI https://pages.github.com/ | grep -iE "^(HTTP|server|x-github-request-id|via|x-served-by|x-fastly-request-id)"
HTTP/2 200
server: GitHub.com
x-github-request-id: 0FF2:4409C:4B03F5:4E1888:6AB7D85F
via: 1.1 varnish
x-served-by: cache-icn1450087-ICN
x-fastly-request-id: 38b44db3c68ee209850df83c148d0c84a70aa233
server: GitHub.com together with an x-github-request-id is how a Pages response identifies itself. The via, x-served-by, and x-fastly-request-id lines come from the cache layer in front of Pages; this request was answered by a node labelled ICN.
Verify the domain before pointing DNS at GitHub
- Open Settings from your profile menu, or your organization’s settings, then Pages under “Code, planning, and automation”, and select Add a domain.
- Publish the TXT record GitHub shows. Its name is
_github-pages-challenge-USERNAME.example.comfor a personal account, or_github-pages-challenge-ORGANIZATION.example.comfor an organization. - Check that it is visible:
dig _github-pages-challenge-USERNAME.example.com +nostats +nocomments +nocmd TXT. - Return to the Pages settings, choose Continue verifying, then Verify.
- Leave the TXT record in place. GitHub says to keep it so the domain stays verified.
Once the domain is verified, only repositories owned by your account or organization can publish a Pages site to it or to its immediate subdomains, such as docs.example.com. If GitHub reports that a domain is already in use, verifying it is how you make it available to yourself.
Verification has a limit. GitHub strongly recommends against wildcard DNS records such as *.example.com, which it says leave you at “an immediate risk of domain takeovers, even if you verify the domain.”
Bind the hostname in the repository
In the repository, open Settings → Pages, enter the custom domain, and save it before you create the serving DNS records. GitHub warns that DNS configured without the repository binding could let someone else host a site on one of your subdomains.
What happens next depends on how the site is published:
- From a branch: saving creates a commit that adds a
CNAMEfile to the root of the source branch. Pull it before your next local build, and make sure your static site generator doesn’t drop it from the output. - With a custom GitHub Actions workflow: no
CNAMEfile is created, and an existing one is ignored. The repository setting is the binding.
GitHub’s troubleshooting page adds the constraints. The file must be named CNAME in uppercase and hold a single domain; a site can’t use more than one apex domain; and a site can’t combine an apex domain with a custom subdomain other than www.
DNS records by hostname type
| Hostname | Record | Value |
|---|---|---|
www or another subdomain |
CNAME | <user>.github.io or <organization>.github.io, without the repository name |
| Apex | A, four records | 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 |
| Apex, IPv6 (optional) | AAAA, four records | 2606:50c0:8000::153, 2606:50c0:8001::153, 2606:50c0:8002::153, 2606:50c0:8003::153 |
| Apex, if your DNS provider supports it | ALIAS or ANAME | <user>.github.io or <organization>.github.io |
GitHub recommends a www subdomain because, unlike an apex pinned with A records, it isn’t affected by changes to the IP addresses of GitHub’s servers. If both the apex and www have correct records, Pages redirects between them automatically, toward whichever name you set as the custom domain.
Live capture, 26 September 2026. An apex domain served by GitHub Pages:
$ dig +nocmd @1.1.1.1 opensource.guide A +noall +answer
opensource.guide. 3600 IN A 185.199.108.153
opensource.guide. 3600 IN A 185.199.109.153
opensource.guide. 3600 IN A 185.199.110.153
opensource.guide. 3600 IN A 185.199.111.153
All four documented addresses, with a one-hour TTL. An AAAA query for the same name returned no records that day: the IPv6 records are optional, and this site doesn’t publish them.
HTTPS: timing, enforcement, and the certificate
- Issuance. After the DNS check passes, GitHub requests a certificate from Let’s Encrypt.
- Timing. GitHub’s troubleshooting page says HTTPS can take up to an hour to become available after you configure the domain, and its custom-domain setup page says the Enforce HTTPS option can take up to 24 hours to appear.
- Stalled provisioning. If nothing has happened several minutes after you saved, remove the custom domain, enter it again, and save. That restarts provisioning.
- CAA. If you publish CAA records, at least one must allow
letsencrypt.org. CAA records and Let’s Encrypt covers inheritance andissuewild. - Enforcement. Enforce HTTPS redirects every HTTP request to HTTPS. Afterwards, fix any
http://asset URLs, or browsers will flag mixed content.
Live capture, 26 September 2026. The HTTP redirect and the certificate on opensource.guide, then the certificate on pages.github.com for contrast, read with OpenSSL 3.6.3:
$ curl -sI http://opensource.guide/ | grep -iE "^(HTTP|location|server)"
HTTP/1.1 301 Moved Permanently
Server: GitHub.com
Location: https://opensource.guide/
$ openssl s_client -connect opensource.guide:443 -servername opensource.guide </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates -ext subjectAltName
issuer=C=US, O=Let's Encrypt, CN=YR2
subject=CN=opensource.guide
notBefore=Aug 21 05:55:54 2026 GMT
notAfter=Nov 19 05:55:53 2026 GMT
X509v3 Subject Alternative Name:
DNS:opensource.guide
$ openssl s_client -connect pages.github.com:443 -servername pages.github.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -ext subjectAltName
issuer=C=US, O=Let's Encrypt, CN=YR1
subject=CN=*.github.io
X509v3 Subject Alternative Name:
DNS:*.github.com, DNS:*.github.io, DNS:*.githubusercontent.com, DNS:github.com, DNS:github.io, DNS:githubusercontent.com
- The apex custom domain has its own Let’s Encrypt certificate naming only
opensource.guide, valid for 90 days. A custom domain of yours gets the same kind of certificate. - The plain HTTP request gets a 301 to HTTPS, which is the behavior Enforce HTTPS turns on.
pages.github.comis a special case. It sits undergithub.com, so GitHub’s shared wildcard certificate covers it. Don’t expect your custom domain’s certificate to look like that one.
Symptoms and where to look
| Symptom | Where to look |
|---|---|
| GitHub says the domain is already in use | Verify the domain for your account or organization, then add it again |
| A branch-published site loses its domain after each deploy | The build replaced the source branch contents and dropped the CNAME file |
An Actions-published site has no CNAME file |
Nowhere; that is expected, and the repository setting is the binding |
| The DNS check in Settings fails | Leftover A records from an old host, a CNAME that includes the repository path, or a CNAME to the wrong account’s github.io name |
| Enforce HTTPS is unavailable | Wait up to 24 hours, check CAA, then remove and re-add the domain |
| Browsers warn about mixed content | http:// asset URLs in your pages |
| The old domain still appears after a change | Your browser cache; GitHub suggests clearing it |
Retire a Pages site without leaving a takeover
GitHub warns that a disabled Pages site with a custom domain still configured is at risk of a domain takeover. When you unpublish, select Remove next to the custom domain under Settings → Pages, then delete the A, AAAA, or CNAME records that point at GitHub. Keep the verification TXT record so the domain stays verified. Dangling DNS and subdomain takeover explains how stale records get claimed.
How GitHub Pages compares with other static hosts
Each cell below comes from the platform’s current documentation, except the Cloudflare Pages IPv6 cell, which comes from our own capture.
| GitHub Pages | Vercel | Netlify | Cloudflare Pages | |
|---|---|---|---|---|
| Apex on another provider’s DNS | Four A records (optionally four AAAA), or ALIAS/ANAME to your github.io name |
A record with the value from the project’s domain settings | ALIAS, ANAME, or flattened CNAME to apex-loadbalancer.netlify.com, or an A record to 75.2.60.5 |
Not possible: the apex must be a Cloudflare zone in the same account |
| Subdomain record | CNAME to <user>.github.io |
CNAME to the project’s own target | CNAME to <site>.netlify.app |
CNAME to <project>.pages.dev |
| IPv6 for the custom domain | Four documented AAAA addresses | Not supported | Not through the apex load balancer | AAAA answered for our own Pages apex on 26 September 2026 |
| Certificate issuer | Let’s Encrypt | Let’s Encrypt | Let’s Encrypt | Chosen by Cloudflare; CAA must allow Let’s Encrypt, Google Trust Services (pki.goog), and SSL.com |
The platform guides have the details and live checks: Vercel custom domain DNS, Netlify external DNS vs Netlify DNS, and Cloudflare Pages custom domains.