Security

Cài đặt WordPress trên VPS AlmaLinux với LEMP

VPS AlmaLinux mới cài xong nhưng bạn cần dựng một website WordPress chạy được thật, không phải bản demo. Stack LEMP (Linux, Nginx, MariaDB, PHP-FPM) là lựa chọn gọn nhẹ và ổn định cho việc này: Nginx đứng trước phục vụ static và reverse proxy, MariaDB lưu dữ liệu, PHP-FPM xử lý mã PHP. Bài này hướng dẫn cài WordPress trên VPS AlmaLinux 9 với LEMP, có SELinux, firewall và SSL đi kèm để site chạy đúng ngay từ đầu.

AlmaLinux 9 có hai điểm khác Ubuntu mà nhiều người vấp: mặc định dùng dnf thay apt và SELinux đang bật ở chế độ enforcing. Đây là lý do nhiều bài hướng dẫn kiểu Ubuntu áp vào là hỏng ngay ở bước ghi file. Mình sẽ đi theo đúng đường của AlmaLinux, không tắt SELinux mà cấu hình cho nó đúng context.

Yêu cầu trước khi bắt đầu

  • VPS AlmaLinux 9 (hoặc 9.x), tối thiểu 2 GB RAM nếu chạy 1 site WordPress nhỏ, 4 GB nếu có plugin nặng.
  • Quyền root hoặc user có sudo.
  • Tên miền đã trỏ bản ghi A về IPv4 của VPS (kiểm tra bằng dig +short yourdomain.com).
  • Đã mở port 80 và 443 ở firewall của nhà cung cấp (nếu có lớp firewall ngoài).
  • Biết sơ SSH, biết dùng dnf, đọc được file cấu hình Nginx.

Vì sao chọn LEMP cho WordPress trên AlmaLinux

LEMP nhanh hơn LAMP truyền thống ở tầng phục vụ static và xử lý đồng thời nhiều kết nối, do Nginx dùng mô hình event-driven thay vì process/thread trên mỗi kết nối như Apache prefork. Với WordPress, bạn không cần Apache: Nginx + PHP-FPM qua socket Unix là combo ổn định và ít tốn RAM, rất hợp cho VPS có IPv4 riêng tại Việt Nam khi phần lớn người dùng cuối ở trong nước.

AlmaLinux 9 đi kèm PHP 8.0 trong repo gốc. WordPress hiện chạy tốt trên PHP 8.0 nhưng mình khuyến nghị dùng PHP 8.3 từ repo Remi, vì nhiều plugin và theme mới đã bỏ hỗ trợ 8.0 và bản 8.3 nhanh hơn đáng kể. Nếu bạn dựng môi trường để lâu dài, hãy lên 8.3 luôn từ đầu.

Bước 1 - Cập nhật hệ thống và cài LEMP

Cập nhật package và cài Nginx, MariaDB server, PHP-FPM cùng các extension WordPress thực sự cần. Không cài tràn lan extension để giảm bề mặt tấn công.

sudo dnf update -y
sudo dnf install -y nginx mariadb-server php-fpm php-mysqlnd php-gd php-mbstring php-xml php-curl php-zip php-intl php-opcache

Bật và cho các service khởi động cùng hệ thống:

sudo systemctl enable --now nginx mariadb php-fpm
sudo systemctl status nginx --no-pager
sudo systemctl status mariadb --no-pager
sudo systemctl status php-fpm --no-pager

Ba dòng status phải trả về active (running). Nếu php-fpm báo failed, kiểm tra sudo journalctl -u php-fpm -n 50 để xem chi tiết lỗi (thường là do cấu hình pool xung đột sau khi cài repo Remi).

Bước 2 - Cấu hình firewall và SELinux

AlmaLinux 9 dùng firewalld. Mở HTTP/HTTPS nhưng giữ nguyên SSH:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

Với SELinux, các thư mục web phải mang đúng context để Nginx và PHP đọc được. Đặt context chung cho toàn bộ /var/www:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/wp-content/uploads(/.*)?"
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/wp-content/upgrade(/.*)?"
sudo restorecon -Rv /var/www/html

Nếu semanage báo không tìm thấy lệnh, cài thêm gói: sudo dnf install -y policycoreutils-python-utils. Bỏ qua bước này thì lúc upload ảnh qua trình soạn thảo, WordPress sẽ báo "không thể di chuyển tệp tin đã tải lên" dù phân quyền file đã đúng.

Nếu bước nào bị treo vì thiếu quyền, cho SELinux biết Nginx được phép kết nối ra ngoài (hữu ích khi WordPress gọi API bên ngoài hoặc cập nhật plugin):

sudo setsebool -P httpd_can_network_connect 1

