Ở Bài 46, chúng ta đã đưa Laravel 13 lên VPS.
Nhưng một Laravel project trên VPS chưa thể tự nhiên hoạt động như một website production chỉ bằng việc upload source code.
Chúng ta cần một Web Server.
Một mô hình phổ biến là:
Internet
│
▼
Nginx
│
▼
PHP-FPM
│
▼
Laravel 13
│
├── MySQL
├── Storage
└── Cache
Trong bài này chúng ta sẽ học cách cấu hình Nginx cho Laravel 13.
1. Nginx là gì?
Nginx là một web server và reverse proxy rất phổ biến.
Nó có thể:
nhận HTTP/HTTPS request;
phục vụ file tĩnh;
xử lý domain;
chuyển request PHP cho PHP-FPM;
reverse proxy;
load balancing;
cache;
giới hạn request;
cấu hình HTTPS;
chạy nhiều website trên cùng một VPS.
Trong Laravel deployment, Nginx thường đứng phía trước PHP-FPM.
Ví dụ:
Browser
│
│ HTTPS
▼
Nginx
│
│ FastCGI
▼
PHP-FPM
│
▼
Laravel
2. Tại sao Laravel cần Nginx?
Laravel không phải là web server production.
Khi development, chúng ta thường chạy:
php artisan serve
Sau đó truy cập:
http://127.0.0.1:8000
Đây là môi trường phát triển.
Production thường sử dụng:
Nginx
+
PHP-FPM
+
Laravel
Nginx nhận request:
GET /
sau đó đưa request đến Laravel.
3. Nginx và PHP-FPM
Đây là phần rất quan trọng.
Nginx không trực tiếp chạy PHP.
PHP được xử lý bởi:
PHP-FPM
PHP-FPM là:
PHP FastCGI Process Manager
Có thể hình dung:
┌──────────────┐
│ Browser │
└──────┬───────┘
│
▼
┌──────────────┐
│ Nginx │
└──────┬───────┘
│
FastCGI │
▼
┌──────────────┐
│ PHP-FPM │
└──────┬───────┘
│
▼
┌──────────────┐
│ Laravel 13 │
└──────────────┘
Nginx đảm nhiệm phần web server.
PHP-FPM đảm nhiệm việc chạy PHP.
Laravel xử lý application logic.
4. Laravel 13 yêu cầu PHP phiên bản nào?
Laravel 13 yêu cầu:
PHP >= 8.3
Vì vậy server của chúng ta trong khóa học sử dụng:
Ubuntu
Nginx
PHP 8.3
PHP-FPM
MySQL
Composer
Laravel 13
Laravel cũng yêu cầu một số PHP extension như:
Ctype
cURL
DOM
Fileinfo
Filter
Hash
Mbstring
OpenSSL
PCRE
PDO
Session
Tokenizer
XML
Đây là các yêu cầu chính thức của Laravel 13.
5. Kiểm tra Nginx
Trên VPS:
nginx -v
Ví dụ:
nginx version: nginx/1.24.0
Kiểm tra trạng thái:
sudo systemctl status nginx
Nếu đang chạy:
Active: active (running)
Khởi động:
sudo systemctl start nginx
Cho Nginx tự khởi động cùng server:
sudo systemctl enable nginx
Restart:
sudo systemctl restart nginx
Reload:
sudo systemctl reload nginx
6. Kiểm tra PHP-FPM
Ví dụ PHP 8.3:
sudo systemctl status php8.3-fpm
Nếu chưa chạy:
sudo systemctl start php8.3-fpm
Enable:
sudo systemctl enable php8.3-fpm
Restart:
sudo systemctl restart php8.3-fpm
7. Nginx configuration nằm ở đâu?
Trên Ubuntu, các file thường nằm trong:
/etc/nginx/
Cấu trúc phổ biến:
/etc/nginx/
├── nginx.conf
├── sites-available/
│ └── default
└── sites-enabled/
└── default
Trong đó:
sites-available
chứa configuration của website.
sites-enabled
chứa các website đang được enable.
8. Tạo configuration cho Laravel
Giả sử project của chúng ta nằm tại:
/var/www/blog
Laravel có:
/var/www/blog/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── vendor/
└── artisan
Điểm quan trọng:
Nginx phải trỏ vào thư mục
public.
Không được trỏ vào:
/var/www/blog
mà phải là:
/var/www/blog/public
Laravel chính thức cũng yêu cầu web server hướng request đến public/index.php, thay vì project root, nhằm tránh expose các file nhạy cảm như .env.
9. Tạo Server Block
Tạo file:
sudo nano /etc/nginx/sites-available/blog
Nội dung:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/blog/public;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 32k;
fastcgi_buffers 8 32k;
fastcgi_busy_buffers_size 64k;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Đây là cấu hình dựa trên cấu hình Nginx production được Laravel 13 documentation cung cấp, với đường dẫn project và PHP-FPM socket cần thay đổi theo server thực tế.
10. Giải thích server
server {
}
Một server block đại diện cho một website hoặc virtual host.
Ví dụ:
example.com
có thể có một server block.
Website khác:
abc.com
có thể có server block khác.
Do đó một VPS có thể chạy:
VPS
│
├── example.com
│
├── abc.com
│
└── xyz.com
11. listen
listen 80;
Port:
80
dành cho HTTP.
IPv6:
listen [::]:80;
Khi dùng HTTPS sau này:
listen 443 ssl;
12. server_name
Ví dụ:
server_name example.com www.example.com;
Nginx sẽ biết request dành cho website nào.
Ví dụ browser truy cập:
https://example.com
Nginx tìm server block có:
server_name example.com;
13. root
Đây là một trong những dòng quan trọng nhất:
root /var/www/blog/public;
Không phải:
root /var/www/blog;
Mà:
root /var/www/blog/public;
Vì Laravel có cấu trúc:
blog/
├── app/
├── config/
├── resources/
├── routes/
├── storage/
├── vendor/
└── public/
├── index.php
├── build/
├── favicon.ico
└── ...
Browser chỉ nên truy cập phần:
public/
14. Tại sao không trỏ vào project root?
Giả sử chúng ta cấu hình:
root /var/www/blog;
Khi đó có nguy cơ expose các file như:
.env
composer.json
composer.lock
artisan
config/
storage/
Đặc biệt .env chứa những thông tin như:
DB_PASSWORD
MAIL_PASSWORD
AWS_SECRET
APP_KEY
Đây là vấn đề bảo mật nghiêm trọng.
Do đó:
❌ /var/www/blog
phải chuyển thành:
✅ /var/www/blog/public
15. index
index index.php;
Khi request đến:
/
Nginx biết entry point chính là:
index.php
Laravel sử dụng:
public/index.php
làm entry point của ứng dụng.
16. location /
Phần quan trọng:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Đây là phần giúp Laravel routing hoạt động.
Ví dụ:
/blog
hoặc:
/blog/laravel-13
không nhất thiết phải là file vật lý.
Nginx sẽ thử:
$uri
Nếu không tồn tại:
$uri/
Nếu vẫn không tồn tại:
/index.php
và giữ query string.
Laravel sau đó nhận request và xử lý:
Route::get('/blog', ...);
hoặc:
Route::get('/blog/{post}', ...);
17. Hiểu try_files bằng ví dụ
Giả sử browser gọi:
/images/logo.png
Nếu file tồn tại:
public/images/logo.png
Nginx phục vụ trực tiếp.
Không cần PHP.
Nhưng nếu gọi:
/blog
không có file:
public/blog
Nginx chuyển request về:
public/index.php
Sau đó Laravel xử lý route.
18. Request Laravel thực tế
Ví dụ:
GET /blog
Luồng xử lý:
Browser
│
▼
Nginx
│
├── public/blog tồn tại?
│ │
│ └── Không
│
▼
public/index.php
│
▼
PHP-FPM
│
▼
Laravel Router
│
▼
BlogController
│
▼
Blade
│
▼
HTML
19. location ~ ^/index\.php
Đây là phần chuyển PHP request cho PHP-FPM:
location ~ ^/index\.php(/|$) {
Laravel 13 documentation sử dụng chính pattern này trong cấu hình Nginx production.
20. fastcgi_pass
Ví dụ:
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
Nginx nói:
Hãy chuyển PHP request cho PHP-FPM 8.3.
Socket có thể khác tùy server.
Kiểm tra:
ls /var/run/php/
Có thể thấy:
php8.3-fpm.sock
Khi đó:
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
Nếu server sử dụng PHP phiên bản khác thì đường dẫn phải thay đổi tương ứng.
21. SCRIPT_FILENAME
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
Dòng này cho PHP-FPM biết file PHP thực tế cần chạy.
Laravel configuration chính thức cũng sử dụng:
$realpath_root$fastcgi_script_name
trong server block mẫu.
22. include fastcgi_params
include fastcgi_params;
Nó đưa các FastCGI parameters cần thiết vào request.
Không nên tùy tiện bỏ dòng này khỏi cấu hình.
23. Chặn file ẩn
Cấu hình:
location ~ /\.(?!well-known).* {
deny all;
}
Mục đích là chặn truy cập đến các file hoặc thư mục bắt đầu bằng:
.
Ví dụ:
.env
.git
.gitignore
Trong đó .env đặc biệt quan trọng.
Laravel 13 documentation cũng đưa rule này vào cấu hình Nginx mẫu.
24. favicon.ico và robots.txt
Có thể sử dụng:
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
Hai file này thường được browser hoặc crawler truy cập thường xuyên.
Không cần ghi log những request không đáng kể này.
25. Enable website
Sau khi tạo:
/etc/nginx/sites-available/blog
tạo symbolic link:
sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog
Có thể xóa site mặc định:
sudo rm /etc/nginx/sites-enabled/default
26. Kiểm tra configuration
Đừng restart Nginx ngay.
Trước tiên:
sudo nginx -t
Nếu đúng:
syntax is ok
test is successful
Sau đó:
sudo systemctl reload nginx
Đây là thói quen rất quan trọng.
Luôn:
Sửa config
↓
nginx -t
↓
OK?
↓
reload nginx
Không nên:
Sửa config
↓
restart ngay
nếu chưa kiểm tra syntax.
27. Cấu hình domain
Giả sử domain:
example.com
DNS cần trỏ:
example.com → IP VPS
Ví dụ:
A
example.com
123.123.123.123
Và:
A
www
123.123.123.123
Sau đó Nginx:
server_name example.com www.example.com;
28. Một VPS chạy nhiều website
Nginx cho phép:
VPS
│
├── example.com
│ └── /var/www/example
│
├── blog.com
│ └── /var/www/blog
│
└── shop.com
└── /var/www/shop
Mỗi website có một server block.
Ví dụ:
/etc/nginx/sites-available/
├── example
├── blog
└── shop
và:
/etc/nginx/sites-enabled/
├── example
├── blog
└── shop
Đây là một trong những lý do Nginx rất phù hợp với VPS.
29. HTTPS
Website production nên sử dụng:
HTTPS
thay vì:
HTTP
Mô hình:
Browser
│
HTTPS :443
▼
Nginx
│
▼
PHP-FPM
│
▼
Laravel
Thông thường có thể sử dụng Let's Encrypt + Certbot để cấp SSL certificate.
Sau khi cấu hình SSL:
http://example.com
có thể redirect sang:
https://example.com
30. Nginx và Laravel Storage
Trong Blog CMS của chúng ta có upload:
Post Image
Laravel public disk mặc định lưu tại:
storage/app/public
Để browser truy cập được, tạo symbolic link:
php artisan storage:link
Khi đó:
storage/app/public
│
│ symbolic link
▼
public/storage
Laravel documentation cũng sử dụng storage:link cho public disk.
Ví dụ database lưu:
posts/abc.jpg
thì URL có thể là:
Storage::url($post->image)
hoặc:
asset('storage/' . $post->image)
31. Nginx và file upload
Blog CMS của chúng ta có upload image.
Nginx cũng có giới hạn kích thước request.
Ví dụ:
client_max_body_size 10M;
Nếu website cho phép upload file lớn hơn mặc định, có thể cấu hình:
server {
...
client_max_body_size 10M;
}
Sau đó:
sudo nginx -t
và:
sudo systemctl reload nginx
Tuy nhiên còn phải kiểm tra giới hạn của PHP:
upload_max_filesize
post_max_size
Ví dụ:
upload_max_filesize = 10M
post_max_size = 12M
Không nên chỉ sửa Nginx mà quên PHP.
32. Lỗi 413 Request Entity Too Large
Nếu upload file quá lớn, Nginx có thể trả:
413 Request Entity Too Large
Nguyên nhân thường liên quan:
client_max_body_size
Ví dụ:
client_max_body_size 2M;
nhưng người dùng upload:
5MB
Nginx có thể chặn request trước khi Laravel nhận được.
33. Nginx log
Log thường nằm trong:
/var/log/nginx/
Ví dụ:
access.log
error.log
Xem error log:
sudo tail -f /var/log/nginx/error.log
Xem access log:
sudo tail -f /var/log/nginx/access.log
Khi website gặp lỗi:
502
403
404
413
hãy kiểm tra log.
34. Lỗi 502 Bad Gateway
Một lỗi rất phổ biến:
502 Bad Gateway
Nếu:
Nginx
│
X
PHP-FPM
thì Nginx không thể giao tiếp với PHP-FPM.
Kiểm tra:
sudo systemctl status php8.3-fpm
Kiểm tra socket:
ls /var/run/php/
Ví dụ socket thực tế là:
php8.3-fpm.sock
nhưng Nginx lại cấu hình:
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
thì sẽ lỗi.
35. Lỗi 403 Forbidden
Nếu gặp:
403 Forbidden
có thể kiểm tra:
quyền thư mục;
owner;
Nginx configuration;
public/;file permissions.
Laravel cần web server có quyền ghi vào:
storage/
và:
bootstrap/cache/
Laravel deployment documentation cũng yêu cầu các thư mục này phải writable bởi web server process.
36. Lỗi 404
Nếu:
/blog
trả:
404
hãy kiểm tra:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Nếu thiếu:
/index.php
Laravel routing có thể không hoạt động đúng.
Sau đó kiểm tra route:
php artisan route:list
37. Lỗi CSS/JS không tải
Nếu Laravel chạy nhưng:
CSS
JS
Image
không hiển thị, kiểm tra:
public/build
Nếu dùng Vite, trên server cần build:
npm install
npm run build
Sau đó kiểm tra:
public/build/
Laravel không tự động biến source frontend thành production assets chỉ vì Nginx được cấu hình.
38. Nginx không thay thế Laravel
Một hiểu nhầm phổ biến:
Nginx = Laravel
Không đúng.
Nginx chỉ là web server.
Kiến trúc:
Nginx
↓
PHP-FPM
↓
Laravel
↓
Application
↓
Database
Mỗi thành phần có nhiệm vụ riêng.
39. Nginx + Laravel + MySQL
Một server Laravel production hoàn chỉnh có thể nhìn như sau:
INTERNET
│
▼
┌─────────┐
│ Nginx │
└────┬────┘
│
▼
┌───────────┐
│ PHP-FPM │
└─────┬─────┘
│
▼
┌─────────────┐
│ Laravel 13 │
└──────┬──────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
MySQL Storage Cache
Đây chính là kiến trúc cơ bản mà chúng ta đang xây dựng cho Blog CMS.
40. Production Nginx configuration
Một configuration tương đối hoàn chỉnh:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/blog/public;
index index.php;
charset utf-8;
client_max_body_size 10M;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 32k;
fastcgi_buffers 8 32k;
fastcgi_busy_buffers_size 64k;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Laravel 13's official deployment example also includes security headers such as X-Frame-Options and X-Content-Type-Options, the Laravel try_files rule, PHP-FPM configuration and hidden-file protection.
41. Sau khi sửa Nginx phải làm gì?
Quy trình:
sudo nginx -t
Nếu:
syntax is ok
test is successful
thì:
sudo systemctl reload nginx
Kiểm tra:
sudo systemctl status nginx
Sau đó mở:
https://example.com
42. Checklist Laravel 13 + Nginx
Trước khi xem deployment là hoàn thành:
[ ] PHP >= 8.3
[ ] PHP-FPM đang chạy
[ ] Nginx đang chạy
[ ] root trỏ tới /public
[ ] server_name đúng domain
[ ] try_files đúng
[ ] fastcgi_pass đúng PHP-FPM socket
[ ] SCRIPT_FILENAME đúng
[ ] .env không public
[ ] storage/ có quyền ghi
[ ] bootstrap/cache có quyền ghi
[ ] php artisan storage:link
[ ] npm run build
[ ] nginx -t thành công
[ ] nginx reload
[ ] HTTPS hoạt động
[ ] APP_DEBUG=false
43. Bài tập thực hành
Bài tập 1
Kiểm tra Nginx:
nginx -v
Bài tập 2
Kiểm tra PHP-FPM:
sudo systemctl status php8.3-fpm
Bài tập 3
Tìm PHP-FPM socket:
ls /var/run/php/
Bài tập 4
Tạo server block:
/etc/nginx/sites-available/blog
và cấu hình Laravel:
/var/www/blog/public
Bài tập 5
Kiểm tra:
sudo nginx -t
Bài tập 6
Reload:
sudo systemctl reload nginx
Bài tập 7
Mở Blog CMS và kiểm tra:
/
├── Login
├── Register
├── Blog
├── Category
├── Post
└── Admin
Bài tập 8
Upload một hình ảnh vào Blog CMS.
Kiểm tra:
storage/app/public
và:
public/storage
44. Tổng kết
Sau bài này chúng ta đã hiểu được:
Nginx
là gì.
PHP-FPM
là gì.
Và quan trọng nhất:
Nginx
↓
PHP-FPM
↓
Laravel 13
Chúng ta đã biết:
cài và kiểm tra Nginx;
kiểm tra PHP-FPM;
tạo Server Block;
cấu hình
server_name;cấu hình
root;trỏ Nginx vào
public/;cấu hình
try_files;cấu hình PHP-FPM;
bảo vệ file ẩn;
cấu hình upload;
cấu hình domain;
chạy nhiều website trên một VPS;
đọc Nginx log;
xử lý lỗi 403;
xử lý lỗi 404;
xử lý lỗi 502;
xử lý lỗi 413;
kết hợp Nginx với Laravel Storage;
kiểm tra configuration bằng
nginx -t;reload Nginx.
Kiến trúc chúng ta đang hướng tới:
DOMAIN
│
▼
┌──────────┐
│ Nginx │
└────┬─────┘
│
▼
┌───────────┐
│ PHP-FPM │
└─────┬─────┘
│
▼
┌─────────────┐
│ Laravel 13 │
└──────┬──────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
MySQL Storage Cache
Đây là nền tảng rất quan trọng trước khi chúng ta đi tiếp vào Queue Worker và Scheduler.
📌 Ghi nhớ
Có 5 điều đặc biệt quan trọng:
1. Nginx không chạy PHP trực tiếp
Nginx → PHP-FPM → Laravel
2. Laravel phải trỏ vào public
root /var/www/blog/public;
3. Laravel route cần try_files
try_files $uri $uri/ /index.php?$query_string;
4. PHP-FPM socket phải chính xác
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
5. Luôn kiểm tra configuration trước khi reload
sudo nginx -t
sudo systemctl reload nginx
Bài tiếp theo: Bài 48 — Queue Worker & Scheduler
Chúng ta sẽ đưa những phần chạy nền của Blog CMS lên production:
Laravel Queue
│
▼
Queue Worker
│
├── Send Mail
├── Notification
├── Job
└── Background Task
Scheduler
│
├── Cron
├── Daily Task
├── Cleanup
└── Scheduled Job
Đây là bước biến một Laravel project từ website đơn giản thành một ứng dụng production thực tế.




Không có nhận xét nào:
Đăng nhận xét