DevOps

Cài PHP-FPM và nối Nginx với PHP bằng FastCGI 2026

cài PHP-FPM và nối Nginx với PHP bằng FastCGI

Nếu “Cài Nginx trên Ubuntu và hiểu cấu trúc config đúng chuẩn 2026” giúp bạn dựng Nginx sạch và “Server blocks Nginx: host nhiều domain, routing và static tối ưu 2026” giúp bạn host nhiều domain bằng server blocks, thì bài “Cài PHP-FPM và nối Nginx với PHP bằng FastCGI” là bước biến web server đó thành một stack chạy được ứng dụng PHP thật.

Về mặt kiến trúc, Nginx không xử lý PHP theo kiểu nhúng module như Apache mod_php; thay vào đó, Nginx chuyển request PHP sang một FastCGI server, và với PHP hiện đại, FastCGI Process Manager tức PHP-FPM là lựa chọn tiêu chuẩn. Nginx tài liệu hóa rõ mô hình này trong phần FastCGI proxying, còn PHP manual mô tả FPM là lớp quản lý tiến trình dành cho FastCGI.

Điểm khiến bài này quan trọng không nằm ở vài lệnh cài package, mà nằm ở chỗ bạn hiểu đúng luồng xử lý request. Một request .php phải được match đúng location, map đúng SCRIPT_FILENAME, chuyển đúng tới socket hoặc TCP listener của PHP-FPM, rồi trả response ngược lại cho Nginx. Chỉ cần sai một mắt xích, bạn có thể gặp 502, 404 lạ, hoặc tệ hơn là tải thẳng file .php thay vì thực thi nó. Nginx docs và phần try_files trong core module đều mô tả rất rõ điểm này.

1. Hiểu đúng về PHP-FPM và FastCGI trước khi cấu hình cài PHP-FPM và nối Nginx với PHP bằng FastCGI

1.1. PHP-FPM là gì và vì sao phù hợp với Nginx

PHP-FPM là FastCGI Process Manager của PHP. PHP manual mô tả FPM như lớp cấu hình và quản lý tiến trình cho PHP chạy kiểu FastCGI, với file cấu hình tổng và các pool riêng để điều khiển cách lắng nghe request, user/group chạy tiến trình và process manager mode. Điều này khiến FPM rất hợp với Nginx, vì Nginx vốn được thiết kế để chuyển request động sang upstream kiểu FastCGI thay vì tự thực thi mã PHP.

Thực chiến mà nói, tách Nginx và PHP-FPM ra như vậy có ba lợi ích rất rõ. Một là trách nhiệm của từng service sạch hơn: Nginx lo HTTP, TLS, static và routing; PHP-FPM lo thực thi mã PHP. Hai là bạn dễ theo dõi log và debug hơn khi có 502 hoặc timeout. Ba là sau này muốn tune hiệu năng, bạn có thể tối ưu pool của PHP-FPM mà không cần chạm quá sâu vào logic phục vụ file tĩnh của Nginx.

1.2. FastCGI khác gì reverse proxy HTTP thông thường

Khi nối Nginx với Node.js hoặc một API backend, bạn thường dùng proxy_pass. Nhưng với PHP-FPM, tài liệu Nginx nói rõ rằng cấu hình cơ bản phải dùng fastcgi_pass cùng các fastcgi_param phù hợp. Nói ngắn gọn, đây không phải là HTTP-to-HTTP proxy như reverse proxy thông thường, mà là Nginx truyền request sang một FastCGI server theo giao thức khác.

Đó là lý do bạn không nên copy nhầm một block proxy_pass sang PHP và hy vọng mọi thứ chạy đúng. Với PHP, phần quan trọng nhất là location .php, fastcgi_pass, và tham số để PHP-FPM biết chính xác file script nào cần thực thi. Chính tài liệu “How nginx processes a request” của Nginx dùng ví dụ /index.php để giải thích việc SCRIPT_FILENAME được tạo ra từ root$fastcgi_script_name.

2. Chuẩn bị môi trường trước khi cài PHP-FPM

2.1. Bạn nên có gì trước khi bắt đầu

Trước bài này, bạn nên có một VPS Ubuntu 24.04 LTS đã cài Nginx, xác nhận nginx -t chạy ổn, và ít nhất một server block hoạt động sạch theo mô hình sites-availablesites-enabled. Ubuntu Server docs hiện vẫn dùng đúng layout này cho Nginx, và đó là nền tốt nhất để bạn nối tiếp sang PHP-FPM mà không phải dồn mọi thứ vào nginx.conf.

Bạn cũng nên có webroot riêng cho site của mình, ví dụ /var/www/example.com/public hoặc /srv/example-site/html. Ubuntu docs minh họa cả hai kiểu tiếp cận, nhưng điểm quan trọng hơn tên đường dẫn là: webroot phải rõ ràng, đúng với root trong server block, và quyền truy cập phải hợp lý để Nginx cùng PHP-FPM đọc được source code.

