Quay lại
Frontend
5 phút đọc23 tháng 3, 20267

Setup NGINX + Next.js Standalone: Hướng dẫn từ thực chiến

Hướng dẫn chi tiết cách setup NGINX làm reverse proxy cho Next.js standalone mode, kèm tips từ kinh nghiệm deploy thực tế.

N

Nguyen Nhat Long

@longnn

Mình từng mất nguyên một buổi chiều debug lỗi 502 Bad Gateway chỉ vì thiếu một dòng config NGINX. Chuyện deploy Next.js nghe thì đơn giản, nhưng khi kết hợp với NGINX ở chế độ standalone, có kha khá thứ dễ sai nếu bạn chưa quen. Bài này mình chia sẻ lại toàn bộ flow setup từ đầu đến cuối, kèm những lỗi mình từng dính để bạn khỏi phải đạp lại vết xe đổ.

Tại sao lại là Standalone + NGINX?

Mặc định khi bạn npm run build một app Next.js, output sẽ phụ thuộc vào toàn bộ node_modules — nặng, chậm copy, và không tối ưu cho production. Chế độ standalone giải quyết đúng pain point này: Next.js sẽ bundle ra một thư mục .next/standalone chứa đúng những gì cần thiết để chạy, bao gồm cả một Node.js server tối giản.

Còn NGINX? Nó đóng vai trò reverse proxy đứng trước, lo việc handle SSL, serve static files, load balancing, và bảo vệ app Node.js phía sau. Theo kinh nghiệm của mình, combo này chạy cực kỳ ổn định trên các VPS từ 1GB RAM trở lên.

Build Next.js ở chế độ Standalone

Bước đầu tiên, mở file next.config.js và thêm một dòng duy nhất:

JavaScript
1module.exports = {
2 output: 'standalone',
3 // các cấu hình khác nếu có
4};

Sau đó build như bình thường:

Terminal
1npm run build

Khi build xong, bạn sẽ thấy thư mục .next/standalone xuất hiện. Thư mục này chứa file server.js và tất cả dependencies đã được bundle sẵn. Bạn chỉ cần copy thư mục này lên server — không cần chạy `npm install` trên server nữa.

Điều mình thấy hay là output standalone nhẹ hơn đáng kể so với cách deploy truyền thống. Một app Next.js trung bình mà mình từng làm giảm từ ~400MB (với node_modules) xuống còn ~50MB. Deploy nhanh hơn rất nhiều.

Lưu ý quan trọng: Thư mục .next/staticpublic không được copy tự động vào standalone. Bạn cần copy thủ công hoặc để NGINX serve trực tiếp — mà cách sau mới là best practice.

Cài đặt môi trường trên Server

Mình giả sử bạn đang dùng Ubuntu/Debian. Đầu tiên, cài NGINX:

Terminal
1sudo apt update
2sudo apt install nginx -y

Tiếp theo, cài PM2 để quản lý process Node.js. Đừng chạy node server.js trần — app sẽ chết khi bạn tắt terminal hoặc khi có lỗi runtime.

Terminal
1npm install -g pm2

Di chuyển vào thư mục chứa app standalone trên server và start với PM2:

Terminal
1cd /duong/dan/den/ung/dung/cua/ban
2pm2 start "npm run start" --name nextjs-app
3# hoặc nếu bạn muốn chỉ định port khác:
4# pm2 start "npm run start -- -p 4000" --name nextjs-app

Mình hay thêm pm2 startuppm2 save để PM2 tự khởi động lại app khi server reboot. Nhiều bạn quên bước này rồi thắc mắc sao restart server xong app không lên — mình cũng từng vậy.

Cấu hình NGINX làm Reverse Proxy

Đây là phần quan trọng nhất. Tạo file config mới:

Terminal
1sudo nano /etc/nginx/sites-available/nextjs.conf

Thêm nội dung sau:

