Build a Personal Blog — Phase 7: Setup Custom Domain

Đây là Phase 7 — phase cuối cùng của series Build a Personal Blog. Ở Phase 6, bạn đã deploy blog Docker hóa lên Ubuntu VPS trên Hostinger. Blog đã live, đã có SSL, và hoạt động ngon lành. Nhưng visitors vẫn truy cập qua IP address hoặc domain tạm. Giờ là lúc fix chuyện đó.
Series: Build a Personal Blog — Complete Roadmap
Trước: Phase 6 — Deploy lên Ubuntu VPS
Source Code: GitHub — personal-blog-phase-7
Bạn Sẽ Build Được Gì
Sau phase này:
✅ Custom domain .blog đã đăng ký trên Hostinger
✅ DNS records trỏ domain về VPS
✅ DNS propagation đã verify bằng nhiều tool
✅ SSL certificates cover cả yourdomain.com và www.yourdomain.com
✅ www → apex redirect trong Nginx cho canonical URL gọn gàng
✅ Google Search Console đã submit với sitemap
✅ Social sharing previews đã verify và hoạt động
Time commitment: 1–2 giờ (cộng thời gian chờ DNS propagation)
Prerequisites: Phase 6 — Deploy lên Ubuntu VPS
Tổng Quan Kiến Trúc
Đây là bức tranh toàn cảnh — từ user gõ domain trên browser đến blog render trang:
Domain registrar (Hostinger) báo cho hệ thống DNS biết server bạn ở đâu. DNS resolve tên miền thành VPS IP. Nginx xử lý SSL và redirect traffic www về apex domain.
1. Đăng Ký Domain Trên Hostinger
1.1 Chọn Domain
Vào Hostinger Domains và tìm domain bạn muốn. Một vài tips:
.blog— sinh ra để làm blog, giá khá mềm (~$10-15/năm).dev— ngon cho developer portfolio (~$12-15/năm).com— classic choice nếu còn available (~$10/năm)- Chọn tên ngắn, dễ nhớ, dễ đánh vần
Trong guide này, mình dùng chanh.blog làm ví dụ. Thay bằng domain thật của bạn nhé.
1.2 Hoàn Tất Mua Hàng
- Add domain vào cart
- Chọn thời hạn đăng ký (1 năm là đủ để bắt đầu)
- Privacy protection: Hostinger cho WHOIS privacy miễn phí — cứ bật
- Thanh toán
Sau khi mua, domain xuất hiện trong Hostinger dashboard ở mục Domains.
1.3 Bảng Quản Lý DNS Hostinger
Vào Domains → chanh.blog → DNS / Nameservers trong Hostinger panel. Đây là nơi bạn sẽ thêm DNS records.
Mặc định, Hostinger set nameservers riêng:
ns1.dns-parking.com
ns2.dns-parking.comCác nameservers này sẽ đổi thành nameservers chính thức của Hostinger khi bạn bắt đầu cấu hình DNS records. Nếu bạn dùng Hostinger cho cả VPS hosting lẫn domain registration thì nameservers mặc định là ổn.
2. Cấu Hình DNS Records
DNS records báo cho internet biết server bạn ở đâu khi ai đó gõ tên miền. Bạn cần hai loại records.
2.1 A Record Là Gì?
A record (Address record) map tên miền sang IPv4 address. Khi ai đó truy cập chanh.blog, hệ thống DNS tra cứu A record và trả về VPS IP của bạn.
2.2 Thêm DNS Records
Trong bảng quản lý DNS Hostinger, thêm các records sau:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | @ | YOUR_VPS_IP | 3600 |
| A | www | YOUR_VPS_IP | 3600 |
Giải thích:
@đại diện cho apex domain (chanh.blog) — record này làm chochanh.blogtrỏ về server bạnwwwxử lý subdomainwww.chanh.blog— mình sẽ redirect cái này về apex domain sau- TTL 3600 nghĩa là DNS resolvers cache record này trong 1 giờ (3600 giây). TTL thấp hơn = propagation nhanh hơn khi bạn đổi records, nhưng nhiều DNS lookups hơn
Tip: Nếu VPS có IPv6 address, thêm AAAA records cùng tên trỏ về IPv6 address. Như vậy users trên mạng hiện đại có thể kết nối qua IPv6.
2.3 Tùy Chọn: MX Records Cho Email
Nếu bạn muốn nhận email tại [email protected], bạn cần MX records. Ngoài phạm vi bài này, nhưng setup tùy theo email provider:
- Google Workspace: Thêm MX records của Google
- Zoho Mail (gói miễn phí): Thêm MX records của Zoho
- Fastmail, ProtonMail, v.v.: Làm theo hướng dẫn DNS setup của họ
Giờ thì skip — bạn luôn có thể thêm email sau.
2.4 Xóa Records Xung Đột
Hostinger có thể có records mặc định (như parking pages hoặc CNAME records) xung đột với A records mới. Trong DNS panel:
- Tìm các A hoặc CNAME records hiện tại cho
@hoặcwww - Xóa nếu chúng trỏ đến đâu đó khác ngoài VPS IP của bạn
- Giữ lại NS (nameserver) records — chúng bắt buộc phải có
3. DNS Propagation
Sau khi thêm DNS records, bạn cần chờ thay đổi lan truyền qua hệ thống DNS toàn cầu. Có thể mất từ 5 phút đến 48 giờ, nhưng thường là 15-30 phút với Hostinger.
3.1 Kiểm Tra Với dig
dig là tool tra cứu DNS tiêu chuẩn. Chạy trên máy local:
# Kiểm tra A record cho apex domain
dig chanh.blog +short
# Kỳ vọng: YOUR_VPS_IP
# Kiểm tra A record cho www
dig www.chanh.blog +short
# Kỳ vọng: YOUR_VPS_IP
# Full DNS trace (hiển thị đường đi resolution)
dig chanh.blog +trace3.2 Kiểm Tra Với nslookup
nslookup chanh.blog
# Output kỳ vọng:
# Name: chanh.blog
# Address: YOUR_VPS_IP3.3 Online Propagation Checkers
Những tool này kiểm tra DNS từ nhiều vị trí toàn cầu:
- whatsmydns.net — bản đồ propagation trực quan
- dnschecker.org — kiểm tra từ 20+ vị trí
- mxtoolbox.com — phân tích DNS chi tiết
Nhập domain và kiểm tra A record. Bạn sẽ thấy VPS IP xuất hiện ở tất cả vị trí. Nếu một số nơi vẫn hiện giá trị cũ, chờ thêm chút nữa.
3.4 DNS Không Propagate Thì Sao?
Các vấn đề thường gặp:
| Vấn Đề | Giải Pháp |
|---|---|
| IP sai trong A record | Kiểm tra lại VPS IP trong Hostinger dashboard |
| CNAME xung đột | Xóa CNAME record cho @ — CNAME và A không thể cùng tồn tại trên apex |
| Vẫn hiện IP cũ | Hạ TTL xuống 300, chờ, rồi đổi record |
| Không có response | Xác nhận nameservers đã set đúng trong Hostinger |
4. Cập Nhật Nginx Cho Domain
Ở Phase 6, Nginx config của bạn dùng VPS IP hoặc domain placeholder. Giờ update bằng domain thật.
4.1 Sửa Cấu Hình Nginx
SSH vào VPS và sửa blog config:
ssh deploy@YOUR_VPS_IP
sudo nano /etc/nginx/sites-available/blogThay server block hiện tại bằng config này:
# Redirect www sang apex domain
server {
listen 80;
listen [::]:80;
server_name www.chanh.blog;
return 301 https://chanh.blog$request_uri;
}
# Server block chính
server {
listen 80;
listen [::]:80;
server_name chanh.blog;
# Proxy mọi requests đến Next.js
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Cache static assets
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
proxy_cache_valid 200 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Cache images
location /images/ {
proxy_pass http://127.0.0.1:3000;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, max-age=2592000";
}
}Thay chanh.blog bằng domain thật của bạn.
4.2 Test Và Reload
# Test lỗi syntax
sudo nginx -t
# Reload cấu hình
sudo systemctl reload nginx4.3 Tại Sao Redirect www Về Apex?
Cả www.chanh.blog và chanh.blog cùng serve content giống nhau sẽ tạo vấn đề duplicate content cho search engines. Chọn một cái làm canonical URL và redirect cái còn lại. Apex domain (không có www) là chuẩn hiện đại:
301 redirect báo cho search engines "trang này đã chuyển vĩnh viễn" — chúng sẽ chỉ index apex domain.
5. SSL Certificate Cho Domain
Ở Phase 6, bạn có thể đã setup SSL với Let's Encrypt bằng VPS IP hoặc domain tạm. Giờ bạn cần certificate cover domain thật.
5.1 Cấp Certificate Mới
sudo certbot --nginx -d chanh.blog -d www.chanh.blogCertbot sẽ:
- Verify bạn sở hữu cả
chanh.blogvàwww.chanh.blogqua HTTP challenge - Cấp certificate cover cả hai domains
- Tự động update Nginx config với SSL settings
- Setup redirect HTTP → HTTPS
Quan trọng: DNS phải propagate hoàn toàn trước khi chạy Certbot. Nếu Certbot không thể truy cập server qua domain name, HTTP challenge sẽ fail. Dùng
dig chanh.blog +shortđể verify VPS IP đã được trả về.
5.2 Verify Nginx Config Đã Cập Nhật
Sau khi Certbot chạy xong, kiểm tra những gì nó thêm vào:
cat /etc/nginx/sites-available/blogBạn sẽ thấy:
listen 443 ssldirectives- Đường dẫn certificate (
/etc/letsencrypt/live/chanh.blog/) - Redirect tự động HTTP → HTTPS trên port 80
www→ apex redirect giờ cũng xử lý HTTPS
Certbot modify config một cách thông minh. www server block sẽ redirect đến https://chanh.blog, và block chính sẽ serve qua HTTPS.
5.3 Test SSL
Mở browser và verify:
# Phải load blog qua HTTPS
curl -I https://chanh.blog
# Phải redirect sang https://chanh.blog
curl -I http://chanh.blog
# Phải redirect sang https://chanh.blog
curl -I https://www.chanh.blog
# Phải redirect sang https://chanh.blog
curl -I http://www.chanh.blogCả bốn URLs cuối cùng phải đến https://chanh.blog với nội dung blog của bạn.
5.4 SSL Labs Test
Để kiểm tra SSL kỹ lưỡng, vào SSL Labs và nhập chanh.blog. Bạn nên đạt A hoặc A+. Những vấn đề thường gặp làm hạ điểm:
| Vấn Đề | Cách Fix |
|---|---|
| HSTS chưa bật | Thêm add_header Strict-Transport-Security vào Nginx |
| Cipher suites yếu | Certbot thường xử lý rồi, nhưng kiểm tra Nginx SSL config |
| Certificate chain không đầy đủ | Chạy lại certbot --nginx để fix |
5.5 Xác Nhận Auto-Renewal
Verify renewal timer đang active:
sudo systemctl status certbot.timer
# Phải hiện "active (waiting)"
# Test renewal
sudo certbot renew --dry-runCertificates tự động renew mỗi 60-90 ngày. Không cần thao tác thủ công.
6. Cập Nhật Cấu Hình Ứng Dụng
Code blog của bạn có thể đang reference URL cũ. Update để dùng domain mới.
6.1 Update Environment Variables
Trên VPS, sửa file production environment:
cd ~/my-blog
nano .env.productionUpdate site URL:
NEXT_PUBLIC_SITE_URL=https://chanh.blog6.2 Rebuild Ứng Dụng
Site URL được bake vào build lúc compile (vì là biến NEXT_PUBLIC_). Phải rebuild:
./deploy.shHoặc chạy thủ công:
docker compose -f docker-compose.yml -f docker-compose.prod.yml \
--env-file .env.production \
up -d --build6.3 Verify Meta Tags
Sau khi rebuild, kiểm tra Open Graph meta tags dùng đúng domain:
curl -s https://chanh.blog | grep -E 'og:url|og:image|canonical'Bạn phải thấy domain mới trong tất cả meta tag URLs — không phải IP cũ hay domain tạm.
7. Test End-to-End
Domain đã cấu hình, SSL đã active, và app đã rebuild. Giờ verify mọi thứ hoạt động.
7.1 Checklist Chức Năng Chính
Test từng mục trong browser:
| Test | URL | Kỳ Vọng |
|---|---|---|
| Trang chủ | https://chanh.blog | Blog home load qua HTTPS |
| Danh sách bài viết | https://chanh.blog/blog | Tất cả posts hiển thị |
| Bài viết đơn | https://chanh.blog/blog/your-post | Post render với syntax highlighting |
| Trang tag | https://chanh.blog/blog?tag=docker | Posts đã filter hiển thị |
| Tìm kiếm | https://chanh.blog/blog?q=nginx | Kết quả tìm kiếm xuất hiện |
| RSS feed | https://chanh.blog/feed.xml | XML feed hợp lệ |
| Sitemap | https://chanh.blog/sitemap.xml | Tất cả pages được liệt kê |
| www redirect | https://www.chanh.blog | Redirect sang https://chanh.blog |
| HTTP redirect | http://chanh.blog | Redirect sang https://chanh.blog |
7.2 Test Mobile
Mở blog trên điện thoại hoặc dùng Chrome DevTools device simulator (F12 → Toggle Device Toolbar). Verify:
- Responsive layout hoạt động
- Navigation menu mở/đóng bình thường
- Posts đọc được trên màn hình nhỏ
- Images không tràn viewport
7.3 Kiểm Tra Performance
Chạy performance audit nhanh với PageSpeed Insights. Nhập https://chanh.blog và kiểm tra:
- Performance: 90+ là tốt, 95+ là tuyệt vời
- Accessibility: Nên đạt 90+
- SEO: Nên đạt 90+
- Best Practices: Nên đạt 90+
8. Checklist Post-Launch
Blog đã live trên custom domain. Đây là những gì cần làm tiếp để tối đa visibility.
8.1 Google Search Console
Google Search Console cho phép bạn monitor cách Google crawl và index site.
- Vào Google Search Console
- Click Add Property
- Chọn URL prefix và nhập
https://chanh.blog - Verify ownership — cách đơn giản nhất:
- Download file HTML verification mà Google cung cấp
- Đặt vào thư mục
public/ - Commit và deploy
- Click "Verify" trong Search Console
- Sau khi verify:
- Submit sitemap: vào Sitemaps → nhập
https://chanh.blog/sitemap.xml→ click Submit - Request indexing cho homepage: nhập URL vào thanh tìm kiếm phía trên → click Request Indexing
- Submit sitemap: vào Sitemaps → nhập
8.2 Social Sharing Previews
Khi bạn share link trên Twitter/X, LinkedIn, hay Facebook, chúng hiện preview card với title, description, và OG image của post. Verify chúng hoạt động:
- Twitter/X: Card Validator
- LinkedIn: Post Inspector
- Facebook: Sharing Debugger
Nhập URL blog hoặc URL post cụ thể. Bạn sẽ thấy:
- Tiêu đề post
- Mô tả
- OG image (image tùy chỉnh bạn đã generate)
Nếu preview hiện sai, kiểm tra <meta> tags:
curl -s https://chanh.blog/blog/your-post | grep -E 'og:|twitter:'8.3 Analytics (Tùy Chọn)
Cân nhắc thêm analytics để track visitors. Các lựa chọn thân thiện privacy:
- Plausible Analytics — nhẹ, privacy-first, không cookies (~$9/tháng)
- Umami — self-hosted, open source, miễn phí
- Google Analytics — miễn phí nhưng dùng cookies, cần consent banner ở EU
Cho approach self-hosted, Umami kết hợp tốt với Docker setup hiện tại — bạn có thể thêm nó như service khác trong docker-compose.yml.
8.4 robots.txt
Nếu chưa có, thêm robots.txt để hướng dẫn search engine crawlers:
Tạo public/robots.txt:
User-agent: *
Allow: /
Sitemap: https://chanh.blog/sitemap.xmlCái này báo cho tất cả crawlers rằng chúng có thể index mọi thứ, và chỉ chúng đến sitemap.
9. Tùy Chọn: Cloudflare DNS Proxy
Nếu bạn muốn thêm một lớp bảo vệ, có thể đặt Cloudflare phía trước domain. Gói miễn phí của Cloudflare cho bạn:
- DDoS protection — hấp thụ các cuộc tấn công volumetric
- CDN caching — serve static assets từ edge servers toàn cầu
- DNS analytics — xem query volume và response times
- Always Online — hiện cached version nếu server bạn down
9.1 Setup Cloudflare
- Tạo tài khoản miễn phí tại cloudflare.com
- Thêm domain (
chanh.blog) - Cloudflare scan DNS records hiện tại
- Review và confirm records đã import khớp với cái bạn set trong Hostinger
- Cloudflare cung cấp hai nameservers (ví dụ:
ada.ns.cloudflare.com,bob.ns.cloudflare.com) - Trong Hostinger → Domains → DNS / Nameservers, đổi nameservers sang của Cloudflare
- Chờ nameserver propagation (có thể tới 24 giờ)
9.2 Cloudflare SSL Mode
Quan trọng: Set SSL mode của Cloudflare thành Full (Strict). Điều này đảm bảo:
- Cloudflare ↔ Server bạn: encrypted bằng Let's Encrypt certificate
- Browser ↔ Cloudflare: encrypted bằng certificate của Cloudflare
Không dùng "Flexible" SSL — nghĩa là Cloudflare kết nối server bạn qua HTTP thuần, phá bỏ mục đích có SSL trên VPS.
9.3 Khi Nào Nên Skip Cloudflare
Cloudflare thêm complexity. Cho blog cá nhân với traffic vừa phải, nó là optional. Bạn có thể skip nếu:
- Muốn quản lý DNS đơn giản hơn (bớt một service phải cấu hình)
- Không lo về DDoS attacks
- Thích giữ infrastructure tối giản
VPS với Nginx + Let's Encrypt đã là setup khá solid rồi. Cloudflare là nice-to-have, không phải must-have.
Xử Lý Sự Cố
Domain không resolve
# Kiểm tra DNS records
dig chanh.blog +short
# Phải trả về VPS IP
# Nếu trống, DNS chưa propagate
# Kiểm tra trạng thái propagation tại whatsmydns.netNguyên nhân phổ biến:
- DNS records chưa save đúng trong Hostinger
- Nameserver change vẫn đang propagate (tới 48 giờ)
- Gõ sai tên domain hoặc IP address
Certbot báo "Could not reach domain"
# Verify DNS trỏ về VPS
dig chanh.blog +short
# Verify port 80 mở
sudo ufw status
# Verify Nginx đang chạy và listening
sudo nginx -t
sudo systemctl status nginx
# Thử Certbot với verbose output
sudo certbot --nginx -d chanh.blog -d www.chanh.blog -vCertbot cần truy cập http://chanh.blog/.well-known/acme-challenge/ để verify ownership. Nếu DNS chưa propagate hoặc port 80 bị block, nó sẽ fail.
www không redirect
Kiểm tra Nginx config có www server block với 301 redirect:
cat /etc/nginx/sites-available/blog | grep -A5 "www\."Nếu www block bị thiếu, sửa lại config và thêm vào (xem Phần 4.1).
OG images hiện domain cũ
Sau khi đổi NEXT_PUBLIC_SITE_URL, phải rebuild:
cd ~/my-blog
./deploy.shSau đó xóa cache trên các nền tảng social:
- Facebook: Dùng Sharing Debugger và click "Scrape Again"
- LinkedIn: Dùng Post Inspector để refresh
- Twitter/X: Cards tự update trong vài giờ
Tổng Kết
Trong phase cuối cùng này, bạn đã:
✅ Đăng ký custom domain trên Hostinger và cấu hình bảng quản lý DNS
✅ Thêm A records cho cả apex domain và www subdomain
✅ Verify DNS propagation bằng dig, nslookup, và online tools
✅ Cập nhật Nginx với tên miền và redirect www → apex
✅ Cấp SSL certificates cover cả chanh.blog và www.chanh.blog
✅ Cập nhật application config với NEXT_PUBLIC_SITE_URL mới
✅ Test end-to-end — HTTPS, redirects, posts, search, và feeds
✅ Submit lên Google Search Console với sitemap để indexing
✅ Verify social sharing previews trên Twitter/X, LinkedIn, và Facebook
Blog đã hoàn thành. Từ một thư mục trống đến blog self-hosted live trên custom domain — bạn đã tự tay build toàn bộ stack.
Series Hoàn Thành!
Chúc mừng! Bạn đã hoàn thành toàn bộ series Build a Personal Blog. Đây là mọi thứ bạn đã build qua 8 bài viết:
| Phase | Bạn Đã Build | Bài Viết |
|---|---|---|
| Overview | Roadmap và kiến trúc series | Build a Personal Blog — Roadmap |
| Phase 1 | Setup project Next.js 16 + ShadCN/UI | Project Setup |
| Phase 2 | MDX on-demand rendering pipeline | MDX Rendering |
| Phase 3 | Tích hợp PostgreSQL + Drizzle ORM | Database Layer |
| Phase 4 | Tags, search, và pagination | Blog Features |
| Phase 5 | Docker Compose cho dev và production | Docker Compose |
| Phase 6 | Deploy lên Ubuntu VPS trên Hostinger | VPS Deployment |
| Phase 7 | Setup custom domain và launch | Bạn đang ở đây |
Giờ bạn có skills để build, deploy, và maintain một production web application từ đầu. Cùng patterns này áp dụng cho bất kỳ project nào — không chỉ blogs. Docker, Nginx, SSL, DNS, và VPS management là skills DevOps nền tảng mà đi đâu cũng dùng được.
Series Index
| Bài | Tiêu Đề | Trạng Thái |
|---|---|---|
| BLOG-1 | Build a Personal Blog — Roadmap | ✅ Hoàn thành |
| BLOG-2 | Phase 1: Project Setup — Next.js 16 + ShadCN/UI | ✅ Hoàn thành |
| BLOG-3 | Phase 2: MDX On-Demand Rendering | ✅ Hoàn thành |
| BLOG-4 | Phase 3: PostgreSQL + Drizzle ORM | ✅ Hoàn thành |
| BLOG-5 | Phase 4: Tags, Search & Pagination | ✅ Hoàn thành |
| BLOG-6 | Phase 5: Docker Compose | ✅ Hoàn thành |
| BLOG-7 | Phase 6: Deploy lên Ubuntu VPS trên Hostinger | ✅ Hoàn thành |
| BLOG-8 | Phase 7: Setup Custom Domain trên Hostinger | ✅ Bạn đang ở đây |
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.