2.2. Hai tài liệu ngoài nên tham chiếu khi làm bài này

Nếu muốn vừa làm vừa đối chiếu tài liệu gốc, hai tài liệu đáng mở song song nhất là phần FastCGI proxying trong Beginner’s Guide của Nginx và manual cấu hình PHP-FPM của PHP. Một nguồn giúp bạn hiểu Nginx cần gì để chuyển request PHP, nguồn còn lại giúp bạn hiểu FPM lắng nghe ở đâu, quyền socket ra sao và pool hoạt động thế nào.

3. Cài PHP-FPM trên Ubuntu theo hướng tối thiểu nhưng hữu ích

3.1. Cài gói nền

Trên Ubuntu, PHP được cài qua APT, còn Nginx là web server bạn đã dựng từ bài trước. Với mô hình Nginx + PHP, gói nền bạn cần là PHP-FPM; ngoài ra, các extension cụ thể sẽ phụ thuộc vào app của bạn. Với đa số website PHP phổ thông, có thể bắt đầu từ bộ tối thiểu như sau:

sudo apt update
sudo apt install php-fpm php-cli php-mysql php-curl php-xml php-mbstring php-zip

Ubuntu docs xác nhận PHP trên Ubuntu được cài bằng APT, còn PHP manual xác nhận FPM là thành phần dành cho mô hình FastCGI. Bộ extension ở trên là lựa chọn khởi đầu thực tế, không phải danh sách bắt buộc cho mọi app.

3.2. Kiểm tra service và socket thực tế

Sau khi cài xong, thay vì đoán version hoặc đoán socket path, bạn nên kiểm tra trực tiếp:

php -v
systemctl status php*-fpm --no-pager
ls -la /run/php/

Cách làm này giúp bạn tránh lỗi rất phổ biến: cấu hình Nginx trỏ tới sai socket, nhất là khi server có nhiều version PHP hoặc vừa nâng cấp package. Trong PHP-FPM, mỗi pool phải có một listen address bắt buộc, và listen có thể là TCP port hoặc Unix socket path. PHP manual mô tả rõ điều đó.

3.3. Vì sao nên ưu tiên Unix socket trong mô hình VPS đơn

PHP-FPM cho phép lắng nghe bằng ip:port hoặc bằng Unix socket path. Với một VPS đơn, Nginx và PHP-FPM thường cùng nằm trên một máy, nên socket thường là lựa chọn gọn hơn vì không mở thêm TCP port nội bộ không cần thiết. PHP manual ghi rõ listen của pool có thể là cả hai kiểu này, và nếu dùng Unix socket thì quyền truy cập socket phải được cấu hình phù hợp.

4. Hiểu nhanh cấu trúc PHP-FPM để không sửa nhầm chỗ

4.1. File tổng và pool config

PHP manual mô tả FPM dùng cú pháp kiểu php.ini cho php-fpm.conf và các pool configuration files. Nói gọn hơn, bạn sẽ có một file tổng cho FPM và một hoặc nhiều pool, mỗi pool có cấu hình listen, user, group, pm, pm.max_children và các tham số liên quan. Đây là điểm rất quan trọng nếu sau này bạn muốn tách pool theo site hoặc theo ứng dụng.

Ở giai đoạn B3, bạn chưa cần tune pool quá sâu. Điều cần nhớ là Nginx đang nối tới “điểm lắng nghe” của đúng pool đó. Nếu pool đổi socket mà Nginx vẫn giữ path cũ, site sẽ trả 502 ngay cả khi PHP-FPM vẫn đang chạy.

4.2. Những directive bạn nên biết sớm

Trong phần pool directives, PHP manual nhấn mạnh vài dòng cực đáng nhớ. listen xác định địa chỉ nhận request FastCGI. listen.owner, listen.group, listen.mode quyết định quyền truy cập nếu bạn dùng Unix socket. usergroup quyết định tiến trình worker chạy bằng tài khoản nào. pm quyết định kiểu quản lý process là static, dynamic hay ondemand. pm.max_children đặt trần số request đồng thời mà pool có thể phục vụ.

Với người mới, chỉ cần hiểu đúng listen, user/grouppm đã đủ tránh một nửa lỗi phổ biến. Tối ưu sâu hơn hãy để sau khi site thật chạy và bạn đã có số đo từ log hoặc monitoring.

5. Nối Nginx với PHP-FPM bằng FastCGI đúng cách

5.1. Tạo snippet PHP dùng lại được

Một cách sạch hơn là tách phần cấu hình PHP ra thành snippet để nhiều site tái sử dụng. Ví dụ:

