Nginx + PHP-FPM
"Nginx + PHP-FPM" is a configuration that runs PHP applications by combining Nginx, a high-performance web server, with PHP FastCGI Process Manager (PHP-FPM). Nginx handles static file delivery, and PHP script execution is delegated to PHP-FPM processes via the fastcgi_pass directive. This approach uses memory more efficiently than Apache + mod_php and provides stable performance even under high traffic.
Syntax
# -----------------------------------------------
# Installing PHP-FPM (Ubuntu / Debian)
# -----------------------------------------------
# Install the php8.3-fpm package
# → Both the PHP-FPM daemon and the CLI are installed together
# Example: sudo apt install php8.3-fpm
sudo apt install php8.3-fpm
# -----------------------------------------------
# Installing PHP-FPM (RHEL / AlmaLinux)
# -----------------------------------------------
# Install the php-fpm package
# Example: sudo dnf install php php-fpm
sudo dnf install php php-fpm
# -----------------------------------------------
# Starting PHP-FPM and enabling auto-start
# -----------------------------------------------
# Start the PHP-FPM service
# Example: sudo systemctl start php8.3-fpm
sudo systemctl start php8.3-fpm
# Enable PHP-FPM to start automatically at boot
sudo systemctl enable php8.3-fpm
# Check the current service status
sudo systemctl status php8.3-fpm
# -----------------------------------------------
# Nginx fastcgi_pass configuration (Unix socket)
# -----------------------------------------------
# Create a virtual host config file in /etc/nginx/sites-available/
# Forward PHP file requests to PHP-FPM via fastcgi_pass
server {
listen 80;
server_name nerv.example.com;
root /var/www/nerv;
index index.php index.html;
# Forward PHP file requests to PHP-FPM
location ~ \.php$ {
# Connect to PHP-FPM via a Unix socket
# → Lower overhead than a TCP port; recommended when both run on the same server
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
# Specify the default index file passed to PHP-FPM
fastcgi_index index.php;
# Pass the actual file path of the script to PHP-FPM
# → Combine $document_root and $fastcgi_script_name
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Load the standard parameters defined in fastcgi_params
# → Sets environment variables such as QUERY_STRING and REQUEST_METHOD
include fastcgi_params;
}
# Block direct access to .htaccess files
location ~ /\.ht {
deny all;
}
}
# -----------------------------------------------
# Nginx fastcgi_pass configuration (TCP port)
# -----------------------------------------------
# Use this when PHP-FPM runs on a separate server,
# or when www.conf is configured with listen = 127.0.0.1:9000
server {
listen 80;
server_name nerv.example.com;
root /var/www/nerv;
index index.php index.html;
location ~ \.php$ {
# Connect to PHP-FPM via TCP port 9000
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# -----------------------------------------------
# PHP-FPM pool configuration (/etc/php/8.3/fpm/pool.d/www.conf)
# -----------------------------------------------
# Specify the pool name (default is www)
[www]
# Set the user and group that PHP-FPM worker processes run as
# → Matching Nginx's worker_processes user prevents permission issues
user = www-data
group = www-data
# Specify the connection type (Unix socket or TCP port)
# → Unix socket (recommended): listen = /run/php/php8.3-fpm.sock
# → TCP port: listen = 127.0.0.1:9000
listen = /run/php/php8.3-fpm.sock
# Specify the process management mode
# → dynamic: automatically adjusts the number of processes based on load (recommended)
# → static: always keeps pm.max_children processes running
# → ondemand: terminates idle processes to save memory
pm = dynamic
# Set the maximum number of processes that can run simultaneously
# → Adjust based on available server memory
# → Guideline: available memory (MB) / memory per process (MB)
pm.max_children = 10
# Set the number of processes to start when PHP-FPM launches
pm.start_servers = 3
# Set the minimum number of idle processes to keep alive
pm.min_spare_servers = 2
# Set the maximum number of idle processes to keep alive
pm.max_spare_servers = 5
Syntax reference
| Setting | Description |
|---|---|
fastcgi_pass | Specifies the connection target for PHP-FPM. You can specify a Unix socket (unix:/run/php/php8.3-fpm.sock) or a TCP port (127.0.0.1:9000). A Unix socket is recommended when both services run on the same server. |
fastcgi_index | Specifies the default filename to append when the URI ends with a slash. Typically set to index.php. |
fastcgi_param SCRIPT_FILENAME | Passes the absolute path of the script for PHP-FPM to execute. The standard value is $document_root$fastcgi_script_name. If this is missing, PHP-FPM cannot find the file and returns an error. |
include fastcgi_params | Loads the standard FastCGI parameters defined in /etc/nginx/fastcgi_params all at once, including QUERY_STRING, REQUEST_METHOD, SERVER_NAME, and others. |
listen in www.conf | Specifies how PHP-FPM listens for connections. Set to a Unix socket path or in IP:port format. This value must match the fastcgi_pass directive in Nginx. |
pm in www.conf | Specifies the process management mode. Choose from dynamic (automatically adjusts based on load), static (fixed count), or ondemand (terminates idle processes). dynamic is recommended for general use. |
pm.max_children in www.conf | Sets the maximum number of PHP-FPM processes that can run simultaneously. Use the server's total memory divided by the estimated memory per process (roughly 30–50 MB) as a starting point. |
pm.start_servers in www.conf | Specifies how many processes to start when the PHP-FPM service launches. Set to a value between pm.min_spare_servers and pm.max_spare_servers. |
pm.min_spare_servers in www.conf | Specifies the minimum number of idle processes to keep running at all times. Keeping a certain number ready ensures fast responses when traffic spikes. |
pm.max_spare_servers in www.conf | Specifies the maximum number of idle processes to maintain. Any idle processes beyond this limit are automatically terminated to free memory. |
user / group in www.conf | Specifies the user and group that PHP-FPM worker processes run as. Matching this with Nginx's worker user (typically www-data) prevents file permission issues. |
Examples
/etc/nginx/sites-available/nerv-php.conf
# -----------------------------------------------
# Nginx virtual host config for the NERV PHP site
# -----------------------------------------------
server {
listen 80;
# Specify the server name (multiple names allowed)
server_name nerv.example.com www.nerv.example.com;
# Set the document root
root /var/www/nerv;
# Specify the priority order of index files
index index.php index.html index.htm;
# Root location block
# → try_files checks for a file, then a directory, then returns 404
location / {
try_files $uri $uri/ =404;
}
# Forward PHP file requests to PHP-FPM
location ~ \.php$ {
# Path injection protection: verify the file actually exists
try_files $uri =404;
# Enable FastCGI split path info
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Connect to PHP-FPM via a Unix socket
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
# Pass the full path of the script for PHP-FPM to execute
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Load standard FastCGI parameters
include fastcgi_params;
# Set a 60-second timeout (for slow or heavy requests)
fastcgi_read_timeout 60;
}
# Block access to hidden files such as .htaccess
location ~ /\.ht {
deny all;
}
# Set access and error log paths
access_log /var/log/nginx/nerv_access.log;
error_log /var/log/nginx/nerv_error.log;
}
Run the following command:
$ sudo apt update $ sudo apt install nginx php8.3-fpm $ sudo systemctl start nginx $ sudo systemctl start php8.3-fpm $ sudo systemctl enable nginx $ sudo systemctl enable php8.3-fpm $ sudo ln -s /etc/nginx/sites-available/nerv-php.conf /etc/nginx/sites-enabled/ $ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful $ sudo systemctl reload nginx $ sudo mkdir -p /var/www/nerv $ sudo chown -R www-data:www-data /var/www/nerv $ sudo nano /var/www/nerv/info.php (After creating info.php, open http://nerv.example.com/info.php in your browser to verify the setup)
/var/www/nerv/info.php
<?php // ----------------------------------------------- // Verification file for PHP-FPM + Nginx // Use this to confirm the PHP environment is working // ----------------------------------------------- // Calling phpinfo() displays PHP version, configuration, // and loaded extension modules in the browser. // Delete this file after verification for security reasons. phpinfo(); ?>
Run the following command:
(Open http://nerv.example.com/info.php in your browser) (If "PHP Version 8.3.x" is displayed and Server API shows "FPM/FastCGI", Nginx + PHP-FPM is working correctly) $ curl -s http://nerv.example.com/info.php | grep -i "server api" <tr><td class="e">Server API </td><td class="v">FPM/FastCGI </td></tr> (Once verified, delete info.php) $ sudo rm /var/www/nerv/info.php
/etc/php/8.3/fpm/pool.d/www.conf
# ----------------------------------------------- # PHP-FPM pool configuration (tuned for the NERV server) # ----------------------------------------------- [www] # Run PHP-FPM worker processes as www-data # → Match this with Nginx's worker user user = www-data group = www-data # Listen for connections on a Unix socket # → Must match fastcgi_pass in Nginx listen = /run/php/php8.3-fpm.sock # Set the socket owner and group so Nginx can read and write it listen.owner = www-data listen.group = www-data listen.mode = 0660 # Automatically adjust process count based on load pm = dynamic # Maximum number of processes that can run simultaneously # → Guideline for 1 GB RAM: 1024 / 40 ≒ 25 pm.max_children = 20 # Number of processes to start at launch pm.start_servers = 5 # Minimum number of idle processes to keep alive pm.min_spare_servers = 3 # Maximum number of idle processes to keep alive pm.max_spare_servers = 8 # Slow log: record requests that take longer than 5 seconds slowlog = /var/log/php8.3-fpm-slow.log request_slowlog_timeout = 5s
Run the following command:
$ sudo php-fpm8.3 -t
[25-Mar-2026 09:00:00] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
$ sudo systemctl restart php8.3-fpm
$ sudo systemctl status php8.3-fpm
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.3-fpm.service; enabled)
Active: active (running) since Wed 2026-03-25 09:00:05 JST
Main PID: 12345 (php-fpm8.3)
Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec"
Overview
"Nginx + PHP-FPM" is an architecture in which Nginx handles fast delivery of static content while PHP execution is offloaded to separate PHP-FPM processes, enabling efficient and scalable PHP serving. PHP-FPM manages a process pool and, with the pm = dynamic setting, automatically adjusts the number of running processes based on traffic. There are two ways to connect Nginx to PHP-FPM: Unix sockets (recommended when both run on the same server) and TCP ports. The socket approach has lower latency than TCP because communication stays within the kernel. Use systemctl to start, stop, and enable services. In production, configure a firewall with ufw or iptables to close unnecessary ports. After verifying the setup, be sure to delete debug files such as info.php.
If you find any errors or copyright issues, please contact us.