Runtimes Intermediate

PHP-FPM Socket Permission Denied: Nginx User/Group Mismatch on CentOS Stream / Rocky Linux

Resolve 'permission denied' errors between Nginx and PHP-FPM Unix sockets on CentOS Stream/Rocky Linux, often leading to 502 Bad Gateway. Fix user/group mismatches and SELinux.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'permission denied' errors between Nginx and PHP-FPM Unix sockets on CentOS Stream/Rocky Linux, often leading to 502 Bad Gateway. Fix user/group mismatches and SELinux.

Introduction

Encountering a "502 Bad Gateway" error when trying to access your PHP-powered website on CentOS Stream or Rocky Linux can be a frustrating experience. Often, this indicates a communication breakdown between your Nginx web server and the PHP-FPM FastCGI Process Manager. A prevalent cause for this is a "permission denied" error when Nginx attempts to connect to the PHP-FPM Unix socket, typically stemming from a user or group mismatch, or an improperly configured SELinux context.

This guide will walk you through diagnosing and resolving these permission issues, ensuring Nginx can successfully hand off PHP requests to PHP-FPM.

Symptom & Error Signature

When this issue occurs, you will typically observe the following:

  • Browser Output: A generic "502 Bad Gateway" error page.

  • Nginx Error Logs: The most definitive evidence will be found in your Nginx error logs (commonly located at /var/log/nginx/error.log). You'll see entries similar to these:

    2023/10/26 14:35:01 [crit] 12345#12345: *1 connect() to unix:/run/php-fpm/www.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.1.100, server: example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/run/php-fpm/www.sock:", host: "example.com"
    

    The key part of this error is connect() to unix:/run/php-fpm/www.sock failed (13: Permission denied). This explicitly tells you that Nginx could not connect to the specified PHP-FPM socket due to a permission issue.

  • PHP-FPM Logs: While less common for this specific error (as the problem is Nginx accessing the socket, not PHP-FPM creating it), it's always good practice to check PHP-FPM logs for related issues. They are typically found at /var/log/php-fpm/www-error.log or similar paths depending on your PHP version and pool configuration.

    tail -f /var/log/nginx/error.log
    # and
    tail -f /var/log/php-fpm/www-error.log
    

Root Cause Analysis

The "Permission denied" error indicates that the Nginx process, running under its designated user, lacks the necessary read/write permissions to interact with the PHP-FPM Unix socket file. This can happen for several reasons:

  1. User/Group Mismatch in PHP-FPM Configuration:

    • By default, PHP-FPM pools (like www.conf) often run as the apache user and group on RHEL-based systems, or nobody if not explicitly configured.
    • Nginx, by default, runs as the nginx user and group.
    • If the PHP-FPM socket is created with ownership apache:apache and restrictive permissions (e.g., 0600), the nginx user will not be able to connect.
  2. Incorrect listen.mode:

    • The listen.mode directive in the PHP-FPM pool configuration specifies the permissions of the Unix socket file. If this mode is too restrictive (e.g., 0600), even if the nginx user is in the correct group, it might not have access if the listen.group is not configured correctly.
  3. SELinux Context Issues:

    • CentOS Stream and Rocky Linux use SELinux (Security-Enhanced Linux) which provides an additional layer of security. Even if file permissions (user/group/mode) are correct, SELinux might be preventing Nginx from accessing the PHP-FPM socket if the socket's SELinux context is not configured to allow httpd_t (Nginx's typical context) processes to interact with it.
  4. Incorrect Socket Path:

    • Less common for permission errors, but ensuring the fastcgi_pass directive in Nginx points to the exact same socket path as configured in PHP-FPM is crucial. A mismatch here won't directly cause "permission denied" but will lead to No such file or directory or similar errors.

Step-by-Step Resolution

Follow these steps carefully to diagnose and resolve the issue.

1. Verify Nginx and PHP-FPM Process Users

First, determine which users Nginx and PHP-FPM are running as.

ps aux | grep nginx | grep -v grep
ps aux | grep php-fpm | grep -v grep

Expected Output Example:

nginx      12345  0.0  0.1  xxxxx  yyyyy ?        S    Oct26   0:00 nginx: worker process
nginx      12346  0.0  0.1  xxxxx  yyyyy ?        S    Oct26   0:00 nginx: worker process
...

root       20000  0.0  0.2  xxxxx  yyyyy ?        Ss   Oct26   0:00 php-fpm: master process (/etc/php-fpm.conf)
apache     20001  0.0  0.3  xxxxx  yyyyy ?        S    Oct26   0:01 php-fpm: pool www
apache     20002  0.0  0.3  xxxxx  yyyyy ?        S    Oct26   0:01 php-fpm: pool www

From this, you can see nginx runs as nginx:nginx and php-fpm workers run as apache:apache. This mismatch is the core of the problem.

2. Locate PHP-FPM Pool Configuration

The main PHP-FPM configuration file is typically /etc/php-fpm.conf, which often includes individual pool configuration files from /etc/php-fpm.d/. The default pool is usually www.conf.

ls /etc/php-fpm.d/

You'll likely see www.conf. Open this file for editing:

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

