Sau khi đã cài Nginx và hiểu cấu trúc config cơ bản, bước tiếp theo bạn gần như chắc chắn sẽ làm là host nhiều domain trên cùng một VPS. Đây chính là lúc server blocks Nginx phát huy vai trò. Trong Nginx, request trước hết được phân vào server block theo cổng lắng nghe và server_name, sau đó mới tiếp tục được xử lý bởi các location bên trong block đó. Vì vậy, nếu hiểu đúng server blocks, bạn sẽ host nhiều website gọn hơn, route request đúng hơn và tránh được rất nhiều lỗi kiểu “domain này nhảy sang site kia”.
Ở góc độ vận hành thật, bài này không chỉ dừng ở việc “tạo thêm một file config”. Mục tiêu là giúp bạn chuẩn hóa layout webroot, tách log theo site, tạo default fallback an toàn, biết khi nào dùng static site, khi nào reverse proxy sang app khác, và giữ cấu hình đủ sạch để sau này nối tiếp sang PHP-FPM, SSL và troubleshooting dễ hơn. Cách tiếp cận này cũng rất sát với định hướng series B trong file plan của bạn: dựng web stack có thể vận hành được chứ không chỉ chạy thử cho xong.
1. Hiểu đúng về server blocks Nginx trước khi cấu hình
1.1. Server block là gì
Trong Nginx, server block là nơi định nghĩa cách một website hoặc một nhóm request được phục vụ. Theo tài liệu chính thức của Nginx, nhiều server block có thể cùng tồn tại trong cấu hình, và chúng được phân biệt chủ yếu bằng cổng listen và giá trị server_name. Điều đó cho phép bạn host nhiều domain trên cùng một Nginx instance mà không cần dựng nhiều web server riêng.
Trên Ubuntu Server, mô hình phổ biến là đặt từng cấu hình site trong thư mục sites-available, rồi kích hoạt site bằng symlink sang sites-enabled. Canonical hiện vẫn tài liệu hóa cách làm này trong phần cấu hình Nginx cho Ubuntu Server, nên đây vẫn là layout hợp lý nhất nếu bạn muốn hệ thống dễ đọc, dễ backup và dễ bàn giao.
1.2. Nginx chọn request đi vào block nào
Nginx không “đoán” website theo cảm tính. Nó xử lý theo thứ tự khá rõ ràng. Đầu tiên, nó xét request đến cổng nào. Sau đó, trong nhóm các server block cùng lắng nghe cổng đó, nó dùng server_name để chọn block phù hợp. Khi đã vào đúng server block, Nginx mới tiếp tục so khớp location để quyết định phục vụ file tĩnh, đẩy sang PHP-FPM hay reverse proxy sang backend khác.
Đó là lý do bạn nên xem server blocks là tầng định tuyến cấp domain, còn location là tầng định tuyến cấp đường dẫn. Nếu trộn lẫn hai ý này ngay từ đầu, cấu hình sẽ sớm rối khi bạn có thêm blog, API, admin panel hoặc static subdomain.
2. Chuẩn bị layout webroot và naming trước khi host nhiều domain
2.1. Vì sao nên tách webroot theo domain
Khi chỉ có một site, nhiều người để thẳng mọi thứ trong /var/www/html. Cách đó chạy được, nhưng không phù hợp lâu dài. Khi bắt đầu host nhiều domain, bạn nên tách webroot theo từng site, ví dụ:
/var/www/example.com/public
/var/www/blog.example.com/public
/var/www/static.example.com/public
Cách đặt này giúp bạn nhìn là hiểu site nào đang nằm ở đâu, hạn chế nhầm file giữa các dự án và dễ áp dụng quyền file, backup hoặc rollback theo từng site. Nó cũng khớp với tinh thần production-ready trong file plan: mỗi bước cấu hình nên để lại trạng thái rõ ràng, dễ kiểm chứng và dễ quay lui.
2.2. Tách log theo site ngay từ đầu
Nếu tất cả domain cùng ghi vào một access log và một error log, lúc site gặp lỗi bạn sẽ phải lọc log rất mệt. Cách gọn hơn là mỗi server block có log riêng, ví dụ:
/var/log/nginx/example.com.access.log
/var/log/nginx/example.com.error.log
/var/log/nginx/api.example.com.access.log
/var/log/nginx/api.example.com.error.log
Đây không phải yêu cầu bắt buộc để site chạy được, nhưng là thói quen cực đáng giá về vận hành. Khi sang các bài về SSL, log rotation hoặc troubleshooting 502, bạn sẽ thấy việc tách log từ sớm giúp tiết kiệm rất nhiều thời gian.
3. Cấu hình domain chính và www theo kiểu sạch, dễ mở rộng
3.1. Tạo thư mục site và file mẫu
Ví dụ bạn muốn host example.com và www.example.com trên cùng một site tĩnh:
sudo mkdir -p /var/www/example.com/public
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.comprintf '%s\n' '<h1>Hello from example.com</h1>' | sudo tee /var/www/example.com/public/index.html
Nếu site của bạn sau này là PHP app hoặc WordPress, layout thư mục có thể khác đôi chút, nhưng ý tưởng vẫn giữ nguyên: mỗi domain có webroot riêng, rõ ràng từ đầu.
3.2. Tạo server block Nginx cho domain chính
Tạo file config:
sudo nano /etc/nginx/sites-available/example.com
Nội dung mẫu:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com; root /var/www/example.com/public;
index index.html index.htm; access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log warn; location / {
try_files $uri $uri/ =404;
} location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
access_log off;
} location ~ /\. {
deny all;
}
}
Khối trên phục vụ khá tốt cho một site tĩnh hoặc landing page đơn giản. try_files giúp Nginx kiểm tra file thật trước, còn block static riêng cho CSS, JS, ảnh và font giúp bạn bật cache dài ngày cho tài nguyên ít đổi. Nếu sau này nội dung static được build theo version hash, kiểu cache này thường cho hiệu quả tốt mà vẫn an toàn.
How to configure nginx – Ubuntu Server
3.3. Vì sao server_name nên khai báo rõ ràng
Theo tài liệu server_name của Nginx, tên máy chủ trong server block quyết định block nào được chọn cho request tương ứng. Vì vậy, bạn nên khai báo rõ example.com và www.example.com thay vì để mơ hồ hoặc lạm dụng wildcard khi chưa thật sự cần. Điều này giúp routing ổn định hơn và giảm nguy cơ request rơi vào nhầm default server.
4. Host subdomain và route request sang ứng dụng khác
4.1. Khi nào nên dùng subdomain riêng
Một mô hình rất phổ biến là:
example.com phục vụ website chínhblog.example.com phục vụ blog khácapi.example.com reverse proxy sang app backend chạy cổng 3000 hoặc 8080static.example.com phục vụ file tĩnh hoặc asset build sẵn
Cách tách này giúp trách nhiệm của từng block rõ ràng hơn. Domain chính lo website công khai, API có block riêng, còn static có thể tối ưu cache mạnh hơn mà không ảnh hưởng đến HTML hoặc response động.
4.2. Ví dụ reverse proxy cho API
sudo nano /etc/nginx/sites-available/api.example.com
server {
listen 80;
listen [::]:80;
server_name api.example.com; access_log /var/log/nginx/api.example.com.access.log;
error_log /var/log/nginx/api.example.com.error.log warn; location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
Điểm quan trọng ở đây là server block xử lý theo domain, còn bên trong block bạn route toàn bộ request sang backend app. Cách này đặc biệt hợp khi bạn vừa có site HTML/PHP truyền thống, vừa có API Node.js, Go hoặc Python chạy ở cổng nội bộ.
4.3. Khi nào nên tách static sang subdomain
Bạn nên nghĩ đến static.example.com khi asset đủ nhiều, site bắt đầu có nhiều CSS, JS, ảnh hoặc muốn tách trách nhiệm rõ giữa nội dung HTML và tài nguyên tĩnh. Không phải dự án nào cũng bắt buộc làm vậy, nhưng với hệ thống nhiều domain hoặc nhiều app, đây là một pattern rất dễ quản lý.
5. Tạo default fallback để tránh request rơi nhầm site
5.1. Default server có vai trò gì
Khi một request đi tới Nginx nhưng không khớp server_name nào như bạn mong đợi, Nginx sẽ vẫn phải chọn một server block để xử lý. Vì vậy, bạn nên có một block fallback rõ ràng thay vì để request vô tình rơi vào site đang chạy thật. Cách này giúp tránh lộ nội dung không mong muốn khi DNS sai hoặc có request lạ đi vào IP của server. Ý tưởng về “virtual server selection” và block mặc định cũng nằm trong logic xử lý request của Nginx.
5.2. Mẫu block mặc định tối giản
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _; return 444;
}
return 444; là kiểu phản hồi thường được dùng trong Nginx để đóng kết nối mà không gửi response chuẩn. Với nhiều hệ thống public, đây là lựa chọn gọn và sạch cho request rác. Nếu bạn muốn dễ debug hơn trong giai đoạn đầu, có thể đổi sang return 404;.
6. Kích hoạt cấu hình và kiểm tra an toàn
6.1. Enable site bằng symlink
Ubuntu Server docs hiện vẫn hướng dẫn bật site bằng cách tạo symlink từ sites-available sang sites-enabled. Đây là bước đúng chuẩn trên Ubuntu nếu bạn muốn quản lý multi-site gọn gàng.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
6.2. Test rồi mới reload
Sau khi thêm hoặc sửa bất kỳ site nào, hãy giữ đúng quy trình sau:
sudo nginx -t
sudo systemctl reload nginx
Tài liệu Ubuntu dùng đúng flow này khi cấu hình site, còn tài liệu Beginner’s Guide của Nginx cũng nhấn mạnh việc nạp lại cấu hình theo cách an toàn qua signal reload. Đây là thói quen quan trọng nhất của phần B1 và B2: đừng sửa xong rồi restart ngay theo phản xạ.
6.3. Kiểm tra bằng curl trước khi mở trình duyệt
curl -I http://example.com
curl -I http://www.example.com
curl -I http://api.example.com
Nếu domain chưa trỏ DNS xong, bạn có thể tạm kiểm tra bằng file hosts trên máy local hoặc test theo Host header. Mục tiêu là xác nhận đúng block đang phản hồi trước khi triển khai tiếp SSL hoặc PHP-FPM.
7. Tối ưu static đúng chỗ, không tối ưu mù
7.1. Static nào nên cache dài
Các file như CSS build, JS build, font, logo versioned, icon sprite hoặc ảnh ít thay đổi là nhóm phù hợp để cache dài. Với HTML, JSON response hoặc nội dung đổi thường xuyên, bạn không nên áp dụng cùng một chính sách cache chỉ vì “muốn nhanh hơn”. Sai lầm phổ biến nhất là cache quá tay khiến người dùng thấy giao diện cũ dù bạn đã deploy bản mới.
7.2. Nên route theo domain trước, theo path sau
Khi hệ thống bắt đầu có nhiều app, một nguyên tắc đơn giản nhưng rất hiệu quả là tách theo domain trước. Ví dụ, thay vì dồn mọi thứ vào example.com/api, example.com/admin, example.com/assets, bạn có thể dùng api.example.com, admin.example.com, static.example.com. Kiểu tổ chức này làm server blocks rõ hơn, SSL dễ hơn và log cũng đỡ lẫn hơn.
8. Lỗi thường gặp khi host nhiều domain bằng Nginx
8.1. Vẫn giữ site mặc định quá lâu
Bạn cấu hình example.com đúng rồi nhưng request vẫn ra trang mặc định của Nginx. Nguyên nhân thường là site mặc định vẫn đang enabled và đang ăn request trước. Sau khi site mới ổn định, hãy disable site mặc định nếu bạn không còn dùng nó.
8.2. Dùng chung một block cho quá nhiều vai trò
Một server block vừa static, vừa PHP, vừa reverse proxy, vừa redirect, vừa cache lẫn lộn sẽ rất nhanh thành mớ khó debug. Tách trách nhiệm theo domain hoặc ít nhất theo block sẽ bền hơn nhiều.
8.3. Không tách log theo site
Site nào cũng ghi vào một log chung thì lúc lỗi 404, 502 hoặc request bất thường bạn sẽ cực khó lần ra nguyên nhân. Đây là lỗi không làm site sập ngay, nhưng làm khâu vận hành sau này tốn thời gian.
8.4. Quên test config trước khi reload
Lỗi này cũ nhưng luôn lặp lại. nginx -t là bước không nên bỏ qua, đặc biệt khi bạn bắt đầu có nhiều file config, nhiều include và nhiều subdomain.
9. Checklist thực hành
9.1. Trước khi reload
Bạn đã tạo webroot riêng cho từng domain.
Bạn đã tách server block riêng cho domain chính, subdomain hoặc API nếu có.
Bạn đã đặt server_name rõ ràng, không mơ hồ.
Bạn đã khai báo log riêng theo site.
9.2. Sau khi reload
Bạn đã chạy sudo nginx -t thành công.
Bạn đã chạy curl -I với từng domain cần phục vụ.
Bạn đã kiểm tra domain nào đang trả về nội dung nào.
Bạn đã chuẩn bị block mặc định để request lạ không rơi vào site thật.
10. FAQ
10.1. Một VPS có thể host bao nhiêu domain với server blocks
Về mặt kỹ thuật, Nginx hoàn toàn có thể phục vụ nhiều domain trên cùng một instance. Giới hạn thật sự không nằm ở số file config, mà ở CPU, RAM, disk I/O, số lượng request và mức độ nặng của từng ứng dụng. Server blocks chỉ là lớp tổ chức routing, không tự tạo ra tải lớn nếu bạn cấu hình gọn.
10.2. Có cần mỗi domain một cổng khác nhau không
Không. Với name-based virtual hosting, nhiều domain hoàn toàn có thể cùng nghe trên port 80 hoặc 443 và được tách bằng server_name. Đây là logic được Nginx tài liệu hóa rất rõ trong phần server_name và cách xử lý request.
10.3. Khi nào nên dùng subdomain thay vì path
Khi bạn muốn tách hẳn trách nhiệm giữa website chính, API, blog, admin hoặc static assets. Với hệ thống nhỏ, dùng path vẫn được. Nhưng khi cần log riêng, SSL riêng, cache riêng hoặc reverse proxy sang app khác, subdomain thường gọn hơn.
10.4. Sau bài này nên đi tiếp bài nào
Đi hợp lý nhất là sang bài về PHP-FPM để nối Nginx với PHP, hoặc bài SSL Let’s Encrypt để bật HTTPS cho từng domain. Nếu bạn mới hoàn tất B1 và B2, đây chính là thời điểm đẹp để stack bắt đầu giống production thật.
11. Kết luận
Server blocks là nền tảng để biến một VPS chạy Nginx thành nơi có thể host nhiều domain, route request đúng ứng dụng và tối ưu static theo từng mục tiêu riêng. Nếu làm đúng từ bước này, bạn sẽ thấy việc thêm site mới, tách API, bật SSL hay đọc log theo site nhẹ hơn rất nhiều.
Điểm quan trọng nhất không phải là viết block thật dài, mà là giữ cấu trúc thật rõ: một domain hoặc một vai trò chính tương ứng một server block, naming rõ ràng, log rõ ràng, fallback rõ ràng, và mọi thay đổi đều phải đi qua nginx -t trước khi reload.

