jvinhit//lab

Search posts

Type to search across journal entries.

↑ ↓ navigate ↵ open esc close

Cloudflare cho Frontend Developer · Phần 4 — DNS chi tiết và các kịch bản trỏ tên miền

Bảng tra trỏ tên miền vào Hostinger, VPS, Cloudflare Pages, Vercel, Netlify, GitHub Pages, CloudFront hay Render: loại bản ghi nào, cam hay xám, vì sao — cộng TTL, các bản ghi hay bị quên và lab so sánh cam/xám bằng dig.

Phần 3 làm với Hostinger: một bản ghi A trỏ vào một IP, đám mây cam, xong. Nhưng frontend hiện nay hay deploy lên Vercel, Netlify, Cloudflare Pages, GitHub Pages hoặc một VPS chạy Docker — và backend lại ở một nơi khác nữa. Mỗi nơi có cách trỏ riêng, và quan trọng hơn, có khuyến nghị riêng về đám mây cam hay xám. Trỏ sai loại bản ghi thì không chạy; chọn sai màu đám mây thì chạy nhưng chứng chỉ hỏng, cache hai tầng khó đoán, hoặc preview deployment của Vercel mất tác dụng.

Bài này là bảng tra: nơi chạy web → loại bản ghi → màu đám mây → vì sao. Kèm theo là ba thứ hay bị bỏ qua khi trỏ tên miền: TTL, các bản ghi bị quên khi chuyển nhà (MX, SPF, DKIM, CAA), và cách tự nhìn thấy khác biệt cam/xám bằng dig.

Thuật ngữ giữ tiếng Anh: apex (tên miền gốc), CNAME flattening, proxied, DNS only, TTL, distribution, origin. Nền tảng gọi tên gì thì giữ tên đó.

Bảng tra nhanh — trỏ tên miền vào đâu thì làm gì

Nơi chạy webLoạiContentProxyGhi chú
Hostinger / VPSAIP máy chủ● CamCần SSL ở origin để dùng Full (strict)
Cloudflare PagesCNAMEproject.pages.dev● CamCloudflare tự thêm khi bạn add domain trong Pages
VercelCNAMEcname.vercel-dns.com○ XámVercel khuyến nghị DNS only. Xem mục Vercel/Netlify bên dưới
NetlifyCNAMEsite.netlify.app○ XámNhư Vercel
GitHub PagesA ×4185.199.108.153 và 3 IP kế tiếp● CamHoặc CNAME user.github.io cho subdomain
AWS CloudFrontCNAMEd123abc.cloudfront.net○ XámChồng hai CDN thường không đáng. Xem mục CloudFront bên dưới
Render / Railway / FlyCNAMETên miền nền tảng cấp● CamKiểm tra tài liệu của họ về SSL

Hai quy tắc ngón tay cái rút ra từ bảng:

  1. Nền tảng cấp cho bạn một IP → bản ghi A. Cấp một tên → CNAME. Nhờ CNAME flattening (phần 2), CNAME dùng được cả ở tên miền gốc @, nên bạn không cần tìm IP của nền tảng.
  2. Nền tảng đã có CDN và SSL riêng → đám mây xám. Vercel, Netlify, CloudFront đều là CDN. Đặt Cloudflare proxy lên trên là hai lớp CDN chồng nhau. Hostinger, VPS, GitHub Pages không phải CDN (hoặc CDN rất mỏng) → cam để hưởng cache, WAF, giấu IP.

Giá trị Content trong bảng là mẫu theo tài liệu của từng nền tảng tại thời điểm viết; luôn lấy đúng giá trị mà dashboard của nền tảng đó hiện ra cho bạn, vì họ có thể đổi.


Kịch bản: frontend tĩnh trên Cloudflare Pages

Đây là lựa chọn đơn giản và nhanh nhất nếu bạn chỉ có frontend. Chi tiết về Pages ở phần 8, nhưng phần DNS thì như sau:

  1. Trong dự án Pages, vào Custom domains → Set up a custom domain.
  2. Nhập example.com.
  3. Vì tên miền đã nằm trong cùng tài khoản Cloudflare, bản ghi CNAME được tạo tự động, proxy sẵn, chứng chỉ cấp sẵn. Không phải làm gì thêm.
