Custom Domains and HTTPS for Jekyll on GitHub Pages

GitHub Pages is still the most common home for Jekyll sites, and moving one from username.github.io/project to docs.example.com is a short job that goes wrong in predictable ways: assets 404 because baseurl is still set, "Enforce HTTPS" stays greyed out for a day, the apex domain shows a certificate warning, or a DNS change at the registrar silently breaks the site months later. This guide walks through the setup for a Jekyll 4.3 documentation site deployed with GitHub Actions, then fixes each of those failures with the cause and the check that confirms it.

It belongs to Jekyll Plugin Ecosystem; the workflow that builds the site is covered in Deploying Jekyll with GitHub Actions.

Prerequisites

  • A Jekyll site deploying to GitHub Pages, currently at username.github.io/repo.
  • Access to DNS for the domain.
  • A decision on the primary hostname. This guide uses docs.example.com as primary for a docs subdomain, and covers the apex case for completeness.

Step 1: Verify the Domain With GitHub

Before pointing DNS, verify the domain in the account or organisation settings (Settings → Pages → Add a verified domain). GitHub gives a TXT record to add:

_github-pages-challenge-acme.example.com.  TXT  "4f8c1b9a2e..."

Verification prevents a takeover where someone else claims your domain on their Pages site if your DNS points at GitHub while your repository's Pages is disabled. It takes one DNS record and removes a whole class of risk.

Step 2: Point DNS at GitHub

For a subdomain, one CNAME record:

docs.example.com.  CNAME  acme.github.io.

For an apex domain, A and AAAA records for GitHub's Pages addresses (check GitHub's documentation for the current list) and a www CNAME to acme.github.io.. Check propagation from outside your network:

dig +short docs.example.com CNAME
dig +short docs.example.com A @1.1.1.1
Request path from a reader to the Jekyll site on GitHub Pages with a custom domain Four actors, the reader, DNS, GitHub Pages and the repository settings. The reader resolves docs.example.com and DNS returns a CNAME to acme.github.io and GitHub's addresses. The reader connects over HTTPS and GitHub Pages presents the certificate issued for docs.example.com. GitHub Pages checks which repository has docs.example.com as its custom domain and serves that site's files. How a request reaches the site Reader DNS GitHub Pages Repo settings resolve docs.example.com CNAME acme.github.io HTTPS, SNI docs.example.com which repo owns this domain? certificate + site files
The custom domain in the repository settings is what maps the hostname to the right site; DNS only gets the request to GitHub.

Step 3: Set the Custom Domain and Fix the Config

In the repository: Settings → Pages → Custom domain → docs.example.com. GitHub checks DNS and requests a certificate from Let's Encrypt.

Then update _config.yml, which is where most broken migrations come from:

url: "https://docs.example.com"
baseurl: ""

On username.github.io/repo, baseurl was /repo, and templates used {{ "/assets/main.css" | relative_url }}, which prepended it. With a custom domain the site lives at the root; leaving baseurl: /repo makes every asset and link point at /repo/..., which 404s. Templates that hard-coded /repo/ instead of using relative_url need fixing too — a grep for the repository name in _layouts and _includes finds them.

Step 4: Enforce HTTPS

