PHP-FPM là gì | Cài đặt và cấu hình PHP-FPM

Môi trường thử nghiệm Họ RHELAlmaLinux 9.8Apache 2.4 + PHP-FPM 8.3Tài khoản rootKiểm tra ngày 2026-10-26

PHP-FPM là trình quản lý tiến trình FastCGI, trong đó các tiến trình worker được khởi động sẵn sẽ nhận các yêu cầu PHP mà web server chuyển sang rồi xử lý. Trên máy chủ AlmaLinux 9 chạy Apache, bài này kiểm tra xem đã cài chưa và cách thiết lập pool cùng socket theo từng trang web, rồi đi tiếp đến kiểm tra cấu hình và kiểm tra trạng thái bằng kết quả thực tế.

Apache cũng có thể chạy PHP trực tiếp bằng mod_php, nhưng nếu giao cho PHP-FPM thì web server và PHP được tách riêng. Nginx không chạy PHP trực tiếp được nên chuyển yêu cầu sang một tiến trình FastCGI như PHP-FPM. Vì vậy một PHP-FPM có thể gắn với Apache hoặc Nginx đều được. Phần thiết lập pool và cấu trúc socket bên dưới áp dụng nguyên vẹn khi bạn kết nối PHP-FPM với Nginx.

1️⃣ PHP-FPM là gì: khác biệt giữa CGI và FastCGI

CGI (Common Gateway Interface) là cách chuẩn để web server chạy một chương trình bên ngoài nhằm tạo nội dung động, và mỗi khi có yêu cầu thì chương trình được khởi động lại từ đầu. Chi phí tạo và xóa tiến trình cho từng yêu cầu cứ dồn lại, nên truy cập càng tăng thì máy chủ phản hồi càng chậm.

FastCGI loại bỏ chi phí này bằng cách khởi động sẵn chương trình và xử lý các yêu cầu nối tiếp nhau. Đó là khác biệt giữa việc bật tắt máy tính mỗi lần dùng và việc dùng ngay một máy tính đã bật sẵn. PHP-FPM (FastCGI Process Manager) là trình quản lý chạy PHP qua FastCGI, và được tích hợp trong PHP từ phiên bản 5.3.3.

Với mỗi pool, tức một nhóm worker, PHP-FPM khởi động sẵn số worker đã định; một worker đang rảnh nhận yêu cầu mà web server chuyển qua socket, xử lý xong rồi chờ yêu cầu tiếp theo. Tệp tĩnh như hình ảnh và CSS do web server trả lời trực tiếp, chỉ tệp PHP mới được chuyển sang PHP-FPM. Điểm khác nhau chỉ là cách web server chuyển yêu cầu vào socket: Apache dùng mô-đun mod_proxy_fcgi, còn Nginx dùng thiết lập fastcgi_pass.

🧩 Cách CGI và PHP-FPM xử lý yêu cầu
Sơ đồ giải thích
⚙️ Chạy PHP
🌐 Web server
🌐 ApacheNginx
📨 Yêu cầu 1 · index.php📨 Yêu cầu 2 · wp-login.php📨 Yêu cầu 3 · index.php
Tệp tĩnh (hình ảnh, CSS) do web server trả lời trực tiếp
Chạy cho mỗi yêu cầuFastCGI · socket→
🧱 Tiến trình CGI
Yêu cầu 1Tiến trình mới → xử lý → kết thúc
Yêu cầu 2Tiến trình mới → xử lý → kết thúc
Yêu cầu 3Tiến trình mới → xử lý → kết thúc
Khi xử lý xong, tiến trình cũng biến mất · yêu cầu kế tiếp bắt đầu lại từ đầu
🧰 Pool PHP-FPM [siteuser]
Worker 1Yêu cầu 1
Worker 2Yêu cầu 2
Worker 3Yêu cầu 3
Worker 4chờ
Worker 5chờ
Worker được khởi động sẵn nhận yêu cầu, xong thì chờ yêu cầu tiếp theo
Khi Apache chạy PHP bằng CGI, mỗi yêu cầu lại khởi động một tiến trình PHP mới và kết thúc khi xử lý xong, nên yêu cầu càng tăng thì chi phí tạo tiến trình càng dồn lại.
Nginx không có mô-đun chạy trực tiếp chương trình CGI nên không dùng cách kết hợp này; PHP được chuyển sang PHP-FPM qua FastCGI.
Apache chuyển yêu cầu PHP vào socket bằng mod_proxy_fcgi, và một worker PHP-FPM được khởi động sẵn sẽ nhận, xử lý rồi chờ yêu cầu tiếp theo.
Nginx chuyển yêu cầu vào cùng một socket bằng fastcgi_pass, nên dù web server khác nhau thì pool PHP-FPM vẫn là một và giống nhau.