Bước 3 - Tạo database và user cho WordPress

Chạy mysql_secure_installation một lần để thiết lập mật khẩu root, xóa user ẩn danh, tắt đăng nhập root từ xa:

sudo mysql_secure_installation

Tạo database riêng cho WordPress, đặt tên gợi nhớ và mật khẩu mạnh. Đừng dùng chung user cho nhiều site, tách ra để dễ quản và giới hạn thiệt hại khi một site bị chèn mã:

sudo mysql -u root -p
CREATE DATABASE wp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'MatKhauRatManh#2026';
GRANT ALL PRIVILEGES ON wp_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Kiểm tra user vừa tạo đăng nhập được:

mysql -u wp_user -p -e "SHOW DATABASES;"

Output phải liệt kê wp_dbinformation_schema. Nếu báo access denied, xem lại mật khẩu hoặc grant đã chạy chưa.

Bước 4 - Cấu hình Nginx và PHP-FPM

Tạo thư mục gốc và phân quyền cho user nginx (user này Nginx dùng để chạy trên AlmaLinux):

sudo mkdir -p /var/www/html
sudo chown -R nginx:nginx /var/www/html
sudo chmod -R 755 /var/www/html

Tạo virtual host cho domain của bạn tại /etc/nginx/conf.d/wordpress.conf. Thay yourdomain.com bằng tên miền thật:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/html;
    index index.php index.html;

    client_max_body_size 64M;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        expires 30d;
        access_log off;
    }

    location ~ /\.ht {
        deny all;
    }
}

Kiểm tra cấu hình trước khi reload. Đây là bước phải làm sau mọi lần sửa file Nginx:

sudo nginx -t
sudo systemctl reload nginx

Nếu nginx -t báo lỗi cú pháp, đọc đúng số dòng nó chỉ và sửa, đừng reload vội. Bước cuối, đảm bảo PHP-FPM listen đúng socket mà Nginx đang gọi:

sudo grep -r "listen" /etc/php-fpm.d/www.conf
ls -l /run/php-fpm/www.sock

Socket www.sock phải tồn tại và Nginx có quyền đọc. Nếu Nginx báo 502 Bad Gateway, gần như luôn là do đường dẫn socket trong fastcgi_pass không khớp với file thực tế.

Bước 5 - Tải WordPress và cấu hình wp-config.php

Tải WordPress mới nhất về và giải nén vào thư mục web:

cd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
sudo cp -r wordpress/* /var/www/html/
sudo chown -R nginx:nginx /var/www/html
sudo rm -rf /tmp/wordpress /tmp/latest.tar.gz

Tạo file cấu hình từ mẫu và sinh salt bảo mật bằng API chính thức của WordPress:

cd /var/www/html
sudo cp wp-config-sample.php wp-config.php
curl -s https://api.wordpress.org/secret-key/1.1/salt/ > /tmp/wp-salt.txt
cat /tmp/wp-salt.txt

Mở wp-config.php bằng editor bạn quen (mình dùng vi), điền thông tin database ở các hằng số DB_NAME, DB_USER, DB_PASSWORD đúng như bước 3. Sau đó thay toàn bộ 8 dòng salt mặc định bằng nội dung trong /tmp/wp-salt.txt. Cuối cùng, thêm vài hằng số hữu ích cho VPS AlmaLinux:

define('FS_METHOD', 'direct');
define('WP_MEMORY_LIMIT', '256M');
define('DISALLOW_FILE_EDIT', true);

FS_METHOD = 'direct' tránh WordPress hỏi thông tin FTP khi cập nhật plugin. DISALLOW_FILE_EDIT chặn sửa file theme/plugin từ admin, giảm rủi ro bị chèn shell nếu tài khoản admin bị lộ.

Sau khi lưu, phân quyền lại và xóa file salt tạm:

sudo chown nginx:nginx /var/www/html/wp-config.php
sudo chmod 640 /var/www/html/wp-config.php
rm /tmp/wp-salt.txt

Bước 6 - Gắn SSL Let's Encrypt và hoàn tất

Cài Certbot và cấp chứng chỉ cho domain. Certbot cần đọc được root web để xác thực qua HTTP-01 nên chạy bình thường trên AlmaLinux 9:

sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot sẽ tự sửa file Nginx thêm block HTTPS, bạn chọn redirect từ HTTP sang HTTPS khi được hỏi. Kiểm tra chứng chỉ cài được:

curl -I https://yourdomain.com
sudo certbot renew --dry-run

Response phải trả về HTTP/2 200 (hoặc HTTP/1.1 200) và header có Strict-Transport-Security nếu bạn bật. Lệnh renew --dry-run cho biết tự động gia hạn đang hoạt động, đây là bước nhiều người bỏ qua rồi sáu tháng sau phát hiện SSL hết hạn.

Mở trình duyệt vào https://yourdomain.com, chạy wizard WordPress: chọn ngôn ngữ, điền tên site, tài khoản admin mạnh (đừng dùng username admin), email quản trị. Xong bước này bạn đã có một site WordPress thật chạy trên LEMP, sau đó có thể upload theme, plugin và bắt đầu tuỳ biến.

Xử lý lỗi thường gặp

  • 502 Bad Gateway: PHP-FPM không chạy hoặc socket sai đường dẫn. Kiểm tra sudo systemctl status php-fpm và so lại fastcgi_pass với output sudo grep listen /etc/php-fpm.d/www.conf.
  • Không upload được file / không cài plugin: 90% là do SELinux context, chạy lại sudo restorecon -Rv /var/www/html và đảm bảo wp-content thuộc context httpd_sys_rw_content_t.
  • Site hiện 403 Forbidden: file index.php thiếu quyền đọc hoặc Nginx không có quyền đi vào thư mục con. Kiểm tra bằng sudo -u nginx head -c 100 /var/www/html/index.php, nếu báo permission denied thì xem lại chmod.
  • WordPress báo "Error establishing a database connection": sai user/password trong wp-config.php hoặc MariaDB chưa chạy. Dùng sudo journalctl -u mariadb -n 30 để xem log.

Câu hỏi thường gặp

Cần bao nhiêu RAM để chạy WordPress trên VPS AlmaLinux với LEMP?

Một site WordPress nhỏ chạy được với 1-2 GB RAM, nhưng mình khuyến nghị 2 GB là sàn thoải mái vì MariaDB chiếm khoảng 300-400 MB, PHP-FPM pool mặc định thêm 200-300 MB, còn lại cho Nginx và OS. Nếu có nhiều plugin nặng hoặc lượng truy cập tăng, nên lên 4 GB để tránh OOM khi cache và database cùng hoạt động.

AlmaLinux có cần tắt SELinux cho WordPress không?

Không nên tắt. Cách đúng là đặt context httpd_sys_rw_content_t cho hai thư mục uploadsupgrade như bước 2, sau đó chạy restorecon. Làm đúng thì SELinux bảo vệ bạn thêm một lớp mà không gây khó chịu cho WordPress. Tắt SELinux chỉ là cách chữa cháy, không phải cấu hình production.

Nên dùng Apache hay Nginx cho WordPress trên AlmaLinux?

Với VPS tài nguyên giới hạn, Nginx + PHP-FPM tiết kiệm RAM và chịu tải đồng thời tốt hơn Apache prefork. Chỉ chọn Apache khi bắt buộc dùng .htaccess hoặc có plugin phụ thuộc module Apache đặc thù. Còn lại LEMP là lựa chọn mặc định mình dùng cho hầu hết site WordPress trên AlmaLinux.

PHP phiên bản nào chạy WordPress trên AlmaLinux 9?

Repo gốc AlmaLinux 9 có PHP 8.0, chạy được nhưng nhiều plugin mới đã bỏ hỗ trợ. Bạn nên nâng lên PHP 8.3 qua repo Remi để dùng được các bản theme, plugin đang được cập nhật. Sau khi đổi version PHP, luôn chạy sudo systemctl restart php-fpm nginx và kiểm tra lại site, vì một số extension có thể cần cài thêm.

Làm sao để back up WordPress trên VPS AlmaLinux?

Gồm hai phần: database (mysqldump) và toàn bộ thư mục /var/www/html. Một lệnh dump cơ bản: mysqldump -u wp_user -p wp_db > /root/wp_db_$(date +%F).sql. Kết hợp với backup file qua tar hoặc rsync lên S3. Đừng chỉ dựa vào snapshot của nhà cung cấp làm phương án riêng biệt, vì snapshot không giúp bạn phục hồi từng bảng dữ liệu khi cần.

Website chạy WordPress trên VPS Việt Nam có cần CDN không?

Nếu người dùng chủ yếu ở Việt Nam và hạ tầng đặt trong nước, độ trễ đã thấp nên CDN không bắt buộc. CDN phát huy tác dụng rõ khi bạn có khách nước ngoài hoặc phục vụ nhiều file tĩnh nặng. Với site nội địa, tối ưu ở tầng Nginx (cache, gzip/brotli, expires) thường đã đủ và ít phức tạp hơn.

Bài viết liên quan

Lưu ý: Bài viết mang tính tham khảo, tổng hợp kiến thức chung. Mỗi hệ thống, hạ tầng và nhu cầu có đặc thù riêng, nên kiểm thử trong môi trường an toàn và tham vấn kỹ sư trước khi triển khai thực tế.