sudo nano /etc/nginx/snippets/php.conf
location ~ \.php$ {
try_files $uri =404;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/phpX.Y-fpm.sock;
}

Trong snippet này, try_files $uri =404; rất đáng giá. Nginx core module giải thích rõ rằng try_files kiểm tra sự tồn tại của file PHP trước khi chuyển request sang FastCGI. Điều này giúp bạn tránh việc request tới một script không tồn tại nhưng vẫn bị đẩy sang PHP-FPM một cách mù quáng.

Ở dòng fastcgi_pass, bạn phải thay phpX.Y-fpm.sock bằng socket thật có trong /run/php/. Đây là lý do bước kiểm tra socket thực tế ở trên quan trọng hơn việc học thuộc version.

5.2. Gắn snippet vào server block của site

Giả sử bạn đã có file site ở /etc/nginx/sites-available/example.com, block tối thiểu có thể trông như sau:

server {
listen 80;
listen [::]:80;
server_name example.com www.example.com; root /var/www/example.com/public;
index index.php index.html index.htm; access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log warn; location / {
try_files $uri $uri/ /index.php?$query_string;
} include snippets/php.conf; location ~ /\. {
deny all;
}
}

Ở đây có hai điểm cực đáng nhớ. Một là index phải có index.php nếu app của bạn vào bằng file đó. Hai là location / với try_files cho phép request đẹp như /bai-viet/abc rơi về index.php khi app dùng front controller. Nginx docs dùng nhiều ví dụ để minh họa việc location / và location ~ \.php$ phối hợp với nhau như thế nào trong request processing.

5.3. Vì sao không nên để Nginx “download file .php”

Lỗi nguy hiểm nhất khi nối sai FastCGI là trình duyệt tải về file .php như một file text thay vì server thực thi nó. Điều đó thường xảy ra khi request .php không match đúng location FastCGI hoặc server block không có cấu hình PHP phù hợp. Về bản chất, nếu request không được đưa sang FastCGI, Nginx sẽ coi đó như file tĩnh và xử lý theo logic bình thường của nó. Luồng match location trong tài liệu request processing của Nginx giúp bạn hiểu chính xác chuyện này.

6. Kiểm tra sau khi cấu hình để tránh tự tạo lỗi khó debug

6.1. Test cú pháp rồi mới reload

Đây là quy trình nên giữ suốt series:

sudo nginx -t
sudo systemctl reload nginx

Ubuntu docs nhấn mạnh việc reload Nginx sau thay đổi cấu hình, còn Beginner’s Guide của Nginx mô tả rõ cách áp dụng cấu hình mới bằng reload signal. Với Nginx, thói quen “test rồi mới reload” luôn tốt hơn “sửa xong restart đại”.

6.2. Tạo file test tối thiểu rồi xóa ngay sau khi xác nhận

Để xác nhận Nginx đã nói chuyện đúng với PHP-FPM, bạn có thể tạo file test:

printf '%s\n' '<?php phpinfo();' | sudo tee /var/www/example.com/public/info.php
curl -I http://example.com/info.php

Nếu vào trình duyệt và thấy trang thông tin PHP hiện ra, nghĩa là đường đi cơ bản đã đúng: request .php đi vào đúng location, Nginx chuyển sang FastCGI đúng socket, và PHP-FPM thực thi được file. Nhưng bạn không nên giữ phpinfo() trên production lâu hơn mức cần thiết, vì trang đó lộ khá nhiều thông tin môi trường.

6.3. Xem đúng log khi có lỗi

Khi kiểm tra, hãy xem cả log Nginx lẫn log service PHP-FPM:

sudo tail -n 80 /var/log/nginx/example.com.error.log
sudo journalctl -u php*-fpm --since "1 hour ago"

Nếu Nginx báo connect() to unix:/run/php/... failed, lỗi gần như chắc nằm ở socket path, service PHP-FPM chưa chạy hoặc quyền socket không cho Nginx truy cập. Nếu log của PHP-FPM báo lỗi script hoặc permission, hướng xử lý sẽ khác. Tách log như vậy giúp bạn phân biệt lỗi routing ở Nginx với lỗi thực thi ở PHP.

7. Lỗi thường gặp khi nối Nginx với PHP bằng FastCGI

7.1. Sai socket hoặc sai version PHP-FPM

Đây là lỗi đứng đầu danh sách. Bạn copy một config mẫu dùng php8.2-fpm.sock, nhưng server thật lại đang chạy socket khác. Kết quả là Nginx reload xong vẫn trả 502. Cách tránh tốt nhất là luôn kiểm tra /run/php/ trước khi điền fastcgi_pass.

7.2. Không có try_files trong location PHP