Nginx
1server {
2 listen 80;
3 server_name your_domain_or_ip www.your_domain_or_ip;
4
5 # Reverse proxy tới Next.js
6 location / {
7 proxy_pass http://localhost:3000;
8 proxy_http_version 1.1;
9 proxy_set_header Upgrade $http_upgrade;
10 proxy_set_header Connection 'upgrade';
11 proxy_set_header Host $host;
12 proxy_cache_bypass $http_upgrade;
13 }
14
15 # Serve static files trực tiếp qua NGINX — nhanh hơn nhiều so với qua Node.js
16 location /_next/static/ {
17 alias /duong/dan/den/ung/dung/cua/ban/.next/static/;
18 expires 1y;
19 access_log off;
20 }
21
22 # Serve public assets
23 location /static/ {
24 alias /duong/dan/den/ung/dung/cua/ban/public/static/;
25 expires 1y;
26 access_log off;
27 }
28}

Mấy dòng proxy_set_header không phải để cho đẹp đâu — thiếu UpgradeConnection là WebSocket sẽ không hoạt động, và thiếu Host thì Next.js sẽ không nhận đúng hostname, ảnh hưởng đến các tính năng như next/image hay middleware.

Kích hoạt config và restart NGINX:

Terminal
1sudo ln -s /etc/nginx/sites-available/nextjs.conf /etc/nginx/sites-enabled/
2sudo rm /etc/nginx/sites-enabled/default
3sudo nginx -t
4sudo systemctl restart nginx

Luôn chạy `nginx -t` trước khi restart. Mình đã thấy không ít trường hợp anh em restart NGINX mà không test trước, config sai → NGINX chết → toàn bộ site down. Một thói quen nhỏ nhưng cứu mạng.

Thêm HTTPS với Let's Encrypt

Năm 2025 rồi, không có lý do gì để chạy HTTP thuần cả. Certbot làm việc này cực kỳ đơn giản:

Terminal
1sudo apt install certbot python3-certbot-nginx -y
2sudo certbot --nginx -d your_domain -d www.your_domain

Certbot sẽ tự động sửa file config NGINX của bạn, thêm các block SSL và redirect HTTP → HTTPS. Nó cũng tự setup cron job để renew certificate trước khi hết hạn.

Những lỗi mình từng gặp

502 Bad Gateway: 90% là do app Next.js chưa chạy hoặc chạy sai port. Kiểm tra pm2 status và đảm bảo port trong config NGINX khớp với port app đang listen.

Static files trả về 404: Đường dẫn alias trong NGINX phải trỏ chính xác đến thư mục .next/static/ — thiếu dấu / cuối cùng là đủ để lỗi.

App chạy nhưng CSS/JS không load: Bạn quên copy thư mục .next/static lên server, hoặc đường dẫn alias sai.

Permission denied: NGINX chạy dưới user www-data. Đảm bảo user này có quyền đọc thư mục chứa static files.

Những điều nên nhớ

  • Standalone mode giảm đáng kể dung lượng deploy — không cần node_modules trên server
  • PM2 là must-have để giữ app luôn sống, đừng chạy node trần
  • NGINX serve static files trực tiếp sẽ nhanh hơn rất nhiều so với để Node.js xử lý
  • Luôn chạy `nginx -t` trước khi restart
  • HTTPS không phải optional — Certbot setup trong 2 phút, không có lý do bỏ qua
  • Nhớ copy `.next/static` và `public` lên server vì standalone không bundle chúng

Setup này mình đã dùng cho cả dự án cá nhân lẫn production của công ty, handle vài nghìn concurrent users mà không vấn đề gì. Nếu bạn đang deploy Next.js lần đầu, hy vọng bài này giúp bạn tiết kiệm được vài giờ debug. Có gì thắc mắc thì cứ trao đổi thêm nhé!

NN

Nguyen Nhat Long

@longnn

Thấy hay? Chia sẻ cho bạn bè!

Bài viết liên quan

Có thể bạn cũng thích

Xem tất cả