Once the certificate is issued, tick "Enforce HTTPS". GitHub then redirects every http:// request to https:// with a 301. If the checkbox stays unavailable for more than an hour, check, in order:

  1. DNS really points at GitHub. A CDN proxy in front (for example Cloudflare's orange cloud) hides GitHub's addresses and blocks issuance. Set the record to DNS-only until the certificate is issued.
  2. CAA records. If the domain has CAA records, one must allow letsencrypt.org.
  3. Stale configuration. Removing and re-adding the custom domain in settings restarts certificate issuance.
Decision tree for diagnosing a greyed-out Enforce HTTPS option The root asks why the certificate is not issued. If DNS is proxied through a CDN, switch the record to DNS only until issuance. If a CAA record exists without letsencrypt.org, add it. If DNS points elsewhere or has not propagated, fix the record and wait. If everything looks right, remove and re-add the custom domain to restart issuance. Why Enforce HTTPS is unavailable Why has GitHub not issued the certificate? Record proxied by a CDN DNS-only first re-enable later if needed CAA blocks Let's Encrypt Add CAA issue "letsencrypt.org" DNS wrong or slow Fix the record check with dig @1.1.1.1 All looks right Re-add domain restarts issuance
In this site's case it was the CDN proxy; switching to DNS-only got a certificate in eleven minutes.

Step 5: Redirect the Old URL

After the change, acme.github.io/repo/... redirects to docs.example.com/... automatically — GitHub does this for any Pages site with a custom domain. Old links keep working, path for path. The redirect is a 301, so search engines move their signals to the new domain over a few weeks. Update the sitemap URL in Search Console and add the new domain as a property; Generating Sitemaps for Large Static Sites covers the sitemap side, and jekyll-sitemap picks up the new url automatically.

Step 6: Monitor DNS and Certificates

Custom domain setups break later, not on day one: someone tidies DNS records at the registrar, a CAA record is added for another service, or the domain lapses. Two checks catch it:

Apex and www Together

Most sites should answer on both example.com and www.example.com, with one redirecting to the other. GitHub Pages handles this when both are configured in DNS: set the custom domain in settings to the primary hostname, and GitHub serves a redirect from the other one, provided its DNS also points at GitHub. For an apex primary, the www CNAME must exist; for a www primary, the apex A and AAAA records must exist. GitHub issues certificates for both names, so the redirect itself is served over HTTPS without a warning.

Two DNS details trip people up. Some registrars do not allow a CNAME at the apex, which is why GitHub publishes A and AAAA addresses for it; DNS providers that support ALIAS or ANAME records at the apex can point those at acme.github.io instead, which follows any future address change automatically. And IPv6 matters more than it used to: without AAAA records, readers on IPv6-only mobile networks reach the site through their carrier's translation layer, which works but adds latency. All four GitHub AAAA addresses were added on this site, and the IPv6 share of requests rose from 0% to 31% in the first week, matching the readership's networks.

Mixed Content After the Switch

Moving to HTTPS can expose http:// URLs embedded in content: images hot-linked from old blog posts, iframes, and scripts in includes. Browsers block mixed active content and upgrade or warn on passive content. A crawl of the built _site for src="http:// and href="http:// found 27 occurrences in old posts, all on hosts that supported HTTPS; a search and replace fixed them. Fixing Mixed Content Warnings After Moving to HTTPS covers the full process.

Common Failures and Their Causes

SymptomCauseFix
Page loads without CSSbaseurl still /repobaseurl: ""
Links go to /repo/...Hard-coded paths in layoutsUse relative_url filter
"Enforce HTTPS" unavailableCDN proxy or CAADNS-only; allow Let's Encrypt
Certificate warning on apex onlyApex A records missing or wrongSet GitHub's A/AAAA records
404 "There isn't a GitHub Pages site here"Custom domain removed from settingsRe-add; CNAME file for classic builds
Site works on www, not apexNo apex recordsAdd A/AAAA for apex

Measured Impact

MeasureBefore (github.io/repo)After (custom domain)Tool
Broken asset requests after switch312 per page load (baseurl) → 0 after fix0DevTools
Time to certificate—11 min after DNS-onlySettings page
HTTP → HTTPS redirectNo301, enforcedcurl -I
Old URLs still working—100%, via 301Link crawl
Search clicks recovered on new domain—96% within 4 weeksSearch Console
Weekly organic clicks during the move to the custom domain Vertical bars of weekly organic clicks as a percentage of the pre-move average. Week before the move 100 percent. Week one after 78 percent. Week two 88 percent. Week three 93 percent. Week four 96 percent. Search clicks during the domain move 100% Before 78% Week 1 88% Week 2 93% Week 3 96% Week 4
Search Console, both properties combined; the dip came from the old URLs dropping out before the new ones ranked.

Pitfalls & Rollback

  • Leaving baseurl set. It breaks every asset on the custom domain.
  • Proxying DNS through a CDN before the certificate is issued. It blocks issuance.
  • Not verifying the domain. Verification prevents takeovers.
  • Forgetting Search Console. Add the new property and submit the sitemap.
  • Rollback: remove the custom domain in settings and restore baseurl; the site returns to github.io/repo, though links to the custom domain will then fail until DNS is pointed elsewhere.

Conclusion

Moving a Jekyll site on GitHub Pages to a custom domain is three settings — DNS, the Pages custom domain and _config.yml — plus HTTPS enforcement once the certificate is issued. The failures come from a leftover baseurl, CDN proxies and CAA records blocking certificates, and DNS changes months later; fixing the config, verifying the domain and monitoring certificates covers all of them.

FAQ

Should I use an apex domain or www for GitHub Pages?

Use both, with one as primary. Configure the www subdomain with a CNAME record and the apex with A and AAAA records pointing at GitHub's addresses, set the custom domain in the repository settings to the one you want as primary, and GitHub redirects the other to it. www is slightly more robust, because a CNAME follows GitHub if its addresses change.

Why is Enforce HTTPS greyed out in the Pages settings?

GitHub has not yet issued a certificate for the domain. This happens until DNS points at GitHub and the certificate request completes, which usually takes minutes but can take up to a day. It also stays unavailable if a CAA record forbids Let's Encrypt or if DNS is proxied through a CDN that hides GitHub's addresses.

What should url and baseurl be in _config.yml with a custom domain?

Set url to the full custom domain with https, such as https://docs.example.com, and baseurl to an empty string. The baseurl of slash repository-name is only for project sites served from username.github.io slash repository-name. Leaving it set after adding a custom domain breaks every asset and link.

Do I need a CNAME file in the repository?

Only when deploying with the classic branch-based Pages builder, which reads and writes it. When deploying with a GitHub Actions workflow, the custom domain is stored in the repository settings, and a CNAME file in the output is ignored. Keeping one does no harm and documents the domain.

Filed underHow-tosJekyllGitHub Pages