Hướng dẫn cấu hình cô lập website trên NGINX + PHP-FPM
- 31-03-2026
- Toanngo92
- 0 Comments
Dưới đây là một bài tổng hợp anh có thể chỉnh nhẹ rồi đăng website ngay:
Mục lục
Cách cô lập website trên NGINX + PHP-FPM để một site bị hack không ảnh hưởng site khác
Trong quá trình vận hành nhiều website trên cùng một VPS hoặc server vật lý, một rủi ro rất thường gặp là: chỉ cần một website bị khai thác, các website còn lại cũng có thể bị ảnh hưởng theo. Đây là tình huống đặc biệt nguy hiểm với các hệ thống chạy nhiều domain WordPress, Laravel hoặc website PHP trên cùng một máy chủ.
Nguyên nhân thường không nằm ở NGINX đơn thuần, mà nằm ở cách triển khai PHP-FPM, quyền thư mục và cấu trúc phân quyền của toàn server. Nếu nhiều website cùng chạy chung một user hoặc chung một pool PHP-FPM, kẻ tấn công chỉ cần chiếm được một site là có thể đọc, sửa hoặc chèn mã độc sang site khác.
Bài viết này tổng hợp cách cấu hình đúng để cô lập từng website, giúp giảm tối đa nguy cơ lây chéo khi có sự cố bảo mật.
Vì sao một site bị hack có thể ảnh hưởng site khác?
Nhiều quản trị viên triển khai nhiều website theo kiểu:
- tất cả source nằm trong
/var/www/ - owner đều là
www-data - tất cả site dùng chung một PHP-FPM pool
- quyền file/thư mục quá rộng
- nhiều site dùng chung database user
Cách làm này có ưu điểm là nhanh và dễ quản lý lúc đầu, nhưng lại tạo ra một vấn đề lớn: các website không được tách biệt thực sự.
Khi một site bị chèn webshell hoặc bị khai thác lỗ hổng upload file, đoạn mã độc đó sẽ chạy với quyền của user đang xử lý PHP. Nếu user này có quyền đọc/ghi sang thư mục của các website khác, hacker có thể:
- đọc file cấu hình của site khác
- lấy thông tin database
- chèn backdoor sang plugin hoặc theme của site khác
- sửa file
index.php,functions.php,wp-config.php - rải mã độc hàng loạt trên toàn bộ server
Nói cách khác, một website bị hack sẽ không còn là sự cố cục bộ, mà có thể trở thành sự cố toàn hệ thống.
Sai lầm phổ biến: nghĩ rằng chỉ cần cấu hình NGINX là đủ
NGINX rất quan trọng, nhưng NGINX không phải là lớp quyết định việc PHP có thể “đụng” sang site khác hay không. Phần quyết định chính là:
- user Linux chạy tiến trình PHP
- pool PHP-FPM
- quyền sở hữu file/thư mục
- socket riêng cho từng site
- giới hạn đường dẫn PHP được truy cập
Vì vậy, muốn chống lây chéo thật sự thì phải xử lý đồng thời cả NGINX, PHP-FPM và filesystem permissions.
Giải pháp đúng: mỗi website là một môi trường PHP riêng
Cách triển khai an toàn hơn là:
- mỗi website có một user Linux riêng
- mỗi website có một pool PHP-FPM riêng
- mỗi website có một socket riêng
- mỗi website có owner thư mục riêng
- mỗi website có thư mục tmp, session, log riêng
Ví dụ với domain example.com, ta nên có cấu trúc:
- user:
example - group:
example - source code:
/var/www/example.com - socket PHP-FPM:
/run/php/example.sock - pool PHP-FPM:
[example]
Cách làm này giúp code PHP của site chỉ chạy bằng quyền của chính site đó, thay vì chạy chung bằng www-data như mô hình mặc định.
Ví dụ cấu hình PHP-FPM pool riêng cho từng website
Giả sử website là hocvietcode.com, ta tạo file:
/etc/php/8.4/fpm/pool.d/hocvietcode.conf
[hocvietcode]
user = hocvietcode
group = hocvietcode
listen = /run/php/hocvietcode.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 8
pm.process_idle_timeout = 10s
pm.max_requests = 300
chdir = /
php_admin_value[open_basedir] = /var/www/hocvietcode.com:/tmp:/usr/share/php
php_admin_value[upload_tmp_dir] = /var/www/hocvietcode.com/tmp
php_admin_value[session.save_path] = /var/www/hocvietcode.com/tmp/sessions
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/www/hocvietcode.com/logs/php-error.log
security.limit_extensions = .php
Cấu hình trên có vài ý nghĩa rất quan trọng:
- PHP của site chạy bằng user
hocvietcode - NGINX kết nối vào site này qua socket riêng
- PHP chỉ được phép truy cập trong các đường dẫn cần thiết
- file upload tạm, session và log đều tách riêng
- chỉ cho phép thực thi file
.php
Đây là bước cốt lõi để cô lập từng website trên cùng máy chủ.
Tạo user riêng cho website
Trước khi dùng pool riêng, cần tạo user Linux tương ứng:
groupadd hocvietcode
useradd -r -g hocvietcode -d /var/www/hocvietcode.com -s /usr/sbin/nologin hocvietcode
Sau đó gán quyền sở hữu thư mục:
chown -R hocvietcode:hocvietcode /var/www/hocvietcode.com
Tạo thư mục phục vụ session và log:
mkdir -p /var/www/hocvietcode.com/tmp/sessions
mkdir -p /var/www/hocvietcode.com/logs
chown -R hocvietcode:hocvietcode /var/www/hocvietcode.com/tmp
chown -R hocvietcode:hocvietcode /var/www/hocvietcode.com/logs
Nếu bỏ qua bước tạo user mà đã khai báo trong pool PHP-FPM, dịch vụ sẽ báo lỗi kiểu:
cannot get uid for user 'hocvietcode'
Đây là lỗi phổ biến khi mới bắt đầu tách pool riêng.
Cấu hình NGINX trỏ đúng vào socket riêng
Sau khi có pool PHP-FPM riêng, NGINX cần trỏ đúng vào socket tương ứng. Ví dụ:
server {
server_name hocvietcode.com www.hocvietcode.com;
root /var/www/hocvietcode.com/public_html;
index index.php index.html index.htm;
disable_symlinks if_not_owner from=$document_root;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/hocvietcode.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Điểm cần chú ý là dòng:
fastcgi_pass unix:/run/php/hocvietcode.sock;
Dòng này phải khớp hoàn toàn với listen = /run/php/hocvietcode.sock trong file pool. Chỉ cần sai một ký tự cũng có thể gây lỗi 502 Bad Gateway.
Vì sao sau khi đổi sang pool riêng thường gặp lỗi 502?
Đây là lỗi rất hay gặp khi chuyển từ cấu hình mặc định sang mô hình cô lập website.
Một số nguyên nhân phổ biến gồm:
1. NGINX vẫn trỏ vào socket cũ
Ví dụ PHP-FPM đã chuyển sang /run/php/hocvietcode.sock, nhưng NGINX vẫn dùng /run/php/php8.4-fpm.sock.
2. Chưa restart đúng service PHP-FPM
Nhiều người test bằng lệnh:
php-fpm8.4 -t
Lệnh này chỉ kiểm tra cú pháp, chưa có nghĩa là service đã reload cấu hình. Trên Ubuntu/Debian, service đúng thường là:
systemctl restart php8.4-fpm
Không phải php-fpm8.4 và cũng không phải php-fpm.
3. NGINX chưa reload cấu hình mới
Sau khi sửa vhost, cần chạy:
nginx -t
systemctl reload nginx
4. Socket chưa được tạo
Sau khi restart PHP-FPM, cần kiểm tra:
ls -l /run/php/
Phải thấy file socket mới, ví dụ hocvietcode.sock.
Quyền file và thư mục nên đặt thế nào?
Tách pool riêng là chưa đủ nếu quyền file vẫn quá rộng. Một cấu hình an toàn hơn nên theo hướng:
- thư mục:
755 - file code:
644 - file nhạy cảm như
wp-config.php:600 - thư mục
tmp,sessions,logs:700
Ví dụ:
find /var/www/hocvietcode.com -type d -exec chmod 755 {} \;
find /var/www/hocvietcode.com -type f -exec chmod 644 {} \;
chmod 600 /var/www/hocvietcode.com/public_html/wp-config.php
chmod 700 /var/www/hocvietcode.com/tmp
chmod 700 /var/www/hocvietcode.com/tmp/sessions
chmod 700 /var/www/hocvietcode.com/logs
Mục tiêu là để website hoạt động bình thường nhưng không mở quyền quá rộng, tránh việc một tiến trình bị xâm nhập có thể sửa linh tinh sang nơi khác.
open_basedir có phải giải pháp chính không?
Nhiều người nghĩ chỉ cần bật open_basedir là đủ. Thực tế, đây chỉ nên xem là một lớp hỗ trợ thêm, không phải giải pháp chính.
open_basedir giúp giới hạn PHP chỉ được truy cập một số đường dẫn cho phép, nhưng nếu toàn bộ website vẫn chạy chung user hoặc chung pool thì nguy cơ lây chéo vẫn còn rất lớn.
Giải pháp cốt lõi vẫn là:
- tách user
- tách pool
- tách socket
- tách quyền thư mục
open_basedir chỉ nên dùng để siết thêm một lớp bảo vệ.
Một số tăng cường bảo mật nên áp dụng thêm
Ngoài việc cô lập từng website, có thể triển khai thêm các lớp bảo vệ sau:
Chặn thực thi PHP trong thư mục upload
Với WordPress, thư mục upload không nên được thực thi PHP:
location ~* ^/wp-content/uploads/.*\.php$ {
deny all;
}
Tắt chỉnh sửa file từ dashboard WordPress
Thêm vào wp-config.php:
define('DISALLOW_FILE_EDIT', true);
Nếu muốn chặn luôn việc cài/sửa plugin và theme trực tiếp từ admin:
define('DISALLOW_FILE_MODS', true);
Tách database user cho từng site
Ngay cả khi file đã được cô lập, việc dùng chung user database giữa nhiều site vẫn là rủi ro. Mỗi website nên có:
- database riêng
- database user riêng
- password riêng
Điều này giúp hạn chế mức độ thiệt hại khi có site bị lộ file cấu hình.
Cách kiểm tra nhanh server có đang “dễ lây chéo” hay không
Quản trị viên có thể kiểm tra sơ bộ bằng vài lệnh:
ps aux | grep php-fpm
ls -ld /var/www/*
find /var/www -maxdepth 2 -name wp-config.php -exec ls -l {} \;
Nếu thấy nhiều website cùng owner là www-data, hoặc nhiều site cùng chạy chung một pool mặc định, thì đó là dấu hiệu cần rà soát lại ngay.
Kết luận
Khi vận hành nhiều website trên cùng một server, bài toán bảo mật không chỉ là chống hack từng website riêng lẻ, mà còn là ngăn sự cố của một site lan sang các site khác.
Một mô hình triển khai an toàn hơn nên đảm bảo:
- mỗi website có user riêng
- mỗi website có pool PHP-FPM riêng
- mỗi website có socket riêng
- mỗi website có quyền thư mục riêng
- NGINX trỏ đúng vào socket của từng site
- bổ sung thêm
open_basedir,disable_symlinks, chặn thực thi PHP trong uploads và siết quyền database
Đây không phải là giải pháp “chống hack tuyệt đối”, nhưng là cách rất hiệu quả để giảm thiểu thiệt hại, khoanh vùng sự cố và tránh lây chéo toàn server khi một website gặp vấn đề bảo mật.