2️⃣ Cài đặt PHP-FPM: kiểm tra gói và cài bằng dnf

Nếu bạn cài PHP bằng dnf trên máy chủ họ RHEL thì gói php-fpm đã được cài cùng. Hãy kiểm tra bằng rpm xem đã cài chưa. Quy trình cài PHP giống như trong bài Cài đặt APM trên Rocky Linux.

rpm -qa | grep php-fpm
php-fpm-8.3.36-1.module_php.8.3.el9.remi.x86_64

Nếu kết quả trống thì chưa cài, hãy cài bằng dnf.

dnf -y install php-fpm

Cài xong, hãy chạy lại đúng lệnh rpm đó để kiểm tra tên gói có hiện ra không.

3️⃣ Thiết lập PHP-FPM: pool www.conf, socket, chế độ tĩnh và động

Có hai tệp cấu hình: cấu hình chung [/etc/php-fpm.conf] và cấu hình pool [/etc/php-fpm.d/www.conf]. Trong pool mặc định www.conf, các dòng cần xem trước là listen và pm, và giá trị ngay sau khi cài như sau.

grep -E '^(listen|pm) ' /etc/php-fpm.d/www.conf
listen = /run/php-fpm/www.sock
pm = dynamic

listen là nơi web server chuyển yêu cầu vào, và giá trị mặc định của bản 8.3 là Unix socket [/run/php-fpm/www.sock]. Giá trị mặc định của các bản cũ, [127.0.0.1:9000], nhận yêu cầu qua cổng TCP. Trong cùng một máy chủ, socket nhẹ hơn vì không phải qua bước kết nối cổng, và với listen.owner cùng listen.group bạn có thể giới hạn quyền truy cập như quyền tệp.

🔲 Thiết lập chế độ tĩnh và chế độ động

pm là cách khởi động worker, và được đặt là một trong ba giá trị: static, dynamic hoặc ondemand. Chế độ tĩnh luôn giữ chạy sẵn số worker ghi trong pm.max_children, và thiết lập bên dưới giữ 100 worker.

pm = static
pm.max_children = 100

Chế độ động là mặc định và tăng giảm số worker theo lượng yêu cầu. Thiết lập bên dưới khởi động 12 worker lúc đầu, giữ số worker rảnh từ 6 đến 18 và tăng tối đa đến 120.

pm = dynamic
pm.max_children = 120
pm.start_servers = 12
pm.min_spare_servers = 6
pm.max_spare_servers = 18

Chế độ ondemand chỉ khởi động worker khi có yêu cầu và kết thúc chúng sau khi hết pm.process_idle_timeout, nên hợp với trang web ít truy cập và không hợp với trang web nhiều truy cập.

Ba chế độ khác nhau ở thời điểm khởi động worker và cách tăng giảm số lượng.

Chế độCách khởi động workerTrang web phù hợp
static (tĩnh)Luôn giữ chạy sẵn số worker ghi trong pm.max_childrenTrang web nhiều truy cập
dynamic (động)Mặc định · tăng giảm trong khoảng pm.start_servers, pm.min_spare_servers và pm.max_spare_servers theo lượng yêu cầuTrang web có lượng truy cập lên xuống
ondemandChỉ khởi động khi có yêu cầu và kết thúc sau khi hết pm.process_idle_timeoutTrang web ít truy cập

max_children được định bằng cách chia bộ nhớ máy chủ cho bộ nhớ một worker dùng. Dù cảnh báo chạm [max_children] có dồn lại, cũng đừng nâng ngay mà trước hết dùng log để lọc ra những yêu cầu đã làm đầy worker.

Giới hạn bộ nhớ và tải lên được đặt trong php.ini, cấu hình chung của PHP, và được điều chỉnh cùng số worker cho phù hợp với trang web bạn vận hành.

memory_limit = 1024M
post_max_size = 128M
upload_max_filesize = 128M

Ba giá trị này là mức bộ nhớ tối đa một tập lệnh PHP được dùng và dung lượng tệp có thể tải lên một lần, và chỉ có hiệu lực sau khi khởi động lại PHP-FPM.

🔲 Thiết lập pool theo từng tài khoản trang web

Khi có nhiều trang web, đừng dùng chung một www.conf mà hãy tạo tệp pool cho từng tài khoản rồi tách tài khoản chạy và socket. PHP của một trang web không đọc được tệp của tài khoản khác, và số worker cũng được định theo từng trang web.