3. Adjust PHP-FPM Socket Permissions

Inside www.conf (or your custom pool file), locate the listen directive and related permission settings.

; Unix user/group of processes
; Note: The user/group specified here will also be used for the socket if listening on a unix socket
;       and not specified otherwise.
user = apache
group = apache

; The address on which to accept FastCGI requests.
; Valid syntaxes are: 'ip.add.re.ss:port', 'port', '/path/to/unix/socket'.
; Default Value: listen = 127.0.0.1:9000
listen = /run/php-fpm/www.sock

; Set permissions for unix socket, if one is used. In Linux, read/write permissions
; are required for both the user and group that the webserver runs as.
; Default Values: listen.owner = <user from above>, listen.group = <group from above>, listen.mode = 0660
listen.owner = nobody
listen.group = nobody
listen.mode = 0660

There are two primary ways to resolve the user/group mismatch:

Option A: Configure PHP-FPM to use the Nginx user/group (Recommended)

This is generally the cleanest approach. Change the user, group, listen.owner, and listen.group directives to nginx.

; /etc/php-fpm.d/www.conf
user = nginx
group = nginx

listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

The listen.mode = 0660 ensures that the owner (nginx) and the group (nginx) have full read/write access to the socket.

Option B: Add Nginx user to PHP-FPM's group (Alternative)

If you prefer to keep PHP-FPM running as apache (or another user) for specific reasons, you can add the nginx user to the apache group.

First, ensure PHP-FPM is configured to run as apache and create the socket with apache group ownership and 0660 mode:

; /etc/php-fpm.d/www.conf
user = apache
group = apache

listen = /run/php-fpm/www.sock
listen.owner = apache
listen.group = apache
listen.mode = 0660

Then, add the nginx user to the apache group:

sudo usermod -aG apache nginx

After making any changes to www.conf or user groups, you must restart PHP-FPM for the changes to take effect. If you modified user groups, a full system reboot might be required or at least restarting all nginx and php-fpm processes to ensure group memberships are reloaded.

4. Verify Socket Path and Nginx Configuration

Ensure your Nginx server block configuration (/etc/nginx/nginx.conf or conf.d/*.conf) correctly points to the PHP-FPM Unix socket path you configured.

Look for a location ~ .php$ block and the fastcgi_pass directive:

# /etc/nginx/nginx.conf or /etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name example.com;
    root /usr/share/nginx/html; # Your document root

    index index.php index.html index.htm;

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

    location ~ .php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock; # <-- This must match
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Confirm that the fastcgi_pass path (unix:/run/php-fpm/www.sock) matches the listen directive in your www.conf. Also, verify the user directive in your main nginx.conf:

# /etc/nginx/nginx.conf
user nginx; # This should typically be 'nginx'
worker_processes auto;
...

5. Check and Adjust SELinux Context (Crucial for CentOS/Rocky)

On CentOS Stream and Rocky Linux, SELinux can block access even if file permissions are correct. The httpd_t type (assigned to Nginx processes) must be allowed to interact with the socket's type. PHP-FPM sockets are typically assigned httpd_var_run_t.

  1. Check current SELinux context of the socket: (You might need to start php-fpm once to create the socket for ls -Z to work)

    sudo systemctl start php-fpm
    ls -Z /run/php-fpm/www.sock
    

    Expected (Correct) Output:

    srw-rw----. nginx nginx system_u:object_r:httpd_var_run_t:s0 /run/php-fpm/www.sock
    

    If you see a different type (e.g., var_run_t or unlabeled_t), you need to correct it.

  2. Set the correct SELinux context:

    This command defines a permanent SELinux context for the /run/php-fpm directory and its contents:

    sudo semanage fcontext -a -t httpd_var_run_t "/run/php-fpm(/.*)?"
    

    Then, apply the new context:

    sudo restorecon -Rv /run/php-fpm
    

    While tempting for troubleshooting, permanently disabling SELinux (setenforce 0 or editing /etc/selinux/config) is not recommended in a production environment due to significant security implications. Always aim to configure SELinux correctly.

    Alternatively, if you've placed your socket in a non-standard location (e.g., /var/www/myproject/php-fpm.sock), you might need to adjust the SELinux boolean httpd_can_network_connect_anon or assign a suitable type to that custom directory:

    sudo setsebool -P httpd_can_network_connect_anon 1 # If Nginx needs to connect to an external socket/port (less common for Unix sockets)
    # Or, for a custom socket path:
    sudo semanage fcontext -a -t httpd_var_run_t "/var/www/myproject/php-fpm.sock"
    sudo restorecon -v "/var/www/myproject/php-fpm.sock"
    

6. Restart Services

After making all necessary configuration changes and potentially adjusting SELinux, restart both PHP-FPM and Nginx to apply them.

sudo systemctl restart php-fpm
sudo systemctl restart nginx

Verify their status to ensure they are running without errors:

sudo systemctl status php-fpm
sudo systemctl status nginx

Now, try accessing your website again. The "502 Bad Gateway" error should be resolved, and your PHP application should load correctly. If the issue persists, carefully re-check each step and examine the Nginx and PHP-FPM error logs for new or different error messages.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.