Connecting a Custom Domain to GitHub Pages — 4 Steps (Cloudflare DNS · CNAME · HTTPS · Re-Indexing)
A write-up of moving taystudio.github.io to taystudios.com on 2026-05-09. As of 6/5 the first 30 days of post-migration data are in, so I'm including the actual numbers too.
Why move
.github.io works fine. For Korean SEO specifically there's barely a difference. But four things bothered me.
- Domain authority starts at zero —
.github.iois on the Public Suffix List, so browsers and search engines treat subdomains as separate sites.taystudio.github.ioandsomeone.github.ioare isolated from each other — good for safety, but authority never accumulates. - English AdSense RPM —
.github.ioURLs read as "developer blog" in English-speaking markets, so general-audience CTR is low. RPM lands around$1~3. With.compeople report$3~8. - Press / editorial backlinks — journalists and reputable blogs tend to skip
.github.ioURLs. Not perceived as a serious source. - Brand — having "github" in your URL just weakens brand recognition.
English market + AdSense + brand — all three were decisive levers for me. Timing matters most, though. Once you've got hundreds of URLs and backlinks pointing at the old domain, migration cost explodes. My site had ~100 URLs at the time, which was the right moment.
4-step overview
① Cloudflare DNS (4 A records + CNAME www)
② GitHub repo CNAME file (1 line)
③ Site-wide rewrite (canonical · sitemap · robots)
④ Re-index everywhere (GSC · Naver · Bing · Daum)
Zero downtime. Both domains stay live during the transition.
1. Cloudflare DNS
I bought the domain at Cloudflare Registrar (any registrar works — Namecheap, Gandi, GoDaddy, etc.). DNS is also Cloudflare — fastest and free.
UI — where to click
- dash.cloudflare.com → click your site → left menu DNS → Records.
Add recordbutton → add the 5 records below:
| Type | Name | Content (Value) | Proxy status |
|---|---|---|---|
| A | @ |
185.199.108.153 |
DNS only (gray cloud) |
| A | @ |
185.199.109.153 |
DNS only |
| A | @ |
185.199.110.153 |
DNS only |
| A | @ |
185.199.111.153 |
DNS only |
| CNAME | www |
<your username>.github.io |
DNS only |
Type @ in the Name field and it auto-resolves to apex (taystudios.com). For www, just type www — Cloudflare expands it to www.taystudios.com.
Two traps
- Register all 4 A records. GitHub Pages load-balances across 4 IPs. If you only set one, the site goes down whenever that IP does.
- Start with DNS only (gray cloud). If you turn the proxy on (orange cloud), GitHub Pages' Let's Encrypt issuer can't reach the origin IP, and cert issuance fails. Turn proxy on after the cert is issued.
Verify
dig taystudios.com +short
# 185.199.108.153
# 185.199.109.153
# 185.199.110.153
# 185.199.111.153
dig www.taystudios.com +short
# taystudio.github.io.
# 185.199.108.153
# ...
Global resolver cache propagation typically takes 30 minutes to 24 hours. Mine resolved in Korea within an hour.
2. The CNAME file in your repo
This is the file that tells GitHub Pages "this repo serves this domain."
Make the file
At your repo root, create a file named exactly CNAME (no extension, all uppercase). One line inside:
taystudios.com
The apex domain, no www. Done.
GitHub UI side
- Repo page → top Settings tab.
- Left menu Pages.
- Custom domain field → enter
taystudios.com→ Save. - A few minutes later (5~30 min) you'll see
✓ DNS check successful. - The Enforce HTTPS checkbox below becomes enabled — tick it.
If you enter the domain into Custom domain, GitHub creates the CNAME file at the repo root for you automatically. Creating it manually (step 1) does the same thing.
Let's Encrypt cert
GitHub auto-issues an SSL cert via Let's Encrypt. 5 to 30 minutes. Once issued, the Enforce HTTPS checkbox becomes clickable (was grayed out).
Auto-renewed every 90 days. Cost zero. Configuration zero.
→ After the cert is issued you can flip Cloudflare proxy on (optional).
3. Site-wide rewrite
The most SEO-critical part of the migration. If a single URL still points to the old domain, Google may keep treating the old domain as the canonical, and authority won't transfer.
What needs to change
| Area | Change |
|---|---|
<link rel="canonical"> |
taystudios.com/... |
<meta property="og:url"> |
same |
<link rel="alternate" hreflang> |
same |
<loc> in sitemap.xml |
same |
Sitemap: URL in robots.txt |
same |
llms.txt (if you have one) |
same |
schema.org JSON-LD @id · url |
same |
| Absolute internal links | taystudio.github.io → taystudios.com |
| IndexNow key file URL | same |
How I handled it
My build script (blog/scripts/build.py) reads site.json's siteUrl once and rewrites everything from that. One value changed, one build run, done.
If you don't have a build script:
# bulk-rewrite old domain → new domain
grep -rl "taystudio.github.io" . | xargs sed -i '' 's|taystudio.github.io|taystudios.com|g'
(macOS sed is -i '', Linux is -i)
Push to GitHub after the rewrite — auto-deploy from there.
4. Re-index everywhere
To Google · Naver · Bing · Daum, .com is a brand new domain. Data accumulated on the old property doesn't auto-transfer, so register fresh.
Google Search Console
- search.google.com/search-console → top-left Add property.
- Pick URL prefix → enter
https://taystudios.com. - Ownership verification — the HTML tag method is easiest. Grab the
<meta name="google-site-verification" content="...">line, drop it in<head>, build → verify. - Left menu Sitemaps → enter
sitemap.xml→ submit. - URL Inspection → enter URLs of priority pages (home · popular posts) → Request indexing. ~10/day quota.
→ Bing Webmaster Tools can auto-import sitemaps from GSC. Doing GSC also covers Bing.
Naver SearchAdvisor (Korean market)
- searchadvisor.naver.com → login → top-right Site management.
- Add site →
https://taystudios.com. - Ownership — download the HTML verification file, upload to your site root. Build → verify.
- Left menu Request → Submit sitemap →
sitemap.xml. - Request → Crawl webpage → one URL per line (50/day).
→ Naver carries 80%+ of Korean-market traffic. If you target Korea, Naver is more urgent than GSC.
Bing Webmaster Tools
- bing.com/webmasters → log in with a Microsoft account.
- Add site → auto-import from GSC is recommended.
- From the old domain's property you can use Site Move to send an explicit move signal.
Daum Webmaster Tools (Korean — Kakao)
(Not called "SearchAdvisor". The UI menu is still labeled "Webmaster Tools". URL is webmaster.daum.net.)
- webmaster.daum.net → Kakao login.
- Add site →
https://taystudios.com. - Ownership — meta tag or a verification token added to the first line of
robots.txt. - Sitemap menu → submit
sitemap.xml. - Request crawl → individual URLs.
IndexNow (bonus)
Single ping to Bing · Yandex · Naver · Seznam. Generate a key once, drop the URL in robots.txt, done.
curl "https://api.indexnow.org/IndexNow?url=https://taystudios.com/&key=<your-key>"
What DNS and CNAME actually are
Worth understanding once — then the 4 steps above make sense.
DNS = phonebook
Humans remember names (taystudios.com); computers only know IPs (185.199.108.153). DNS translates name → IP.
How browsers find the IP (5 hops)
(when resolver cache is empty)
Browser
→ ISP DNS (KT · SKB · Google 8.8.8.8 ...)
→ Root nameserver (".")
→ TLD nameserver (".com")
→ Domain nameserver (Cloudflare)
→ A record response (185.199.108.153)
In practice the resolver caches results based on TTL (default 1 hour), so every lookup doesn't hit root. When you buy a new domain or change records, resolver caches around the world are empty and have to walk all 5 hops = DNS propagation.
A vs CNAME
A record — domain → IP directly:
taystudios.com A 185.199.108.153
CNAME — domain → another domain (alias):
www.taystudios.com CNAME taystudio.github.io
When a resolver gets a CNAME it looks up the A record for that other domain. One extra hop, but the alias automatically follows whenever the target's IP changes.
CNAME at apex (zone root) — forbidden by RFC 1912
taystudios.com itself = no CNAME. It collides with other record types (MX · NS), and the standard forbids it.
www.taystudios.com · blog.taystudios.com = CNAME OK.
→ That's why apex uses 4 A records and www uses CNAME. Cloudflare supports "CNAME flattening" to fake apex CNAME, but GitHub Pages' official recommendation is 4 A records.
Host header
GitHub Pages' 4 IPs (185.199.108~111.153) are shared across millions of sites. How does your site get served?
HTTP/1.1 GET /
Host: taystudios.com ← this is the key
The browser sends a Host header → GitHub looks up CNAME file == "taystudios.com" internally and maps to your repo. → The 1-line CNAME file is the actual binding.
My measurements — 30-day sandbox window
Data from the 30 days after 5/9 migration:
| Date | GSC impressions | Naver impressions |
|---|---|---|
| 5/9 (migration) | 0 | (residual on old domain) |
| 5/14 (Week 1) | 450 peak | spike begins |
| 5/24 (Week 3) | 10 (dip) | 7,600 peak |
| 6/2 (Week 4) | ~0 | 2,000~3,000/day steady |
Google clearly entered sandbox. 6~12 weeks expected. Naver has no sandbox — recovers immediately. Full retrospective in the Google Sandbox 30-day post.
If you're doing the same migration
- Move early, while URL count is low. Pre-backlink is the cheapest time. Once you have 100+ URLs, redirect / canonical work explodes.
- Zero downtime — both domains stay live. Visitors landing on the old domain get auto-redirected by GitHub to the new one (as long as the old repo's CNAME file is removed).
- Canonical consistency — bulk-rewrite with sed or your build script in one pass. If even one URL still points to the old domain, authority transfer fails.
- Don't panic looking at Google alone — sandbox keeps impressions near zero for ~a month. Naver · Bing · Cloudflare together show the site is actually fine.
The domain swap itself takes about an hour. Authority transfer is the 1~3 month wait.
Related posts
- Google Sandbox After 30 Days — GSC · Naver · Cloudflare tracking
- Naver SearchAdvisor — Complete Registration Guide — 80% of Korean-market traffic
- GSC vs Naver vs Cloudflare — three data sources compared
Comments