Nginx docs giải thích rất rõ rằng try_files có thể kiểm tra file PHP có tồn tại trước khi chuyển request tới FastCGI. Nếu bỏ bước này, request tới script sai tên vẫn có thể bị đẩy sang backend và tạo ra những lỗi khó hiểu hơn mức cần thiết.

7.3. SCRIPT_FILENAME không khớp với root

Theo tài liệu request processing của Nginx, SCRIPT_FILENAME thường được tạo từ root$fastcgi_script_name. Nếu root của server block sai, hoặc bạn include snippet nhưng lại không tương thích với layout site, PHP-FPM có thể không tìm thấy file cần chạy dù file đó thật ra đang có trên máy.

7.4. Quyền file hoặc quyền socket không hợp lý

PHP manual ghi rõ nếu dùng Unix socket thì quyền đọc/ghi phải được set để web server truy cập được. Ngoài socket, source code của app cũng cần quyền đủ để Nginx và PHP-FPM đọc được. Nhiều lỗi “No input file specified” hoặc “Permission denied” không phải do FastCGI sai, mà do quyền file sai.

8. Checklist thực hành

8.1. Trước khi bật site PHP

Bạn đã cài php-fpm và xác nhận service đang chạy.

Bạn đã xác định đúng socket thật trong /run/php/.

Bạn đã có server block với root rõ ràng.

Bạn đã thêm location PHP bằng fastcgi_pass.

8.2. Trước khi reload

Bạn đã chạy sudo nginx -t.

Bạn đã kiểm tra snippet hoặc block site không bị sai dấu ngoặc.

Bạn đã chắc indexindex.php nếu app dùng entry point này.

8.3. Sau khi reload

Bạn đã test bằng phpinfo() hoặc một file PHP cực nhỏ.

Bạn đã xem error log của site.

Bạn đã xem journal của PHP-FPM ít nhất một lần.

Bạn đã xóa file test sau khi xác nhận thành công.

9. FAQ

9.1. Nên dùng Unix socket hay TCP port cho PHP-FPM

Với một VPS đơn, Unix socket thường gọn hơn và ít mở thêm bề mặt kết nối không cần thiết. Nếu Nginx và PHP-FPM ở hai máy khác nhau hoặc bạn có lý do riêng về network architecture, TCP port có thể phù hợp hơn. PHP manual xác nhận cả hai đều là giá trị hợp lệ cho listen.

9.2. Có cần tạo pool riêng cho từng site ngay từ đầu không

Không bắt buộc. Với site nhỏ hoặc môi trường mới dựng, một pool mặc định thường đã đủ. Nhưng khi bạn host nhiều app, cần tách quyền hoặc cần tune hiệu năng riêng, pool riêng sẽ sạch hơn nhiều. PHP manual cũng mô tả FPM hỗ trợ nhiều pool với setting khác nhau.

9.3. Vì sao request / vào được nhưng /index.php lại lỗi

Đây thường là dấu hiệu location / và location ~ \.php$ đang không phối hợp đúng, hoặc SCRIPT_FILENAME bị map sai. Tài liệu request processing của Nginx mô tả rõ việc / có thể nội bộ redirect sang /index.php, và lúc đó location PHP phải xử lý chính xác request mới.

9.4. Sau bài này nên đi đâu tiếp

Lộ trình hợp lý là sang bài cài database rồi hoàn thiện stack LEMP. Khi Nginx đã nối đúng với PHP-FPM, bước tiếp theo là làm cho app PHP có database, rồi bật HTTPS và bắt đầu nói chuyện về deploy, backup, log và tuning.

10. Kết luận

Cài PHP-FPM và nối Nginx với PHP bằng FastCGI là bước chuyển từ “server phục vụ file tĩnh” sang “server chạy được ứng dụng web động”. Nếu làm đúng ở đây, bạn sẽ tránh được gần hết những lỗi nhập môn khó chịu nhất như 502, 404 kỳ lạ, hoặc tải nhầm source .php.

Điểm quan trọng nhất không phải là nhớ một đoạn config thật dài, mà là hiểu ba mắt xích: Nginx phải match đúng location, FastCGI phải trỏ đúng socket hoặc listener của PHP-FPM, và PHP-FPM phải nhận được đúng đường dẫn script cần thực thi. Khi ba điểm này rõ ràng, các bước tiếp theo của series sẽ nhẹ hơn rất nhiều.

Đừng bỏ lỡ

Theo dõi bản tin mới nhất tại đây

Chào bạn 👋
Rất vui được làm quen.

Đăng ký để nhận nội dung hấp dẫn mỗi tháng qua email.

Bằng việc đăng ký, bạn đồng ý với Chính sách bảo mật của DevNook.

Tác giả

Tuấn Lê

Tại DevNook, mình chia sẻ kiến thức thực chiến, kinh nghiệm làm sản phẩm và những công cụ hữu ích giúp bạn làm việc hiệu quả hơn mỗi ngày.