To fix PHP-FPM and Nginx security isolation failures, you must ensure that each website runs in a dedicated pool under a unique system user. The core issue usually stems from misconfigured
listen.owner,
listen.group, or incorrect file permissions on the document root. By aligning these settings correctly, Nginx can properly hand off requests to the isolated PHP process without exposing one site to another.
Understanding the Isolation Architecture
PHP-FPM (FastCGI Process Manager) allows you to run multiple PHP processes under different system users. This is critical for shared hosting environments where multiple websites reside on the same server. Without proper isolation, a compromise in one website could potentially allow an attacker to read files from another site if they share the same execution context.
Nginx acts as the reverse proxy, receiving HTTP requests and passing them to the appropriate PHP-FPM socket or port. Each pool should be bound to a specific user and group. If two pools are configured with the same user, or if the file permissions on the web directories do not match the intended owner, the isolation breaks down. The goal is strict separation: User A cannot access User B's files, even through the web server.
Verifying System Users and Groups
The foundation of security lies in the operating system level. You need distinct system users for each website. Avoid using generic users like
www-data for all sites if you require isolation. Instead, create specific users such as
site1_user and
site2_user.
Check if the users exist by running the following command in your terminal:
id site1_user
id site2_user
If these commands return errors, the users do not exist. Create them using
useradd with no login shell to prevent direct SSH access:
sudo useradd -s /bin/false -d /var/www/site1 site1_user
sudo useradd -s /bin/false -d /var/www/site2 site2_user
Ensure that the home directories have restricted permissions. The directory ownership should reflect the new users. Use
chown to assign ownership recursively:
sudo chown -R site1_user:site1_user /var/www/site1
sudo chown -R site2_user:site2_user /var/www/site2
Set directory permissions to
755 and file permissions to
644. This ensures the web server can read the files but cannot write to them unless explicitly allowed for uploads or caches.
Configuring PHP-FPM Pools
Each pool configuration file typically resides in
/etc/php-fpm.d/ or
/etc/php/*/fpm/pool.d/. You must define a separate pool for each website. The most common mistake is reusing the same
user and
group directives across multiple pools.
In your
site1.conf pool file, verify these lines:
[site1]
user = site1_user
group = site1_user
listen = /run/php/site1.sock
listen.owner = www-data
listen.group = www-data
The
listen.owner and
listen.group directives determine who owns the socket file. Typically, this is set to the web server user (
www-data on Debian/Ubuntu or
nginx on CentOS/RHEL). This allows Nginx to communicate with the FPM socket. However, the processes themselves run as
site1_user.
Repeat this structure for
site2.conf, ensuring the
user and
group match the second website's system user. Do not mix these up. After editing, restart the PHP-FPM service:
sudo systemctl restart php-fpm
Nginx Configuration Alignment
Your Nginx server blocks must point to the correct FastCGI socket for each domain. Open your server block configuration for
site1.com:
server {
listen 80;
server_name site1.com;
root /var/www/site1;
location ~ \.php$ {
fastcgi_pass unix:/run/php/site1.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Notice the
fastcgi_pass directive uses the socket path defined in the
site1 pool. If you accidentally point both domains to the same socket, both will run under the same user, destroying isolation. Test your configuration for syntax errors:
sudo nginx -t
If the test passes, reload Nginx:
sudo systemctl reload nginx
Debugging Permission Errors
If you encounter 403 Forbidden or 502 Bad Gateway errors, check the logs. Nginx error logs often indicate permission denied issues when trying to access the document root. PHP-FPM logs will show if the worker process lacks permission to execute scripts.
Use
ls -l on your document root to verify ownership. Ensure the web server user has read access. In some cases, SELinux or AppArmor may block access. Check SELinux status with
getenforce. If it is enforcing, you may need to set correct contexts:
sudo restorecon -Rv /var/www/site1
For deeper debugging, enable logging in the PHP-FPM pool configuration:
access.log = /var/log/php-fpm/site1-access.log
log_level = notice
This helps identify if requests are being routed to the wrong pool. You can also use
ss -lnp | grep php to see which sockets are active and owned by which users.
Serving HTML and PHP from Multiple Folders on Nginx Host provides additional context on managing mixed content types, which often accompanies multi-site setups. Another relevant resource is
Troubleshooting Nginx: HTML Page Returned Instead of Expected HTTP Status Code, which covers diagnostic steps for unexpected Nginx behavior that might mask isolation issues.
Frequently Asked Questions
Can I use the same user for multiple sites?
No, using the same system user for multiple sites defeats the purpose of isolation. If one site is compromised, the attacker gains access to all files owned by that user. Always assign a unique system user to each website pool.
What does listen.owner mean in PHP-FPM?
listen.owner defines the user that owns the Unix socket file created by PHP-FPM. It is typically set to the web server user so that Nginx can connect to the socket. It does not affect the user running the PHP processes, which is controlled by the
user directive.
How do I check if my PHP-FPM pools are running correctly?
You can check the status of individual pools using
systemctl status php-fpm. Additionally, inspect the log files located in
/var/log/php-fpm/ for any startup errors or permission denials. Active sockets can be verified with
ss -lnp | grep php.
Why am I getting 403 Forbidden errors after changing users?
This usually happens because the new user does not have read permissions on the web directory, or the directory permissions are too restrictive. Ensure the document root is owned by the new user and has
755 permissions for directories and
644 for files.
Does SELinux affect PHP-FPM isolation?
Yes, SELinux can block PHP-FPM workers from accessing files outside their designated context. If you switch users, you may need to update SELinux contexts using
restorecon or add custom policies to allow the new user to read web files.