vi /etc/php-fpm.d/siteuser.conf

Trong tệp ghi tên pool, tài khoản chạy, đường dẫn socket và số worker.

[siteuser]
user = siteuser
group = nobody
listen = /run/php-fpm/siteuser.sock
listen.owner = siteuser
listen.group = nobody
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
chdir = /home/siteuser/www

listen.owner và listen.group là chủ sở hữu và nhóm của tệp socket, và PHP-FPM tạo socket bằng các giá trị này khi khởi động. Nếu web server chạy bằng tài khoản nobody thì ghi listen.group là nobody, còn nếu là tài khoản apache thì ghi apache, để web server mở được socket.

listen.owner = siteuser
listen.group = nobody
listen.mode = 0660

Ba dòng này có trong mọi tệp pool, và sau khi đổi giá trị phải khởi động lại để socket được tạo lại với quyền mới.

🔲 Thiết lập slowlog

slowlog là tệp ghi lại vị trí đang chạy của những yêu cầu mất nhiều thời gian hơn request_slowlog_timeout, và nếu giá trị là 0 thì không ghi. Khi tìm nguyên nhân của yêu cầu chậm, hãy bật bằng cách đặt thời gian theo giây như 3s.

slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 0

Tệp được ghi giữ lại đường dẫn tập lệnh của yêu cầu chậm và hàm đang chạy lúc đó, nhờ vậy bạn biết plugin hay đoạn mã nào đã làm mất thời gian.

4️⃣ Kết nối Apache: thiết lập VirtualHost và quyền của socket

Apache chuyển yêu cầu tệp PHP vào socket của pool bằng mô-đun mod_proxy_fcgi. Bạn đặt một dòng SetHandler trong FilesMatch bên trong VirtualHost, và với mỗi trang web ghi socket của tài khoản đó.

# Mỗi tài khoản trang web một VirtualHost · ghi socket của tài khoản đó vào SetHandler (có thể kết nối bằng ProxyPassMatch thay cho SetHandler)
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /home/siteuser/www

    <Directory /home/siteuser/www>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php-fpm/siteuser.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

DocumentRoot và Directory là thư mục gốc web của tài khoản đó, và -Indexes trong Options ngăn danh sách thư mục hiện ra trên trình duyệt.

🔲 Thiết lập quyền của socket

Khi pool khởi động, socket được tạo bằng các giá trị listen.owner, listen.group và listen.mode, và bạn kiểm tra bằng ls xem quyền đã đúng chưa.

ls -l /run/php-fpm/
tổng 4
srw-rw----  1 siteuser_a nobody 0 Th10 22 23:34 siteuser_a.sock
-rw-r--r--  1 root       root   7 Th10 26 03:00 php-fpm.pid
srw-rw----  1 siteuser_b nobody 0 Th10 22 23:34 siteuser_b.sock
srw-rw----+ 1 root       root   0 Th10 22 23:34 www.sock

Nếu quyền không khớp và web server không mở được socket thì khách truy cập nhận lỗi 503; để sửa ngay mà không khởi động lại, hãy chỉ định chủ sở hữu và quyền bằng chown và chmod.

Đây là cú pháp hiện tại, phân tách người dùng và nhóm bằng dấu hai chấm.

chown siteuser:nobody /run/php-fpm/siteuser.sock

Sau khi đổi chủ sở hữu, hãy đặt quyền là 660.

chmod 660 /run/php-fpm/siteuser.sock

Đây là cú pháp cũ, phân tách người dùng và nhóm bằng dấu chấm, và chown trên AlmaLinux 9 vẫn chạy được nguyên như vậy.

chown siteuser.nobody /run/php-fpm/siteuser.sock

Sau khi đổi chủ sở hữu, hãy đặt quyền là 660.

chmod 660 /run/php-fpm/siteuser.sock

Kết quả của hai cú pháp là như nhau; dùng cách nào thì chủ sở hữu của socket cũng thành siteuser và nhóm thành nobody.

🔲 Kết nối Nginx: thiết lập fastcgi_pass

Trong Nginx, location dành cho tệp PHP bên trong khối server chuyển yêu cầu vào socket của cùng pool bằng fastcgi_pass.

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

Nếu Nginx chạy bằng tài khoản nginx thì phải ghi listen.group trong tệp pool là nginx thì mới mở được socket.

5️⃣ Kiểm tra cấu hình và khởi động lại: php-fpm -t và systemctl

Sau khi đổi cấu hình, đừng khởi động lại ngay mà hãy kiểm tra cú pháp bằng php-fpm -t trước. Nếu tệp pool có lỗi gõ, khi khởi động lại toàn bộ PHP-FPM sẽ sập và mọi trang web đều nhận lỗi 503.

