Connecting a Custom Domain to GitHub Pages — 4 Steps (Cloudflare DNS · CNAME · HTTPS · Re-Indexing)

광고

This is a write-up of how I moved taystudio.github.io over to taystudios.com back 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 alongside the steps.

Why move

To be honest, .github.io works perfectly fine. For Korean SEO specifically there's barely any difference. Even so, four things kept nagging at me, and in the end they were enough to make me move.

  • Domain authority starts at zero. .github.io is on the Public Suffix List, so browsers and search engines treat its subdomains as separate sites. That's how taystudio.github.io and someone.github.io stay isolated from one another — safe, sure, but it also means your authority never really accumulates.
  • English AdSense RPM is the one I cared about. In English-speaking markets a .github.io URL reads as "developer blog," so the general-audience CTR is low. RPM tends to sit around $1~3, whereas with a .com I've seen people report $3~8.
  • Press and editorial backlinks work against you too. Journalists and reputable blogs tend to skip .github.io URLs — they just don't read as a serious source.
  • And finally there's brand. Having "github" sitting in your URL weakens brand recognition all on its own.

The English market, AdSense, and brand — all three were decisive for me, so I went ahead and moved. The one thing I'd really stress, though, is timing. Once you've got hundreds of URLs and backlinks pointing at the old domain, the cost of migrating explodes. My site had around 100 URLs at the time, and in my experience that's about the sweet spot for making the move.

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)

There was zero downtime throughout. Both domains stay live and the transition happens gradually.


1. Cloudflare DNS

I bought the domain at Cloudflare Registrar, though honestly any registrar works — Namecheap, Gandi, GoDaddy, whatever you like. I kept DNS on Cloudflare too, since in my experience propagation is fastest there and it's free, so there was no reason to move it elsewhere.

UI — where to click

  1. Log in at dash.cloudflare.com, click your site, and go to DNS → Records in the left menu.
  2. Hit the Add record button and 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 the apex (taystudios.com). For www, just type www and Cloudflare expands it to www.taystudios.com.

Two traps

There are two traps here that I either hit or came close to hitting.

  • Register all 4 A records. GitHub Pages load-balances across these 4 IPs, so if you only set one, the whole site goes down whenever that single IP does.
  • Start with DNS only (gray cloud). If you flip the proxy on (orange cloud) too early, GitHub Pages' Let's Encrypt issuer can't reach the origin IP and cert issuance fails. It's safest to turn the proxy on only after the cert has been 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
# ...

Propagating to resolver caches around the world usually takes anywhere from 30 minutes to 24 hours. In my case it resolved within Korea inside an hour.


2. The CNAME file in your repo

This is the file that tells GitHub Pages "this repo serves this domain," and it really is the heart of the whole setup.

Make the file

At your repo root, create a file named exactly CNAME — no extension, all uppercase. Put a single line inside.

taystudios.com

The apex domain, no www. That's all it is.

GitHub UI side

  1. Go to your repo page and open the Settings tab at the top.
  2. Pick Pages in the left menu.
  3. In the Custom domain field, enter taystudios.com and hit Save.
  4. A few minutes later (5~30 min) you'll see the ✓ DNS check successful message.
  5. Once the Enforce HTTPS checkbox below it becomes enabled, tick it.

When you enter the domain into Custom domain, GitHub creates the CNAME file at your repo root for you automatically. Creating it by hand (step 1) does exactly the same thing.

Let's Encrypt cert

GitHub auto-issues an SSL cert through Let's Encrypt. It usually takes 5 to 30 minutes, and once it's issued the Enforce HTTPS checkbox, which was grayed out, becomes clickable.

It auto-renews every 90 days, costs nothing, and there's no configuration to do.

