自訂網域很難嗎?DNS 的限制與實踐
自訂網域(Custom Domain)是 SaaS 常見的服務,只是我通常都沒花錢買。某次跟朋友聊天,她想聽聽我對內容平台的觀點,嘰哩呱啦分析完一堆後,我最後建議她,最好還是買個網域:
「你想想看,妳現在投入這麼多心力在經營內容,建立自己的品牌形象。如果妳的網址永遠都掛在別人的平台底下,就像在別人家租房子,雖然方便,但終究不是自己的。」
「有了自訂網域,妳的品牌就是自己的,無論未來平台怎麼變,妳的讀者都能透過固定的網址找到妳,這對品牌來說很重要。就算未來妳想換平台,也不會流失妳辛苦建立起來的流量。」
後來我在 Ghost 官方頁面看到類似說法
If you would like to make your site memorable and easy to find with a branded custom domain, then you can map any domain you own directly to your Ghost(Pro) publication. This is done by adding CNAME and A records within your domain's DNS settings.
如果你想讓你的網站更具品牌識別且容易被找到,你可以將你擁有的任何網域直接映射到你的 Ghost(Pro) 發布平台。這是透過在你的網域 DNS 設定中新增 CNAME 和 A 紀錄來完成的。
Regardless of how a user enters your publication's URL in their browser, they'll always be directed to the correct site.
無論使用者如何在瀏覽器中輸入你的發布平台網址,他們都會被導向正確的網站。
當時有股好奇,SaaS 服務是如何設計自訂網域的呢?他們之間有哪些差異?
DNS 的起源
既然說到網域,就離不開 DNS(Domain Name System,網域名稱系統),這是由 Paul Mockapetris 在 1980 年代發明的網路基礎技術。它像是網路的電話簿,能幫助使用者將(人類容易閱讀的)域名轉換成(電腦可以閱讀的)IP。也就是說呢,如果訪客想訪問「公視新聞網」,他在網址列輸入 news.pts.org.tw,DNS 會翻譯成 3.169.121.3 的 IP,讓瀏覽器可以造訪網站伺服器。
當然現在都用搜尋引擎了,只是 1980 年以前搜尋引擎還沒普及,而且 news.pts.org.tw 也比 medium.com/@themenightshow 更具品牌辨識度。
1980 年代以前的域名由 SRI(Stanford Research Institute,史丹佛研究機構)負責管理,你如果要登記網域,需要在他們上班時間打電話過去,他們會分配一個 IP 給你,並將網域跟 IP 的對應關係記在一個名為 hosts.txt 的檔案中。這個檔案會定期發布,電腦只需要查詢檔案就能知道該如何訪問網站。
顯然這個方案只能小規模運作,因為 SRI 的處理能量決定了系統上限。DNS 的出現改變了這點,在名為〈網域名稱-概念與功能〉的 RFC 882 中提到
The size of this table, and especially the frequency of updates to the table are near the limit of manageability. What is needed is a distributed database that performs the same function, and hence avoids the problems caused by a centralized database.
此表格的規模,尤其是更新頻率,已接近可管理的極限。所需的是一個分散式資料庫來執行相同功能,從而避免集中式資料庫所造成的問題。
我們可以把 DNS 的分散式資料庫看成網路上若干個節點,每個節點都儲存這世界一部分的 DNS 紀錄。這些節點之間以樹狀結構組織起來,當訪客要詢問特定網域的 IP 時,他可以從根節點開始查找,直到找到保存的葉節點為止。
常見的 DNS 紀錄
DNS 紀錄是保存在 DNS 伺服器上的資訊,從品牌的觀點來看,這些紀錄告訴訪客品牌的位置在哪?提供哪些服務?在現代網路技術中,也會使用 DNS 紀錄來驗證該網域確實是你擁有。如果有在使用 Cloudflare 當 DNS 服務,你可以去後台下載 DNS 紀錄,內容類似
;; SOA Record
kenwsc.com 3600 IN SOA arushi.ns.cloudflare.com. dns.cloudflare.com. 2050043919 10000 2400 604800 3600
;; NS Records
kenwsc.com. 86400 IN NS arushi.ns.cloudflare.com.
kenwsc.com. 86400 IN NS mustafa.ns.cloudflare.com.
;; A Records
blog.kenwsc.com. 1 IN A 62.xxx.xxx.xxx ; cf_tags=cf-proxied:true
;; CNAME Records
email.kenwsc.com. 1 IN CNAME mailgun.org. ; cf_tags=cf-proxied:false
;; MX Records
kenwsc.com. 1 IN MX 10 mxb.mailgun.org.
kenwsc.com. 1 IN MX 10 mxa.mailgun.org.
;; TXT Records
kenwsc.com. 1 IN TXT "v=spf1 include:mailgun.org ~all"
kenwsc.com. 1 IN TXT "google-site-verification=TRTXK-odhHBOXBVHXgEMgC9CXRZy58TkITSofr5uJF8"
常見的 DNS 紀錄有
SOA
權威記錄起始(Start of Authority),用來儲存有關網域區域的重要資訊。例如
kenwsc.com 3600 IN SOA arushi.ns.cloudflare.com. dns.cloudflare.com. 2050043919 10000 2400 604800 3600
這筆紀錄,說明該區域的主要名稱伺服器是 arushi.ns.cloudflare.com
,它的管理員信箱是 [email protected]
,版本序號是 2050043919
,保存時間是 3600
秒。
SOA 的重要性跟網路節點的備援機制有關,我們可以想像網路中有兩台名稱伺服器儲存同一份網域跟 IP 的對應關係,以防其中一台過載時,可以由另一台處理。可是當這兩台伺服器內容不同時,要以哪台為主呢?SOA 標明了哪一台是主要名稱伺服器,以及說明次要名稱伺服器應該怎麼跟它同步。每當次要名稱伺服器拿到紀錄時,只要比較版號,就能知道自己的資料是否需要更新。
NS
名稱伺服器(Name Server),用來指示哪個名稱伺服器持有實際的 DNS 紀錄。舉個例子
kenwsc.com. 86400 IN NS arushi.ns.cloudflare.com.
kenwsc.com. 86400 IN NS mustafa.ns.cloudflare.com.
是在告訴查詢者,你如果想知道 kenwsc.com
的 DNS 紀錄,可以到 arushi.ns.cloudflare.com
或 mustafa.ns.cloudflare.com
去問喔。
為什麼 NS 有兩條呢?因為其中一條是主要伺服器,另一條是次要伺服器。在 Cloudflare 的服務中,這兩個名稱伺服器的域名會是一個男孩的名字跟一個女孩的名字(有意思的是,曾經有人寫信給客服,批評這項設計是異性戀本位)。Cloudflare 還雇用藝術家為每個伺服器提供一幅忍者插畫,並放到 Facebook 上,雖然我找不到我的 ns,可能是後期新增的沒有插畫。
A
位置(Address),就是 IP 位置。通常一個網站只會有一個 A 紀錄,但如果你有 Load Balance 的設計,可能會有多個,例如
blog.kenwsc.com. 1 IN A 104.21.66.118
blog.kenwsc.com. 1 IN A 172.67.159.153
說明 blog.kenwsc.com 有兩個 IP 可用,DNS 會輪流用不同順序回覆這兩個答案。
CNAME
標準名稱(Canonical Name),用來將別名網域指向標準網域,同樣看例子
email.kenwsc.com. 1 IN CNAME mailgun.org.
當客戶端來問 email.kenwsc.com
時,伺服器會說這個網域只是別名而已,要知道真的 IP,請去查 mailgun.org
。
有 SEO 經驗的人,應該對 canonical 這個名稱很熟悉,當兩個網頁的內容相同時,我要怎麼告訴搜尋引擎,哪個網頁才是正版的呢?方法是在非正版的網頁上,加上 canonical,指向正版網頁。搜尋引擎看到 canonical,就知道正版是哪個了。
CNAME 可以讓你將不同服務指向同個位置,如果有使用反向代理,就可以在伺服器上依照 HTTP Request 路由到對應的服務。例如你的 n8n 跟 blog 跟主域名在同一台伺服器,只要用 CNAME 指向主域名,這樣主域名的 IP 變動時,n8n 跟 blog 會自動跟著一起變更。
MX
郵件交換器(Mail Exchange),顯示特定網域的郵件應該要送到哪台電腦,例如
kenwsc.com. 1 IN MX 10 mxb.mailgun.org.
當寄件者要寄信給 [email protected]
時,寄信的郵件伺服器會先查詢 kenws.com 對應的郵件伺服器是哪個,知道是 mxb.mailgun.org
後,才會寄信給 mxb.mailgun.org
所在的 IP。
TXT
純文字(Text),可以讓管理員在 DNS 中加入文字,例如
kenwsc.com. 1 IN TXT "v=spf1 include:mailgun.org ~all"
kenwsc.com. 1 IN TXT "google-site-verification=TRTXK-odhHBOXBVHXgEMgC9CXRZy58TkITSofr5uJF8"
有兩條 TXT,一條是 SPF 紀錄,用來防止惡意者仿冒自己的郵件;另一條是 Google 的 DCV 紀錄,用來驗證你的網域所有權。
TXT 一開始目的是當註解用,但現在兩個最重要的用途反而是垃圾郵件防護跟網域所有權驗證。
在第一條 SPF 紀錄中,v=spf1
代表這是 SPF 紀錄,而 include:mailgun.org
的意思是授權 mailgun.org
可以代表我發送電子郵件。如果郵件不是由 mailgun.org
發出的呢?這時要看 ~all
,它表示:如果郵件來源不是 mailgun.org
的 IP,收件者可以接收這封信,但應該將它當成垃圾郵件。
第二條 google-site-verification
則是申請 Google Workspace 時,Google 希望先驗證網域所有權,免得別人假冒你的名字。具體來說,Google 會給你一組 43 個字元的 Token,請你加到 DNS TXT 紀錄中。因為只有網域管理員能修改網域的 TXT,Google 如果能從 DNS 讀到 Token,就知道網域的確是你的。
CAA
憑證頒發機構授權(Certificate Authority Authorization),用來聲明哪些憑證授權機構可為網域頒發憑證。例如
kenwsc.com. IN CAA 0 issue "letsencrypt.org"
說明 Let's Encrypt 可以替 kenwsc.com 頒發憑證。
如果沒有 CAA 記錄,意思是沒有限制,任何人都可為網域頒發憑證。
你不知道的自訂網域
對 DNS 原理有一定理解後,我們來問個實際問題:假設你是 SaaS 的服務供應商,你的使用者想將他的網域掛到你的服務上,你該如何設計自訂網域的功能呢?
為了簡化問題,我們假設使用者的網域是 www.example.com,而你的服務是 service.com。如果希望瀏覽器輸入 www.example.com 得到 service.com 的 IP,直覺方式是請使用者在他的 DNS 加入 CNAME 紀錄,讓網域指向你的服務
www.example.com. 1 IN CNAME service.com.
最簡單的狀況下,這樣是可以了,但如果別人單方面設個 CNAME,就能把你全部的內容當成它自己的,聽起來不是怪怪的嗎?現代網路中,訪問網站通常會用 HTTPS,這時網站會傳送它的憑證給瀏覽器,要是該憑證的網域沒有涵蓋自訂網域,就會出現底下的警告文字:
伺服器無法證明其屬於 www.example.com 網域;其安全性憑證來自 *.service.com 網域。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。
因此 SaaS 服務供應商要提供憑證管理服務,你需要幫使用者簽署、續約憑證,並部署到你的服務中,有時候,這會需要使用者在它的 DNS 管理服務中加入 CAA 紀錄。
假設你都處理好了,可以打個 DNS Query 看看
# dig www.example.com
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> www.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19758
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 300 IN CNAME service.com.
service.com. 300 IN A 34.34.34.34
最底下有顯示 www.example.com 跟 service.com 的資訊,看起來沒問題了對吧?讓我們再多加一條限制:使用者想直接用根網域當自訂網域。
根網域(Root Domain)是你由網域註冊商取得的網域,例如 service.com
。子網域(Subdomain)則是你拿到根網域後,自行切分的,由你控制的網域名稱。當 1980 年代 Paul Mockapetris 設計 DNS 時,子網域的角色類似根網域底下的服務,就像 Alphabet 是控股公司,底下有 Google、Google X、Google Venture、Google Capital 這些公司等。在標準制定當時,網站僅僅只是某個網域下的全球資訊網服務(World Wide Web,即 www)而已,沒人預見日後有人希望直接用品牌名--通常是根網域--當成網站。
於是,關於 CNAME,1987 年的 RFC 1034 中寫到
If a CNAME RR is present at a node, no other data should be
present; this ensures that the data for a canonical name and its aliases
cannot be different.
如果在某節點存在 CNAME RR,則不應該有其他資料;這確保了規範名稱及其別名的資料不會不同。
而在 1996 年的 RFC 1912 寫得更明確
A CNAME record is not allowed to coexist with any other data. In other words, if suzy.podunk.xx is an alias for sue.podunk.xx, you can't also have an MX record for suzy.podunk.edu, or an A record, or even a TXT record. Especially do not try to combine CNAMEs and NS records like this!
CNAME 紀錄不允許與任何其他資料共存。換句話說,如果 suzy.podunk.xx 是 sue.podunk.xx 的別名,你就不能同時為 suzy.podunk.edu 設置 MX 紀錄,或 A 紀錄,甚至是 TXT 紀錄。尤其不要嘗試像這樣將 CNAME 和 NS 紀錄混合使用!
這讓使用者無法替根網域加上 CNAME,因為像前面展示的,根網域通常都有其他紀錄,例如 SOA、NS、MX、CAA。如果替根網域加上 CNAME,像是告訴客戶端:我這裡的紀錄只是別名,你要標準紀錄,請到另個網域查詢。在這情況下,客戶端是要用你這裡的 MX 紀錄,還是用另個網域的 MX 紀錄呢?
Cloudflare 在一篇技術文章中,同樣指出這點:
Technically, the root could be a CNAME but the RFCs state that once a record has a CNAME it can't have any other entries associated with it: that's a problem for a root record like example.com because it will often have an MX record (so email gets delivered), an NS record (to find out which nameserver handles the zone) and an SOA record.
技術上來說,根域名可以是 CNAME,但 RFC 規範指出,一旦一個記錄是 CNAME,它就不能有其他相關的條目:這對像 example.com 這樣的根記錄來說是個問題,因為它通常會有 MX 記錄(用於郵件投遞)、NS 記錄(用來查詢哪個名稱伺服器管理該區域)以及 SOA 記錄。
喔,我們看到了一個很好玩的問題。使用者只是要求你拿掉 www 三個字母,而你卻要苦苦哀求他改個規格。使用者非常驚訝:「辦不到?你為什麼辦不到?」這真是好問題,讓我們先從 DNS 的起源開始講起……
有個折衷辦法,你給使用者 IP,讓他能用 A 紀錄指向你的服務,這樣就不會違反 CNAME 的限制。只是從個人觀點,我不太欣賞這樣做,因為它會在你的伺服器跟使用者間創造直接的依賴關係,哪天你需要遷移伺服器怎麼辦?Elastic IP 可以緩解這問題,但如果可以,我還是希望使用者用別名就好。
當然,上有政策,下有對策,Cloudflare 直接說它打算繞開 RFC
Never one to let a RFC stand in the way of a solution to a real problem, we're happy to announce that CloudFlare allows you to set your zone apex to a CNAME. This allows CloudFlare users to host on EC2, Rackspace's Cloud, Google App Engine, or other cloud hosts and use their naked domain (e.g., yourdomain.com) without forcing a hack solution to a subdomain (e.g., www.yourdomain.com).
我們從不讓 RFC 阻礙解決實際問題的方案,很高興宣布 CloudFlare 允許你將區域頂點設定為 CNAME。這讓 CloudFlare 用戶能夠在 EC2、Rackspace 的 Cloud、Google App Engine 或其他雲端主機上架設網站,並使用裸網域(例如 yourdomain.com),而不必強迫使用子網域的變通方案(例如 www.yourdomain.com)。
它們怎麼做到的?既能讓使用者設置 CNAME,而又不違反 RFC?說起來原理很簡單,它們讓 DNS 伺服器幫你查詢 CNAME。當網域管理員直接在根網域上設置 CNAME 時,Cloudflare 的 DNS 伺服器會直接查詢目標所在的 IP,然後轉成 A 紀錄,回給查詢者。對網域管理員來說,它設置的是 CNAME,可是對查詢者來說,它拿到的是 A 紀錄。Cloudflare 稱這項技術為 CNAME Flattening。
這項技術也不是只有 Cloudflare 在用,例如 DNS Made Easy 有 ANAME 紀錄、gandi.net 有 ALIAS 紀錄,原理都跟 CNAME Flattening 差不多。事實上 IETF 曾經有討論是不是將 ANAME 加入標準,只是後來進度停擺,遲遲不前。
從 2018 年開始,IETF 開始討論另一種新的紀錄,在 2023 年定稿,成為 RFC 9460。在其中,他們納入現代網路需求,新的 SVCB/HTTPS 紀錄可以讓根網域指向其他服務
The SVCB and HTTPS RRs also help when the operator of a service wishes to delegate operational control to one or more other domains, e.g., aliasing the origin "https://example.com" to a service operator endpoint at "svc.example.net". While this case can sometimes be handled by a CNAME, that does not cover all use cases.
SVCB 和 HTTPS 資源記錄(RR)也有助於當服務的營運者希望將操作控制委派給一個或多個其他網域時,例如,將原始網址 "https://example.com" 指向服務營運者端點 "svc.example.net"。雖然這種情況有時可以用 CNAME 處理,但並不涵蓋所有使用案例。
使用方式是
example.com. 7200 IN HTTPS 0 svc.example.net.
因為 SVCB/HTTPS 沒有跟其他紀錄不能並存的限制,我們可以預期,等到各 DNS 服務供應商實作 RFC 9460 後,使用根網域當自訂網域將不再是問題。
其他人是怎麼做的?
對設計方向有概念後,我們來看看市面上的 SaaS 服務供應商們是怎麼做的,主要看兩點:
- 是否支持根網域
- 怎麼設定 DNS
Medium
它們的幫助中心寫得很詳細,還有提供 GoDaddy / Namecheap / Google Domains 的設定方式。我們要的關鍵資訊在這
You can only connect a top-level domain name like yourdomain.com or a subdomain like blog.yourdomain.com to your profile page or publication.
您只能將頂級網域名稱(例如 yourdomain.com)或子網域(例如 blog.yourdomain.com)連接到您的個人檔案頁面或出版物。
Please note that you cannot connect a custom URL that's a subdirectory (folder), for example yourdomain.com/blog.
請注意,您無法連接子目錄(資料夾)形式的自訂網址,例如 yourdomain.com/blog。
看起來 Medium 支援根網域,但它的用詞有點怪,頂級網域是網域層級中最高級的域名,例如 com。依照定義,yourdomain.com 應該只是根網域,而不是頂級網域?
另個有趣的點是,Medium 特別強調不能指定 path,我猜他們的使用者想讓網域點開後直接到特定頁面。可惜這不是自訂網域能處理的事,需要靠自訂路由。
至於 DNS 應該要如何設定呢?方式在這
Now you need to update A records for your custom domain, and you can do that on your domain provider's website.
現在您需要為您的自訂網域更新 A 紀錄,您可以在您的網域提供商網站上進行此操作。
直接更新 A 紀錄,簡單暴力。
GitBook
依照 GitBook 的說明