php-fpm -t
[26-Oct-2026 17:33:45] NOTICE: configuration file /etc/php-fpm.conf test is successful

Nếu kiểm tra đạt, thông báo [test is successful] hiện ra cùng đường dẫn tệp cấu hình, rồi bạn áp dụng thay đổi vào PHP-FPM. Thay đổi cần tạo lại socket, như đường dẫn socket hay listen.owner, được áp dụng bằng restart. Nếu trên máy chủ đang vận hành bạn chỉ đổi những giá trị như số worker thì áp dụng bằng reload.

systemctl restart php-fpm

Nếu bạn đã sửa VirtualHost thì khởi động lại cả Apache.

systemctl restart httpd

Đăng ký bằng enable để dịch vụ tự chạy khi máy chủ bật lại.

systemctl enable php-fpm

Kiểm tra đã đăng ký chưa bằng is-enabled.

systemctl is-enabled php-fpm
enabled

Dùng status để kiểm tra worker có đang chạy ở từng pool không.

systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
     Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled; preset: disabled)
     Active: active (running) since x
    Process: 3934033 ExecReload=/bin/kill -USR2 $MAINPID (code=exited, status=0/SUCCESS)
   Main PID: 1333314 (php-fpm)
     Status: "Processes active: 4, idle: 21, Requests: x, slow: 0, Traffic: x"
      Tasks: x
     Memory: x
        CPU: x
     CGroup: /system.slice/php-fpm.service
             ├─  41920 "php-fpm: pool siteuser_a"
             ├─  94143 "php-fpm: pool siteuser_b"
             ├─1333314 "php-fpm: master process (/etc/php-fpm.conf)"
             ├─3934078 "php-fpm: pool www"
             └─4009016 "php-fpm: pool siteuser_a"

Th10 26 03:00:01 server.example.com systemd[1]: Reloading The PHP FastCGI Process Manager...
Th10 26 03:00:01 server.example.com systemd[1]: Reloaded The PHP FastCGI Process Manager.

Ở dòng Status, active và idle là các worker đang xử lý yêu cầu và các worker đang rảnh, còn CGroup liệt kê worker theo tên pool. Tên máy chủ và tên pool trong kết quả đã được thay bằng giá trị ví dụ.

🔢 FAQ & nội dung đề xuất

mod_php chạy PHP bên trong tiến trình Apache nên thiết lập đơn giản, nhưng PHP được nạp trong từng worker của Apache, tốn nhiều bộ nhớ và khó tách tài khoản chạy theo từng trang web. PHP-FPM tách PHP thành tiến trình riêng nên mỗi trang web có thể có tài khoản và số worker riêng; vì vậy máy chủ có nhiều trang web hoặc dùng Nginx thì dùng PHP-FPM.

systemctl reload php-fpm gửi tín hiệu USR2 đến tiến trình chính để đọc lại thiết lập và thay worker. Nếu đặt process_control_timeout thì các worker đã xử lý xong yêu cầu đang chạy được thay trước (theo tài liệu chính thức). restart hạ toàn bộ worker xuống rồi bật lại cùng lúc nên yêu cầu ở thời điểm đó có thể bị ngắt; trên máy chủ đang vận hành, hãy dùng reload trước.

Log mặc định của họ RHEL là [/var/log/php-fpm/error.log], và các cảnh báo thiếu worker (seems busy · max_children) cùng bản ghi khởi động và dừng pool nằm ở đó. Tệp bạn chỉ định cho từng pool bằng php_admin_value[error_log] ghi riêng các lỗi của mã PHP trang web đó. Pool mặc định ghi vào [/var/log/php-fpm/www-error.log].

ℹ️ Thông báo tiếp thị liên kết
Nội dung trên trang này có chứa liên kết affiliate (tiếp thị liên kết). Khi khách truy cập mua sản phẩm hoặc dịch vụ qua các liên kết này, trang sẽ nhận hoa hồng từ bên bán. Trong quá trình đó, số tiền người mua phải trả (sẽ giảm khi có chương trình khuyến mãi ↓) không hề tăng lên. Giá, khuyến mãi và tình trạng hàng được đăng theo thời điểm viết bài và có thể khác với thực tế, vì vậy hãy kiểm tra lại lần cuối với bên bán trước khi mua. Việc chọn và đánh giá sản phẩm dựa trên tiêu chí riêng của trang; việc có nhận hoa hồng hay không không ảnh hưởng đến thứ tự giới thiệu hay nội dung đánh giá.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Prove your humanity: 6   +   7   =