→ After the cert is issued you can head back to Cloudflare and flip the proxy on if you want (it's optional).


3. Site-wide rewrite

This is the most SEO-critical part of the migration. If even a single URL still points to the old domain, Google can end up treating that old domain as the canonical, and your authority simply 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.iotaystudios.com
IndexNow key file URL same

How I handled it

I'd written my build script (blog/scripts/build.py) so that it reads site.json's siteUrl once and rewrites everything from that single value. So I changed that one line, ran the build once, and it was done.

If you don't have a build script, sed will do the job.

# bulk-rewrite old domain → new domain
grep -rl "taystudio.github.io" . | xargs sed -i '' 's|taystudio.github.io|taystudios.com|g'

(On macOS sed is -i '', on Linux it's -i.)

Once the rewrite is done, push to GitHub and it auto-deploys from there.


4. Re-index everywhere

광고

To Google · Naver · Bing · Daum, a .com looks like a brand new domain. The data accumulated on the old property doesn't transfer automatically, so you have to register fresh.

Google Search Console

  1. Go to search.google.com/search-console and click Add property in the top-left.
  2. Pick URL prefix and enter https://taystudios.com.
  3. Verify ownership. Of the various methods, the HTML tag one is easiest — grab the <meta name="google-site-verification" content="..."> line, drop it into <head>, build, and verify.
  4. In the left menu under Sitemaps, enter sitemap.xml and submit.
  5. Under URL Inspection, enter the URLs of your priority pages (home · popular posts) and hit Request indexing. There's a quota of about 10 a day.

→ Bing Webmaster Tools can auto-import sitemaps from GSC, so doing GSC tends to cover Bing as well.

  1. Log in at searchadvisor.naver.com and open Site management in the top-right.
  2. Under Add site, enter https://taystudios.com.
  3. For ownership, download the HTML verification file, upload it to your site root, build, and verify.
  4. In the left menu, go to Request → Submit sitemap and add sitemap.xml.
  5. Under Request → Crawl webpage, add one URL per line (50/day).

→ Naver carries 80%+ of Korean-market traffic, so for me Naver was actually more urgent than GSC.

Bing Webmaster Tools

  1. Log in at bing.com/webmasters with a Microsoft account.
  2. Under Add site, the recommended route is to auto-import from GSC.
  3. If you have the old domain's property, you can use Site Move to send an explicit move signal.

Daum Webmaster Tools (Korean — Kakao)

(It's not called "SearchAdvisor." The UI menu is still labeled "Webmaster Tools," and the URL is webmaster.daum.net.)

  1. Log in at webmaster.daum.net with a Kakao account.
  2. Under Add site, enter https://taystudios.com.
  3. For ownership, use a meta tag, or add a verification token to the first line of robots.txt.
  4. In the Sitemap menu, submit sitemap.xml.
  5. Under Request crawl, add your individual URLs.

IndexNow (bonus)

This sends a single ping to Bing · Yandex · Naver · Seznam at once. You generate a key just once, drop the URL in robots.txt, and you're done.

curl "https://api.indexnow.org/IndexNow?url=https://taystudios.com/&key=<your-key>"

What DNS and CNAME actually are

When you're in the middle of a migration it's easy to start wondering what any of this even means, so I'll lay it out once. Once it clicks, you can see why the 4 steps above are shaped the way they are.

DNS = phonebook

People remember names (taystudios.com), but computers only know IPs (185.199.108.153). DNS is the thing that translates the name into the IP.

How browsers find the IP (5 hops)

(this is with an empty resolver cache)

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, of course, the resolver caches results based on TTL (default 1 hour), so not every lookup climbs all the way to root. But when you buy a new domain or change a record, resolver caches around the world are empty and have to walk all 5 hops — and that's exactly what DNS propagation is.

A vs CNAME

An A record points a domain directly at an IP.

taystudios.com   A   185.199.108.153

A CNAME points a domain at another domain (an alias).

www.taystudios.com   CNAME   taystudio.github.io

When a resolver gets a CNAME, it goes and looks up the A record for that other domain. It's one extra hop, but the upside is that the alias automatically follows along whenever the target's IP changes.

CNAME at apex (zone root) — forbidden by RFC 1912

taystudios.com itself can't take a CNAME. It collides with other record types (MX · NS), so the standard forbids it. On the other hand, www.taystudios.com · blog.taystudios.com can use CNAME just fine.

→ That's why the apex uses 4 A records and www uses a CNAME. Cloudflare can fake an apex CNAME with "CNAME flattening," but GitHub Pages' official recommendation is the 4 A records.

Host header

GitHub Pages' 4 IPs (185.199.108~111.153) are shared across millions of sites. So how does yours, of all of them, get served?

HTTP/1.1 GET /
Host: taystudios.com   ← this is the key

It comes down to the Host header the browser sends, working together with GitHub looking up the repo whose CNAME file == "taystudios.com" internally and mapping to it. So in the end, that 1-line CNAME file in your repo is what really does the binding.


My measurements — 30-day sandbox window

Here's the data from the 30 days following the 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

You can clearly see Google enter its sandbox — expect 6~12 weeks of it. Naver, on the other hand, has no sandbox, so it recovered right away. I've written up the full retrospective on this separately in the Google Sandbox 30-day post.


If you're doing the same migration

If you're about to do the same move, these are the four things I'd keep in mind after going through it myself.

  1. Move early, while your URL count is still low. Before the backlinks land is the cheapest moment. Once you're past 100+ URLs, the redirect and canonical work explodes.
  2. Zero downtime. Both domains stay live at the same time, so even if a visitor lands on the old domain, GitHub auto-redirects them to the new one (as long as you've cleared the CNAME file in the old repo).
  3. Canonical consistency matters. Whether you use sed or your build script, rewrite everything in a single pass. If even one URL still points to the old domain, authority transfer fails.
  4. Don't panic looking at Google alone. Because of the sandbox, impressions sit near zero for about a month. Only when you look at Naver · Bing · Cloudflare together can you see that the site is actually running just fine.

The domain swap itself is about an hour of work. It's just the authority transfer that takes a 1~3 month wait.

Share𝕏f

Comments