還有
The final step requires you to copy the name and value to your clipboard so that you can create a CNAME DNS record.
最後一步需要您將名稱和值複製到剪貼簿,以便建立 CNAME DNS 紀錄。
看起來很單純,它們不支援自訂網域為根網域,因此只要設定 CNAME 即可。
Notion
我們想知道的資訊放在 Notion 的 Callout 區塊中,它們可能也知道這是關鍵訊息
When entering your domain, you must include a subdomain. This is typicallywww
, but any other subdomain works too (ex.:careers.my-domain.com
orportfolio.my-domain.com
).
輸入您的網域時,必須包含子網域。通常是www
,但任何其他子網域也可以(例如:careers.my-domain.com
或portfolio.my-domain.com
)。
所以同樣的,Notion 不支援根網域。然而別急,它底下還有
If you want your root domain to point to your Notion Site, we recommend setting up your subdomain towww
and setting up a redirect with your DNS provider.
如果您想讓您的根網域指向您的 Notion 網站,我們建議將您的子網域設定為www
,並在您的 DNS 服務提供商處設定轉址。
這是我沒想到的小技巧,它用重定向的方式,將根網域轉址到 www.example.com 這類子網域上。 假設你的 DNS 服務商是 gandi.net,當你設置網頁轉址後,gandi 會修改原本的 DNS 紀錄,讓 exampe.com 指向它的伺服器(可能是 webredir.vip.gandi.net),然後再重定向到 www.example.com。
這個方法會將 example.com 的網域權重轉移到 www.example.com,這倒沒問題,我們本來就應該讓權重集中,唯一的副作用是,瀏覽器最後顯示的網域會是 www.example.com,但我猜對多數使用者來說,還算是個可接受的實現?
關於 DNS 紀錄,底下還有一段
You’ll be prompted to go to your DNS provider and add a CNAME record for the domain you want to connect. Set the CNAME target toexternal.notion.site.
. Note the period at the end of the CNAME target!
系統會提示您前往您的 DNS 服務提供商,並為您想連接的網域新增 CNAME 紀錄。將 CNAME 目標設定為external.notion.site.
。請注意 CNAME 目標末尾的句點!
這個就很典型了,因為不支援根網域,使用 CNAME 設定就好。
小結
自訂網域是個簡單的需求,可是當自己要設計時,沒想到這麼有趣,會需要不斷逼問它的限制在哪,脈絡是什麼。我還記得 Joel Spolsky 當 PM 時,Bill Gates 問他:「像是那些日期和時間的函數。Excel 有這麼多日期和時間的函數。Basic 會有一樣的函數嗎?它們會不會都用一樣的方式運作?」而 Joel 居然答得上來!是的,Excel 跟 Basic 在 1900 前兩個月的計算不同,這關係到另一個故事(想知道的人可以翻翻 Joel 的 Blog,文章標題是 My First BillG Review)。
回到自訂網域,深入研究後最有意思的是 Cloudflare 的觀點,看完忍不住想「原來還有這招」,但如果我是 SaaS 供應商,要問實際會不會因此而建議使用者用根網域?應該還是不會,因為沒辦法保證使用者的 DNS 有支援 CNAME Flattening,除非那是必要需求,否則請使用者乖乖用子網域還是最標準的方式。
Reference
- DNS Hacking 之 基礎知識:DNS 運作與紀錄類型
- Adding a custom domain
- 什麼是 DNS 記錄?
- And the DNS was Born
- What's the story behind the names of CloudFlare's name servers?
- How can I add a custom domain to a docs site?
- Connect a custom domain with Notion Sites
- Introducing CNAME Flattening: RFC-Compliant CNAMEs at a Domain's Root
- DOMAIN NAMES - CONCEPTS AND FACILITIES
- DNS SVCB/HTTPS 记录介绍