Git repository source + lockfile Pages build install → build Deployment immutable assets Cloudflare edge custom domain Production branch example.com Preview branch / PR unique preview URL Build success creates a new deployment; DNS does not move for every commit.
Tên miền và Pages cùng tài khoản: Cloudflare tự tạo CNAME @ → project.pages.dev, bật cam và cấp chứng chỉ — không có bước thủ công

Sau khi xong, bảng DNS có thêm dòng CNAME @ my-app.pages.dev ● Proxied. Muốn www cũng chạy, thêm www.example.com làm custom domain thứ hai (Cloudflare lại tự tạo CNAME), rồi dùng Redirect Rule ở phần 7 gom về một địa chỉ. Nếu tên miền nằm ở tài khoản Cloudflare khác, hoặc DNS chưa ở Cloudflare, Pages sẽ yêu cầu bạn tự tạo CNAME và có thể thêm một TXT xác minh — làm theo đúng giá trị nó hiện.


Kịch bản: frontend tĩnh + API riêng

Rất phổ biến: giao diện ở một nơi, API ở một nơi khác.

Tên miềnLoạiContentProxyTrỏ tới
example.comCNAMEmy-app.pages.dev●Frontend
wwwCNAMEexample.com●Chuyển hướng về gốc (xem phần 7)
apiA203.0.113.99●VPS chạy backend
stagingCNAMEstaging.my-app.pages.dev●Môi trường thử nghiệm

Với dòng api: VPS phải nghe cổng 443 với chứng chỉ hợp lệ (nginx đứng trước app Node, xem VPS + nginx reverse proxy), vì đám mây cam chỉ đi qua cổng web và bạn đang ở Full (strict). App Node nghe 3000 mà không có nginx thì phải để xám — và khi đó mất giấu IP, mất WAF cho chính endpoint nhạy cảm nhất.

