How to Configure PHP-FPM with Nginx on RHEL 7

PHP-FPM (FastCGI Process Manager) is the recommended way to run PHP alongside Nginx. Unlike Apache’s mod_php which embeds PHP directly into the web server process, PHP-FPM runs as a completely separate daemon. Nginx passes PHP requests to PHP-FPM over a Unix socket or TCP connection, and PHP-FPM manages a pool of worker processes to handle them. This separation has significant advantages: PHP-FPM can run under a different user than Nginx for improved security isolation, worker processes can be recycled to prevent memory leaks, and the pool configuration gives you fine-grained control over concurrency and resource usage. This guide covers the complete configuration of PHP-FPM and Nginx on RHEL 7, from pool setup and socket configuration through process management strategies, the PHP-FPM status page, and slow log debugging.

Prerequisites

  • RHEL 7 server with root or sudo access
  • PHP 7.4 installed from the Remi repository including the php-fpm package
  • Nginx installed and running
  • Basic familiarity with systemctl and vi or another text editor

Step 1: Verify PHP-FPM is Installed

Confirm the php-fpm package is installed:

rpm -q php-fpm

If not installed, add it:

sudo yum install -y php-fpm

Check the version and confirm it matches your installed PHP:

php-fpm -v

Step 2: Understand PHP-FPM Configuration Files

