cách cài LAMP stack trên Ubuntu 24.04 PHP-FPM
Hướng dẫn cài Apache, MariaDB, PHP 8.3 và Certbot trên Ubuntu 24.04. Cách cấu hình PHP-FPM, vhost và xử lý lỗi không chạy được source code trên VPS.
Những gì bạn sẽ xây dựng
Một LAMP stack gồm bốn thành phần chạy trên một server Ubuntu 24.04: Linux ở lớp dưới cùng, Apache xử lý HTTP, MariaDB lưu trữ dữ liệu, và PHP 8.3 chạy code. Khi kết thúc, bạn sẽ có một virtual host dựa trên tên miền phục vụ một thư mục ứng dụng thực tế, một database với user riêng biệt có quyền tối thiểu (least-privilege), PHP được kết nối với Apache thông qua PHP-FPM, và một certificate Let's Encrypt miễn phí.
Quá trình cài đặt chỉ gồm bốn lệnh apt. Hầu hết nội dung trong hướng dẫn này là về cách kết nối các thành phần, và các lỗi nhỏ khiến một stack mới cài xong chỉ hiện trang trắng, tải source code về máy thay vì chạy, hoặc từ chối quyền truy cập vào database vừa cài. Mỗi lỗi đó đều có dấu hiệu nhận biết riêng, và tôi sẽ liệt kê tên chính xác của chúng ở bên dưới.
Điều kiện tiên quyết và những lỗi thường gặp
Giả định bạn có một VPS KVM Ubuntu 24.04 mới với user sudo hoặc root, và một địa chỉ IPv4 công khai. Một stack tối thiểu chạy tốt với 1 GB RAM; hãy cấp cho nó 2 GB trước khi cài ứng dụng chạy database thực tế, vì các buffer mặc định của MariaDB cộng với một vài PHP-FPM workers sẽ ngốn hết 1 GB đầu tiên rất nhanh.
Có hai điều cần phải hoàn tất trước khi Certbot có thể hoạt động, hãy chuẩn bị chúng ngay: Bạn cần một domain name với bản ghi A trỏ về IP công khai của VPS — Let's Encrypt xác thực qua HTTP tới tên miền đó, và một địa chỉ IP trần không bao giờ lấy được certificate. Ngoài ra, các port 80 và 443 phải có thể truy cập được từ internet, điều này có nghĩa là bạn phải mở chúng trong network firewall trên control panel của nhà cung cấp cũng như trong ufw trên máy. Thay đổi DNS có thể mất tới một giờ để cập nhật, vì vậy hãy thiết lập bản ghi A trước, nó sẽ có hiệu lực ngay khi bạn cần.
Bước 1 - Cài đặt Apache và kiểm tra trang mặc định
sudo apt update
sudo apt install -y apache2apt sẽ khởi chạy và enable service cho bạn. Kiểm tra nó:
systemctl status apache2Bạn sẽ thấy dòng active (running). Bây giờ hãy mở http://YOUR_SERVER_IP/ trên trình duyệt. Kết quả đúng là trang Apache2 Ubuntu Default Page với banner "It works!" lớn — đây là bằng chứng Apache đang chạy, không phải lỗi. Trang đó nằm tại /var/www/html/index.html và được phục vụ bởi virtual host mặc định 000-default.conf. Bạn sẽ disable cả hai sau này; hiện tại, việc chúng đang chạy là đúng ý muốn.
Nếu trang không load được nhưng systemctl báo process đang chạy, thì firewall đang chặn. Đó là bước tiếp theo.
Bước 2 - Mở firewall cho HTTP và HTTPS
Package apache2 đăng ký ba profile ứng dụng ufw. Liệt kê chúng:
sudo ufw app listBạn sẽ thấy Apache, Apache Full, và Apache Secure. Apache chỉ dành cho port 80, Apache Secure chỉ dành cho port 443, và Apache Full là cả hai — đây là cái bạn cần, vì bạn sẽ thêm TLS ở bước cuối.
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableHãy allow OpenSSH trước khi chạy ufw enable. ufw mặc định là deny tất cả traffic đến, và nếu bạn enable nó mà không có rule SSH, bạn sẽ bị ngắt kết nối ngay lập tức — session hiện tại vẫn giữ được nhưng bạn không thể reconnect. Xác nhận bằng sudo ufw status; bạn cần OpenSSH, Apache Full, và các bản v6 tương ứng đều hiển thị ALLOW.
Bước 3 - Cài đặt MariaDB và bảo mật
sudo apt install -y mariadb-server
systemctl status mariadbUbuntu 24.04 đi kèm MariaDB 10.11, một bản LTS, nên bạn không cần repository bên ngoài. Khi service đã chạy, hãy harden nó:
sudo mysql_secure_installationBạn nên đọc kỹ các prompt thay vì chỉ nhấn Enter. Khi nó hỏi current root password, hãy nhấn Enter — vì hiện tại chưa có. Khi nó hỏi "Switch to unix_socket authentication?", câu trả lời không thay đổi gì vì nó đã được enable sẵn trong package này, nên hãy nhấn n. Chọn n cho mục "Change the root password?" vì lý do ở đoạn tiếp theo, sau đó trả lời Y cho các mục còn lại: xóa anonymous users, cấm root login từ xa, xóa test database, và reload privilege tables.
Đây là phần gây nhầm lẫn cho mọi người. Trên MariaDB của Ubuntu, tài khoản database root sử dụng xác thực unix_socket, không phải password. Điều này có nghĩa là database tin tưởng user hệ điều hành mà bạn đã authenticate. Vì vậy, lệnh này sẽ chạy được từ root shell:
sudo mysql...và nó sẽ đưa bạn về prompt MariaDB [(none)]> mà không hỏi password. Cùng một lệnh đó nếu chạy bằng user không có đặc quyền sẽ bị từ chối, đó chính là mục đích: quyền truy cập root database gắn liền với sudo trên máy, và không có password nào để bị đánh cắp hay brute-force. Cách này bảo mật hơn password, nên hãy để nguyên như vậy. Quy tắc rút ra là: không bao giờ trỏ ứng dụng vào tài khoản root. Hãy tạo user riêng cho mỗi ứng dụng (Bước 7), vì một app kết nối qua TCP bằng username/password không thể dùng socket auth, và bạn muốn mỗi app chỉ giới hạn trong database riêng của nó.
Bước 4 - Cài đặt PHP 8.3 với PHP-FPM
PHP mặc định của Ubuntu 24.04 là 8.3. Hãy cài đặt FPM process manager và các extension mà một app điển hình cần:
sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
php8.3-curl php8.3-xml php8.3-mbstring php8.3-zipLưu ý những gì không có trong danh sách đó: libapache2-mod-php. Package cũ đó nhúng một PHP interpreter vào mỗi Apache process. Nó đơn giản, nhưng mỗi worker sẽ mang theo một bản copy PHP dù đang phục vụ script hay một ảnh static, hai thứ này dùng chung lifecycle, và nó chỉ chạy được với Apache MPM prefork — loại kém hiệu quả nhất. Thay vào đó, PHP-FPM chạy PHP như một pool process riêng biệt mà Apache giao tiếp qua một socket. Apache có thể dùng threaded event MPM cho các file static và chỉ chuyển các request PHP sang, pool này được tune độc lập với web server, và cùng một setup FPM này sẽ hoạt động nếu sau này bạn dùng nginx làm proxy phía trước. Đây là default hiện tại vì lý do chính đáng.
Apache kết nối tới FPM thông qua module proxy_fcgi. Hãy enable nó, enable config mà package FPM đã cài vào, và restart:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2a2enconf php8.3-fpm kích hoạt /etc/apache2/conf-available/php8.3-fpm.conf, chứa rule điều hướng các file PHP tới FPM socket. Phần cốt lõi của nó khớp với bất kỳ file .php nào và chuyển nó tới socket tại /run/php/php8.3-fpm.sock:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>Bạn không cần sửa file đó; nó đã chuẩn sẵn. Nhưng biết đường dẫn socket là cách để bạn chẩn đoán lỗi "PHP tải về thay vì thực thi" và lỗi "Primary script unknown" sau này — cả hai đều do Apache và FPM không khớp nhau về socket này hoặc file đằng sau nó.
Bước 5 - Virtual host dựa trên tên miền cho app của bạn
Name-based virtual hosting cho phép một IP phục vụ nhiều site; Apache chọn site dựa trên header Host: trong request. Hãy tạo một thư mục cho app, nằm tách biệt khỏi /var/www/html mặc định:
sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testappQuyền sở hữu (ownership) rất quan trọng. Apache và PHP-FPM đều chạy dưới user www-data trên Ubuntu, nên các file mà web server cần đọc — và các thư mục mà app cần ghi, như folder uploads — nên thuộc sở hữu của www-data. Nếu bạn cũng muốn edit file bằng user đăng nhập của mình, một pattern phổ biến là sở hữu file đó và thêm user của bạn vào group www-data; với một bản deploy thuần túy, www-data:www-data là lựa chọn ít gây rắc rối nhất.
Tạo virtual host tại /etc/apache2/sites-available/testapp.conf:
<VirtualHost *:80>
ServerName app.example.com
DocumentRoot /var/www/testapp
<Directory /var/www/testapp>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>Đặt ServerName thành domain thật của bạn. Options -Indexes ngăn Apache liệt kê thư mục khi không có file index — nếu không, khách truy cập sẽ duyệt được toàn bộ cây thư mục source code của bạn. AllowOverride All cho phép file .htaccess hoạt động, thứ mà hầu hết các ứng dụng PHP đều cần để có pretty URLs; hãy bỏ nó đi để lấy None nếu app của bạn không cần. Enable site này, disable cái mặc định, kiểm tra config, và reload:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2apache2ctl configtest sẽ in ra Syntax OK. Dòng a2dissite 000-default là dòng mà mọi người hay quên, và đó là lý do tại sao trang mặc định sau này có vẻ bị kẹt — đã được đề cập trong phần lỗi.
Bước 6 - Kiểm tra PHP đã chạy chưa, sau đó xóa file test
Tạo một file PHP một dòng vào thư mục root của app:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phpTruy cập http://app.example.com/info.php. Kết quả đúng là bảng PHP Version 8.3.x màu tím và xám liệt kê các module đã load, với dòng Server API hiển thị FPM/FastCGI. Dòng cuối cùng đó xác nhận request đang đi qua PHP-FPM chứ không phải mod_php.
Bây giờ hãy xóa nó ngay lập tức:
sudo rm /var/www/testapp/info.phpphpinfo() tiết lộ chính xác phiên bản PHP, mọi extension đã load, đường dẫn file và chi tiết môi trường — một món quà cho bất kỳ ai đang dò tìm server để tìm các lỗ hổng phiên bản. Đây là bài test, không phải tính năng. Xóa nó ngay khi bạn đã thấy trang web. Nếu thay vì bảng, trình duyệt lại đề nghị tải về info.php, nghĩa là PHP chưa được kết nối với Apache; hãy chuyển sang phần lỗi trước khi làm bất cứ việc gì khác.
Bước 7 - Tạo database cho app và user có quyền tối thiểu
Mở database với quyền root socket-authenticated:
sudo mysqlSau đó tạo một database và một user chỉ có quyền trên đúng database đó:
CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;Có ba lựa chọn có chủ đích ở đây. utf8mb4 là UTF-8 4-byte thực sự — alias utf8 cũ sẽ âm thầm làm mất emoji và một số ký tự CJK, nên hãy luôn dùng utf8mb4. Quyền grant nằm trên appdb.*, không phải *.*: user này chỉ có thể chạm vào database của chính nó và không gì khác, nên một lỗ hổng SQL-injection trong app không thể đọc được bảng của các site khác. Và 'appuser'@'localhost' giới hạn tài khoản chỉ cho các kết nối từ chính máy đó.
Kiểm tra với user đó:
mysql -u appuser -p appdbNó sẽ hỏi password và đưa bạn vào prompt MariaDB [appdb]>. Lưu ý không có flag -h — hãy để trống và client sẽ kết nối qua Unix socket cục bộ, đây chính xác là cách MariaDB tính là localhost. Một lưu ý cần biết: với MySQL và MariaDB, localhost nghĩa là Unix socket và 127.0.0.1 nghĩa là kết nối TCP. Trên một bản MariaDB Ubuntu 24.04 chuẩn, server vẫn resolve một kết nối TCP từ 127.0.0.1 về localhost, nên cả hai đều khớp với account — nhưng trên các server đã enable skip-name-resolve (một tweak hiệu năng phổ biến và là chuẩn trong nhiều container images), hai thứ này được coi là hai host khác nhau, và một app gọi tới 127.0.0.1 sẽ bị từ chối với lỗi ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) ngay cả khi password đúng.
Vì vậy, hãy trỏ ứng dụng của bạn tới host localhost, user appuser, database appdb — đừng bao giờ trỏ tới root. PHP's mysqli và PDO đều chuyển sang Unix socket khi host là chuỗi ký tự localhost, khớp với account bạn vừa tạo. Nếu một framework bắt buộc dùng TCP host dạng số, hãy tạo user khớp với cách nó kết nối — 'appuser'@'127.0.0.1', hoặc @'%' (kèm với một rule firewall) nếu nó bắt buộc phải kết nối tới database từ một máy khác.
Bước 8 - Thêm HTTPS với Certbot
Phục vụ một form đăng nhập qua HTTP trần sẽ gửi password dưới dạng clear text, và mọi trình duyệt hiện đại đều đánh dấu trang là "Not secure". Certbot giải quyết việc đó chỉ với một lệnh. Cài đặt nó cùng plugin Apache:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot sử dụng hai plugin ở đây. apache authenticator chứng minh bạn kiểm soát domain bằng cách chạy một challenge file tạm thời qua Apache đang chạy, và apache installer sau đó sẽ rewrite virtual host để thêm block 443, trỏ nó tới certificate mới, và redirect toàn bộ traffic HTTP sang HTTPS theo mặc định — từ Certbot 2.0 không còn câu hỏi redirect nữa; hãy dùng --no-redirect nếu bạn muốn giữ phục vụ HTTP trần. Vì bạn đã đặt ServerName thật ở Bước 5, Certbot sẽ tự động nhận diện domain. Certificate có hạn 90 ngày và package sẽ cài một systemd timer để tự động renew; kiểm tra timer bằng sudo certbot renew --dry-run, nó sẽ kết thúc ở Congratulations, all simulated renewals succeeded.
Để xem hướng dẫn đầy đủ về challenge, renewal timer, và các yêu cầu DNS/firewall, hãy xem hướng dẫn đi kèm về issuing free Let's Encrypt TLS certificates with Certbot on Apache.
Backup, nâng cấp và hardening
Hãy backup hai thứ giữ trạng thái của bạn: database và web root. Một bản logical dump hàng đêm là cách đơn giản và đáng tin cậy nhất — dùng sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', sau đó copy ra khỏi máy. Việc bọc toàn bộ pipeline trong sudo sh -c là rất quan trọng: nếu không, shell sẽ chạy lệnh redirect > /root/... dưới quyền user của bạn và lỗi với Permission denied, vì chỉ có mysqldump mới kế thừa được sudo. --single-transaction giúp lấy snapshot nhất quán của các InnoDB tables mà không làm khóa chúng. Kết hợp nó với một bản tar của /var/www và /etc/apache2/sites-available, bạn có thể rebuild toàn bộ stack trên một VPS mới từ những file đó.
Nâng cấp là một việc sudo apt update && sudo apt upgrade bình thường. Thứ gây rắc rối là nâng cấp phiên bản PHP — khi một bản Ubuntu tương lai chuyển default sang PHP 8.4, apt có thể cài php8.4-fpm song song với 8.3, socket trở thành /run/php/php8.4-fpm.sock, và config Apache của bạn vẫn trỏ vào socket 8.3. Hãy enable conf mới (sudo a2enconf php8.4-fpm) và disable cái cũ, nếu không site của bạn sẽ trả về lỗi Primary script unknown sau một đợt nâng cấp thông thường. Vì các bản release PHP ra mắt nhanh hơn distro LTS, hãy kiểm tra release notes của PHP thay vì cố định một patch version.
Có hai bước hardening nên làm ngay ngày đầu tiên. Thứ nhất, hãy cài Fail2Ban watching SSH lên máy — một VPS công khai sẽ bị các bot login tự động tấn công trong vài phút, và một jail nhỏ sẽ biến hàng ngàn lần thử thành một vài lần trước khi ban. Thứ hai, nếu bạn muốn quản lý Apache virtual hosts, MariaDB databases và users qua trình duyệt thay vì sửa file bằng tay, the Webmin web-based control panel chạy trên chính stack này và điều khiển các config file tương tự như bạn vừa viết. Cả hai không thay thế việc hiểu rõ các thành phần, nhưng cả hai đều giúp giảm bớt các thao tác lặp lại hàng ngày.
Các lỗi thường gặp, kèm theo các chuỗi ký tự bạn sẽ thấy
Trang mặc định không biến mất. Bạn đã edit virtual host, đã reload, nhưng trình duyệt vẫn hiển thị "Apache2 Ubuntu Default Page" và banner "It works!". Apache phục vụ virtual host khớp đầu tiên, và khi không có ServerName nào khớp với request, config theo thứ tự bảng chữ cái sẽ thắng — 000-default.conf sắp xếp trước testapp.conf. Có thể hostname của request không khớp với ServerName của bạn, hoặc bạn chưa chạy sudo a2dissite 000-default. Hãy disable cái mặc định, sudo systemctl reload apache2, và xác nhận bằng apache2ctl -S, lệnh này sẽ in ra vhost map và cho biết config nào đang giữ cái mặc định. Hãy xóa cả cache trình duyệt; một mã 200 cũ từ trang cũ có thể vẫn còn lưu trong cache.
Một file .php bị tải về thay vì thực thi. Bạn mở info.php và trình duyệt tải về một file chứa source code <?php thô, hoặc hiển thị nó như text thuần thay vì chạy nó. Apache đang phục vụ file đó như một asset static vì PHP handler chưa được gắn vào — bạn đã bỏ qua sudo a2enmod proxy_fcgi, hoặc sudo a2enconf php8.3-fpm, hoặc chưa restart Apache sau đó. Hãy chạy cả ba (Bước 4) và reload. Xác nhận module đã được load bằng apache2ctl -M | grep fcgi, nó sẽ liệt kê proxy_fcgi_module. Đây là lỗi lộ source code, không phải lỗi hiển thị, nên hãy sửa nó trước khi đưa bất cứ thứ gì quan trọng lên server.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Bạn đã chạy mysql -u root hoặc mariadb -u root mà không có sudo. Tài khoản root sử dụng xác thực unix_socket, nên nó chỉ chấp nhận bạn khi user OS của bạn thực sự là root. Cách sửa là sudo mysql — không cần -u root, không cần password. Thông báo này là hành vi bình thường của socket auth khi hoạt động đúng, không phải lỗi cài đặt.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' từ ứng dụng, dù đã có đúng password. Tài khoản tồn tại dưới dạng 'appuser'@'localhost', nhưng app của bạn đang kết nối qua TCP tới 127.0.0.1 trên một server mà việc resolve hostname đã bị tắt (skip-name-resolve), nên MariaDB coi hai thứ này là hai host khác nhau — localhost là Unix socket, 127.0.0.1 là TCP. Hãy trỏ app tới host localhost để nó dùng socket và khớp với account, hoặc tạo một account thứ hai 'appuser'@'127.0.0.1' nếu framework chỉ hỗ trợ TCP.
AH01071: Got error 'Primary script unknown' trong /var/log/apache2/testapp-error.log, với trình duyệt hiển thị File not found.. Apache đã chuyển request tới PHP-FPM, nhưng FPM không tìm thấy script tại đường dẫn mà Apache cung cấp. Hai nguyên nhân phổ biến: socket FPM trong config của bạn trỏ tới một phiên bản PHP không được cài đặt (một socket php8.4 sau khi nâng cấp trong khi chỉ có 8.3 đang chạy), hoặc file thực sự không tồn tại vì DocumentRoot và thư mục thực tế không khớp nhau. Kiểm tra socket tồn tại bằng ls -l /run/php/, xác nhận DocumentRoot khớp với nơi file cư ngụ, và restart cả php8.3-fpm và apache2.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name sau mỗi lần restart. Đây là cảnh báo vô hại, không phải lỗi — Apache đang báo cho bạn biết không có ServerName global nào được thiết lập. Hãy tắt nó bằng cách viết ServerName your.domain vào /etc/apache2/conf-available/servername.conf và chạy sudo a2enconf servername.
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 khi Apache khởi động. Một web server khác đã chiếm port 80 — thường là một nginx chạy sót từ một thử nghiệm trước đó. Tìm nó bằng sudo ss -ltnp | grep :80, sau đó stop và disable service đó trước khi chạy Apache.
FAQ
mod_php hay PHP-FPM - nên dùng cái nào?
Hãy dùng PHP-FPM. mod_php nhúng một interpreter vào mỗi Apache process và bắt buộc dùng prefork MPM chậm chạp, khiến Apache chịu overhead của PHP ngay cả khi đang phục vụ một ảnh static. PHP-FPM chạy PHP như một pool riêng biệt, được tune độc lập mà Apache kết nối qua socket, hoạt động tốt với threaded event MPM nhanh hơn, và có thể chuyển sang nginx sau này mà không đổi cấu trúc. Đây là default hiện đại; mod_php chỉ có ý nghĩa với các app cũ phụ thuộc vào một số hành vi in-process.
Tại sao trình duyệt tải file PHP thay vì thực thi nó?
Apache đang xử lý file .php như một bản tải về static vì không có PHP handler nào được gắn vào nó. Trên Ubuntu 24.04 với FPM, điều đó có nghĩa là bạn đã thiếu một trong các bước sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm, hoặc chưa restart Apache. Hãy chạy cả ba và reload, sau đó xác nhận bằng apache2ctl -M | grep fcgi rằng proxy_fcgi_module đã được liệt kê. Cho đến khi bạn sửa xong, server đang bị lộ source code, hãy xử lý gấp.
Tại sao bị từ chối quyền root trong MariaDB dù đã có đúng password?
Vì không có password — MariaDB của Ubuntu xác thực tài khoản root bằng unix_socket, gắn nó với user root của hệ điều hành. mysql -u root từ một shell bình thường sẽ trả về ERROR 1698 (28000): Access denied for user 'root'@'localhost' theo thiết kế. Hãy kết nối bằng sudo mysql thay vào đó, và tạo một user xác thực bằng password riêng cho bất kỳ ứng dụng nào thay vì dùng lại root.
Làm thế nào để thêm HTTPS cho site LAMP của tôi?
Cài đặt certbot và python3-certbot-apache, trỏ bản ghi A của domain tới server, sau đó chạy sudo certbot --apache. Apache authenticator sẽ chứng minh quyền kiểm soát domain thông qua Apache đang chạy và installer sẽ rewrite virtual host cho port 443 và thiết lập tự động renew. full Certbot and Apache walk-through sẽ hướng dẫn chi tiết về challenge, renewal timer, và các lỗi thường gặp.