Frontend và API khác subdomain là cross-origin. example.com gọi api.example.com vẫn bị tính là khác origin → cần CORS. Nếu muốn tránh CORS hoàn toàn, bạn có thể dùng Cloudflare Worker để định tuyến example.com/api/* xuống backend — tương đương behavior /api/* của CloudFront. Xem phần 9.

Nếu bạn quen Vite, đây chính là chuyện server.proxy ở dev: ở localhost:5173, /api được Vite chuyển tiếp sang localhost:3000 nên trình duyệt thấy same-origin, không CORS, không preflight. Lên production không còn Vite, fetch('https://api.example.com/orders') từ https://example.com là cross-origin thật — backend phải trả Access-Control-Allow-Origin, và cookie cần SameSite=None; Secure nếu muốn gửi kèm. Bài CORS giải thích tận gốc đi hết các trường hợp; Worker ở phần 9 là cách đưa “server.proxy” lên production.


Kịch bản: Vercel hoặc Netlify — cam hay xám?

Đây là câu hỏi gây tranh cãi nhất. Cả Vercel lẫn Netlify đều đã có CDN và SSL riêng. Đặt Cloudflare proxy lên trên nghĩa là chồng hai lớp CDN.

Tiêu chí○ DNS only (khuyến nghị)● Proxied
Chứng chỉ SSLVercel/Netlify tự cấp và gia hạn, không vấn đềCó thể xung đột, đôi khi chặn được việc cấp chứng chỉ tự động của họ
CacheChỉ một lớp, dễ đoánHai lớp, purge một bên vẫn còn bên kia
Tính năng nền tảngPreview deploy, ISR, Edge Function hoạt động đúng thiết kếCó thể bị ảnh hưởng
WAF / chặn bot của CloudflareKhông dùng đượcDùng được
Giấu IPKhông cần — không có IP riêng để giấuKhông thêm lợi ích

Khuyến nghị thực tế: với Vercel/Netlify, để đám mây xám, dùng Cloudflare thuần tuý làm DNS. Bạn vẫn được lợi ích quản lý DNS tập trung và CNAME flattening ở tên miền gốc. Chỉ bật đám mây cam khi bạn thực sự cần một tính năng của Cloudflare mà nền tảng kia không có (ví dụ WAF hoặc rate limiting). Khi đó phải đặt SSL mode là Full (strict) và đọc kỹ tài liệu tương thích của nền tảng đó.

Vì sao chứng chỉ lại “xung đột”? Vercel và Netlify xin chứng chỉ cho example.com bằng cách chứng minh họ kiểm soát tên miền — thường qua một request HTTP tới /.well-known/acme-challenge/... hoặc qua việc DNS trỏ đúng về họ. Khi Cloudflare proxy đứng giữa, DNS trả IP Cloudflare chứ không phải IP của họ, và bước kiểm tra “tên miền có trỏ về chúng tôi không” có thể thất bại; dashboard của họ sẽ báo “Invalid Configuration” hoặc treo ở “Pending”. Cache hai tầng thì tệ theo cách khác: bạn deploy, Vercel purge cache của Vercel, nhưng PoP Cloudflare vẫn giữ bản cũ của file — và bạn phải purge thêm ở Cloudflare, mỗi lần deploy.

Nếu bạn vẫn quyết định bật cam (cần WAF, cần chặn theo quốc gia, cần rate limit trang đăng nhập): làm khi chứng chỉ bên kia đã cấp xong ở trạng thái xám, đặt Full (strict), tắt Rocket Loader và các “tối ưu tự động” (phần 10), rồi kiểm tra kỹ preview deployment và ISR còn đúng không. Và ghi lại quyết định đó ở đâu đó trong repo — người sau sẽ không hiểu vì sao Vercel báo cảnh báo cấu hình.


Kịch bản: đã có CloudFront (AWS) rồi

Nếu bạn đã dựng hạ tầng trên AWS theo series AWS cho Frontend Developer, câu hỏi là có nên thêm Cloudflare không.

Chồng CloudFront dưới Cloudflare thường không đáng. Hai lớp CDN nghĩa là hai lớp cache, hai chỗ phải purge (CloudFront invalidation tính phí sau một ngưỡng, Cloudflare purge miễn phí — nhưng bạn phải làm cả hai), độ trễ cộng dồn khi miss, và khó debug gấp đôi: cf-cache-status: HIT ở Cloudflare không nói gì về việc CloudFront có HIT hay không.

  • Nếu muốn dùng Cloudflare để quản lý DNS thôi: để đám mây xám, trỏ CNAME tới d123abc.cloudfront.net. Đây là cách dùng hợp lý và phổ biến. Chứng chỉ của CloudFront (ACM) xác thực qua một bản ghi CNAME mà ACM đưa cho bạn — thêm nó vào Cloudflare ở trạng thái xám như mọi bản ghi xác minh khác.
  • Nếu muốn chuyển hẳn sang Cloudflare: bỏ CloudFront, dùng Cloudflare proxy trỏ thẳng tới origin (ALB, hoặc S3 website endpoint), hoặc chuyển frontend sang Cloudflare Pages. Phần 12 có bảng đối chiếu từng khái niệm AWS ↔ Cloudflare để bạn quyết định.

Kịch bản: GitHub Pages và các nền tảng cấp CNAME khác

GitHub Pages là trường hợp bản ghi A duy nhất ngoài hosting truyền thống: GitHub công bố bốn IP cố định 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 cho tên miền gốc — tạo bốn bản ghi A cùng Name @. Với subdomain (docs.example.com) dùng CNAME trỏ tới user.github.io. Ngoài DNS còn hai việc ở phía GitHub: khai custom domain trong Settings → Pages (nó tạo file CNAME trong repo), và bật Enforce HTTPS sau khi GitHub cấp xong chứng chỉ. Đám mây cam dùng được với Full (strict); nếu GitHub loay hoay không cấp được chứng chỉ khi đang cam, tạm chuyển xám tới khi nó báo HTTPS sẵn sàng rồi bật cam lại. Bài GitHub Pages + custom domain + Cloudflare làm từng bước.

Render, Railway, Fly và các nền tảng tương tự cấp cho bạn một tên miền dạng my-app.onrender.com để CNAME tới, và tự cấp chứng chỉ. Cam hay xám tuỳ tài liệu của họ: nếu họ cấp chứng chỉ qua HTTP challenge thì thường vẫn ổn khi cam; nếu họ yêu cầu TXT xác minh thì TXT luôn là DNS thuần nên không ảnh hưởng. Nguyên tắc: xám trước cho tới khi chứng chỉ của họ Active, rồi mới cân nhắc cam với Full (strict).


TTL — và vì sao ở Cloudflare bạn ít phải lo

TTL (time to live) là thời gian resolver được phép nhớ một bản ghi trước khi hỏi lại — Cache-Control: max-age của thế giới DNS. TTL 3600 nghĩa là sau khi bạn đổi IP, người dùng nào đã phân giải trong giờ vừa rồi vẫn tới IP cũ tới khi hết giờ.

Trạng thái bản ghiTTL hoạt động thế nào
● ProxiedÔ TTL bị khoá ở Auto. Vì DNS luôn trả về IP Cloudflare (không đổi), TTL không còn quan trọng. Đổi IP origin có hiệu lực gần như tức thì — đây là một lợi ích lớn.
○ DNS onlyBạn tự đặt TTL. Mặc định Auto (khoảng 300 giây). Trước khi chuyển hosting, hạ xuống 60 giây và chờ hết TTL cũ rồi mới đổi Content.

Lý do proxied không cần TTL: thứ resolver nhớ là IP Cloudflare, còn IP origin chỉ có Cloudflare biết và tra trực tiếp trong bảng của nó mỗi request. Bạn sửa bản ghi A từ 203.0.113.45 sang IP VPS mới, Cloudflare gõ cửa VPS mới ngay ở request tiếp theo — không ai ngoài kia phải “hết TTL”. Đây là lợi ích “đổi hosting không cần chờ DNS” ở phần 1.

Với bản ghi xám, quy trình chuyển nhà an toàn: hạ TTL xuống 60 → chờ ít nhất bằng TTL cũ (để mọi resolver đã nhớ giá trị cũ kịp hết hạn) → đổi Content → sau khi ổn định, trả TTL về Auto. Bài Zero-downtime migration và debugging đi sâu kịch bản này.


Các bản ghi hay bị quên

Khi chuyển DNS sang Cloudflare, hoặc khi chuyển frontend từ Hostinger sang Pages, người ta nhớ bản ghi A và www — và quên phần còn lại:

Bản ghiNếu quên thì
MXMất toàn bộ email đến
TXT SPFEmail bạn gửi đi bị Gmail đánh dấu spam
TXT DKIMNhư trên
TXT xác minhGoogle Search Console, Facebook Business mất xác minh
CNAME autodiscover, autoconfigOutlook / ứng dụng mail không tự cấu hình được
CNAME ftpKhông kết nối FTP được (nhớ để đám mây xám)
CAANếu có bản ghi CAA cũ chỉ cho phép một CA nhất định, Cloudflare có thể không cấp được chứng chỉ

CAA đáng nói thêm vì nó âm thầm: bản ghi CAA khai “chỉ CA này mới được cấp chứng chỉ cho tên miền tôi”. Nếu tên miền của bạn từng có 0 issue "letsencrypt.org" (nhiều hosting tự thêm), Universal SSL của Cloudflare — vốn dùng nhiều CA khác nhau — có thể bị từ chối, và bạn thấy Universal SSL kẹt ở trạng thái chờ mãi. Kiểm tra:

dig example.com CAA +short
# Không có gì  → ổn, CA nào cũng cấp được
# 0 issue "letsencrypt.org"  → chỉ Let's Encrypt; kiểm tra docs Cloudflare về CAA

Không có CAA là trạng thái mặc định và đủ an toàn cho hầu hết dự án. Nếu bạn có, đọc mục CAA trong docs SSL của Cloudflare để thêm đúng các CA mà Universal SSL đang dùng, thay vì xoá bừa.

Với SPF và DKIM, chúng cũng ảnh hưởng tới email gửi đi từ backend của bạn (email xác nhận đăng ký, OTP): thiếu SPF thì Gmail đẩy vào spam, và bạn chỉ biết khi người dùng phàn nàn “không nhận được mã”. Bài Email DNS: SPF, DKIM, DMARC là nơi tra khi bạn tới bước đó.


Lab 4.1 — Thêm subdomain (20 phút)

  1. Tạo bản ghi CNAME cho test trỏ tới một dịch vụ bất kỳ bạn có (hoặc chính example.com).
  2. Bấm thử đám mây cam, chạy dig test.example.com +short — ghi lại kết quả.
  3. Chuyển sang đám mây xám, chạy lại lệnh đó — so sánh. Bạn sẽ thấy rõ sự khác nhau giữa IP Cloudflare và địa chỉ thật.
  4. Với đám mây xám, chạy curl -sI https://test.example.com và xem header server — không còn là cloudflare.
# Cam
dig test.example.com +short
# 104.21.x.x
# 172.67.x.x

# Xám (CNAME tới example.com, mà example.com đang cam → vẫn ra IP Cloudflare!)
dig test.example.com +short
# example.com.
# 104.21.x.x

# Xám, CNAME tới một dịch vụ ngoài (vd my-app.vercel.app)
dig test.example.com +short
# my-app.vercel.app.
# 76.76.21.x   ← IP của nền tảng đó, không phải Cloudflare

Điểm tinh tế ở dòng thứ hai: nếu bạn CNAME test (xám) tới example.com (cam), kết quả cuối vẫn là IP Cloudflare — vì resolver đi theo CNAME tới example.com, và example.com đang được proxy. Đám mây xám của test chỉ có nghĩa “Cloudflare không giấu đích của CNAME này”, còn đích đó cam hay xám là chuyện của đích. Muốn thấy khác biệt rõ nhất, trỏ test tới một dịch vụ ngoài Cloudflare.

Kết quả mong đợi: với cam, dig ra 104.x/172.67.x và curl có server: cloudflare, cf-ray. Với xám trỏ ra ngoài, dig ra IP của dịch vụ đích, curl ra server của họ (Vercel, nginx, GitHub.com…) và không có cf-ray. Bạn giải thích được vì sao CNAME xám tới một tên đang cam vẫn cho IP Cloudflare.

Khi xong lab, xoá bản ghi test nếu không dùng — bản ghi thừa trỏ tới dịch vụ bạn đã huỷ là một lỗ hổng “subdomain takeover” kinh điển: ai đó đăng ký lại đúng tên my-app.vercel.app là chiếm được test.example.com.


Sai lầm hay gặp ở chương này

  • Bật cam cho Vercel/Netlify “cho có WAF” rồi chứng chỉ của họ không cấp được. Nhận biết: dashboard bên kia báo Invalid Configuration hoặc Pending mãi. Sửa: xám, chờ chứng chỉ Active, rồi mới cân nhắc lại.
  • Trỏ A vào VPS nghe cổng 3000, bật cam. Nhận biết: https://api.example.com lỗi, http://IP:3000 chạy. Sửa: nginx nghe 443, hoặc xám.
  • Chỉ tạo @ mà quên www (hoặc ngược lại). Nhận biết: một trong hai địa chỉ “không tìm thấy máy chủ”. Sửa: thêm CNAME www → example.com cùng màu với @.
  • Đổi IP origin của bản ghi xám rồi kết luận ngay. Nhận biết: một số người vẫn vào IP cũ. Sửa: đó là TTL — chờ, hoặc hạ TTL trước lần sau. Bản ghi cam không có vấn đề này.
  • Xoá sạch bản ghi cũ vì “không biết nó làm gì”. Nhận biết: vài ngày sau Search Console mất xác minh, Outlook không tự cấu hình. Sửa: đối chiếu với ảnh chụp ở phần 3 và khôi phục; thứ không biết thì để yên ở trạng thái xám.
  • Giữ CNAME trỏ tới dịch vụ đã huỷ. Không có triệu chứng cho tới khi bị chiếm subdomain. Sửa: dọn bản ghi khi dọn dịch vụ.

Tóm tắt

  • Nền tảng cấp IP → A, cấp tên → CNAME; CNAME dùng được ở @ nhờ CNAME flattening.
  • Nền tảng đã có CDN + SSL riêng (Vercel, Netlify, CloudFront) → xám, dùng Cloudflare làm DNS thuần. Hostinger, VPS, GitHub Pages, Pages → cam.
  • Cloudflare Pages cùng tài khoản: add custom domain là xong, CNAME + chứng chỉ tự tạo.
  • Frontend và api.example.com là cross-origin → CORS; Worker định tuyến /api/* là cách đưa “Vite server.proxy” lên production.
  • Chồng CloudFront dưới Cloudflare: hai lớp cache, hai chỗ purge, khó debug gấp đôi — chỉ nên xám làm DNS, hoặc chuyển hẳn.
  • Bản ghi cam không cần lo TTL; bản ghi xám thì hạ TTL xuống 60 và chờ hết TTL cũ trước khi chuyển nhà.
  • Đừng quên MX, SPF, DKIM, TXT xác minh, autodiscover, ftp, CAA. Dọn CNAME trỏ tới dịch vụ đã huỷ.

Phần tiếp theo: Phần 5 — SSL/TLS: bốn chế độ và lỗi ERR_TOO_MANY_REDIRECTS — chương gây lỗi nhiều nhất, giải thích tận gốc vòng lặp chuyển hướng.

Đọc thêm