PHP-FPM’s configuration is split across two locations:

  • /etc/php-fpm.conf — Global PHP-FPM settings (log file, pid file, emergency restart thresholds)
  • /etc/php-fpm.d/*.conf — Pool definition files, one per pool; the default pool is www.conf

View the default pool file:

sudo cat /etc/php-fpm.d/www.conf

The pool file controls everything about how PHP-FPM handles requests for that pool: which user runs the workers, where to listen, how many processes to maintain, and what resource limits to apply.

Step 3: Configure the PHP-FPM Pool

Open the default pool configuration for editing:

sudo vi /etc/php-fpm.d/www.conf

User and Group

PHP-FPM worker processes should run as the same user as Nginx to allow the socket to be accessed. On RHEL 7 with Nginx from the base repository, this is the nginx user:

user = nginx
group = nginx

Listen — Unix Socket vs TCP

PHP-FPM can accept connections over a Unix domain socket (recommended when Nginx and PHP-FPM are on the same server) or a TCP socket (required when they are on different servers):

; Unix socket — faster, recommended for same-server setups
listen = /run/php-fpm/www.sock

; TCP — use when Nginx is on a separate host
; listen = 127.0.0.1:9000

When using a Unix socket, set the socket permissions so Nginx can connect to it:

listen.owner = nginx
listen.group = nginx
listen.mode = 0660

When using TCP, no socket permission settings are needed, but ensure a firewall allows the port if Nginx is remote.

Process Management Modes

The pm directive controls how PHP-FPM manages worker processes. Three modes are available:

; static: Always keeps a fixed number of workers running
; Best for high-traffic, predictable workloads with sufficient RAM
pm = static
pm.max_children = 20

; dynamic: Adjusts the number of workers based on current load
; Best for servers with variable traffic or limited RAM
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 20

; ondemand: Spawns workers only when requests arrive, kills idle ones
; Best for low-traffic servers or many pools on one machine
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 10s

For most production web servers, dynamic is the recommended mode. A good rule of thumb for sizing pm.max_children is:

pm.max_children = (Total RAM - OS overhead) / Average PHP process memory

Check the average memory used by current PHP-FPM workers:

ps --no-headers -o "rss,cmd" -C php-fpm | awk '{sum+=$1} END {print int(sum/NR/1024) "MB average"}'

Request and Worker Limits

; Restart a worker after this many requests (prevents memory leaks)
pm.max_requests = 500

; Maximum time a request may take before the worker is killed (seconds)
request_terminate_timeout = 60

; Maximum time before a slow request is logged (0 = disabled)
; Set this to enable the slow log
request_slowlog_timeout = 5s

Step 4: Configure the PHP-FPM Slow Log

The slow log records stack traces for requests that take longer than request_slowlog_timeout to complete. It is invaluable for identifying performance bottlenecks:

; Path to the slow log file
slowlog = /var/log/php-fpm/www-slow.log

; Log requests taking longer than this (5 seconds in this example)
request_slowlog_timeout = 5s

Create the log directory if it does not exist:

sudo mkdir -p /var/log/php-fpm
sudo chown nginx:nginx /var/log/php-fpm

When a slow request is detected, the slow log will contain output similar to this:

[17-May-2026 14:32:01]  [pool www] pid 12345
script_filename = /var/www/myapp/public/index.php
[0x00007f3d4c009f60] mysqli_query() /var/www/myapp/app/Models/User.php:87
[0x00007f3d4c009e40] getUser() /var/www/myapp/app/Controllers/HomeController.php:43
[0x00007f3d4c009d30] index() /var/www/myapp/public/index.php:22

Step 5: Enable the PHP-FPM Status Page

PHP-FPM exposes a real-time status endpoint that shows active workers, accepted connections, and queue depth. Enable it in the pool configuration:

; Status page URI — must start with /
pm.status_path = /fpm-status

; Ping endpoint for health checks
ping.path = /fpm-ping
ping.response = pong

Step 6: Start and Enable PHP-FPM

Save the pool configuration. Enable and start the PHP-FPM service:

sudo systemctl enable php-fpm
sudo systemctl start php-fpm

Confirm it started without errors:

sudo systemctl status php-fpm

Verify the socket file was created with the correct ownership:

ls -la /run/php-fpm/www.sock

Step 7: Configure Nginx to Pass PHP Requests to PHP-FPM

Create or update your Nginx virtual host configuration to forward .php requests to the PHP-FPM socket:

sudo vi /etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name yourdomain.com;

    root /var/www/html;
    index index.php index.html;

    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    location / {
        try_files $uri $uri/ =404;
    }

    # Pass PHP files to PHP-FPM via Unix socket
    location ~ .php$ {
        # Mitigate PATH_INFO security issue
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;

        # Connect to the PHP-FPM socket
        fastcgi_pass   unix:/run/php-fpm/www.sock;
        fastcgi_index  index.php;

        # CRITICAL: tells PHP-FPM which file to execute
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Include standard FastCGI parameters
        include        fastcgi_params;

        # Increase timeout for long-running scripts
        fastcgi_read_timeout 300;

        # Buffer settings
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    }

    # Expose the PHP-FPM status page — restrict to localhost
    location ~ ^/(fpm-status|fpm-ping)$ {
        allow 127.0.0.1;
        deny  all;
        fastcgi_pass   unix:/run/php-fpm/www.sock;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

Test the configuration and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Step 8: Verify the FastCGI Configuration

The fastcgi_params file that Nginx includes by default (/etc/nginx/fastcgi_params) sets most FastCGI variables, but the most important one — SCRIPT_FILENAME — must be set explicitly. Confirm it is in your config:

grep SCRIPT_FILENAME /etc/nginx/conf.d/default.conf

Without a correct SCRIPT_FILENAME, PHP-FPM will not know which script to execute and will return a blank page or error.

Step 9: Test PHP Processing and View the Status Page

Create a PHP test file:

sudo bash -c 'echo "<?php echo phpinfo(); ?>" > /var/www/html/test.php'

Access it in a browser at http://yourdomain.com/test.php to confirm PHP is processing. Remove it after testing.

Access the PHP-FPM status page from the server itself:

curl http://127.0.0.1/fpm-status

Example output:

pool:                 www
process manager:      dynamic
start time:           17/May/2026:10:00:00 +0000
start since:          3600
accepted conn:        1482
listen queue:         0
max listen queue:     0
listen queue len:     0
idle processes:       5
active processes:     1
total processes:      6
max active processes: 8
max children reached: 0
slow requests:        2

For a JSON response suitable for monitoring systems:

curl http://127.0.0.1/fpm-status?json

Check the ping endpoint for simple health checks:

curl http://127.0.0.1/fpm-ping

Step 10: Graceful Reload and Log Rotation

After any changes to php.ini or pool configuration files, reload PHP-FPM gracefully. This finishes in-flight requests before restarting workers:

sudo systemctl reload php-fpm

PHP-FPM integrates with logrotate. The default log rotation configuration is at /etc/logrotate.d/php-fpm. After log rotation, send USR1 to PHP-FPM to reopen log file handles:

sudo kill -USR1 $(cat /var/run/php-fpm/php-fpm.pid)

Conclusion

PHP-FPM is now fully configured to work with Nginx on RHEL 7 using a Unix domain socket, with dynamic process management tuned to balance memory usage and concurrency. The slow log will capture stack traces for requests exceeding five seconds, the status endpoint provides real-time operational metrics, and SCRIPT_FILENAME is correctly set so PHP-FPM knows exactly which file Nginx is requesting. This architecture is the standard production setup for PHP applications on RHEL 7 — it is secure, efficient, and easily extended to support multiple application pools by creating additional .conf files in /etc/php-fpm.d/, each listening on its own socket with its own user, process limits, and PHP settings.