Web Server Intermediate

Resolving Nginx 502 Bad Gateway with PHP-FPM Unix Socket on macOS Localhost

Troubleshoot Nginx 502 Bad Gateway errors on macOS with PHP-FPM Unix sockets. This guide covers common misconfigurations and permissions issues.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Troubleshoot Nginx 502 Bad Gateway errors on macOS with PHP-FPM Unix sockets. This guide covers common misconfigurations and permissions issues.

When developing web applications locally on macOS using Nginx and PHP-FPM, encountering a "502 Bad Gateway" error is a common but frustrating experience. This error typically indicates that Nginx, acting as a reverse proxy, failed to receive a valid response from the upstream server – in this case, PHP-FPM – which is responsible for processing PHP requests. This guide will walk you through the precise steps to diagnose and resolve this issue, focusing on configurations involving Unix sockets.

Symptom & Error Signature

The primary symptom is a generic "502 Bad Gateway" message displayed in your web browser when trying to access a PHP-enabled page. To truly diagnose the problem, you need to consult the Nginx error logs.

Browser Output:

502 Bad Gateway
nginx/1.24.0

Nginx Error Log (typical location on macOS Homebrew: /usr/local/var/log/nginx/error.log):

One of these will typically appear, often providing the most direct clue:

2023/10/26 10:30:05 [crit] 12345#0: *1 connect() to unix:/usr/local/var/run/php-fpm/php-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/usr/local/var/run/php-fpm/php-fpm.sock:", host: "localhost"

Or, a permissions issue:

2023/10/26 10:30:05 [crit] 12345#0: *1 connect() to unix:/usr/local/var/run/php-fpm/php-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/usr/local/var/run/php-fpm/php-fpm.sock:", host: "localhost"

Less commonly, if PHP-FPM starts but crashes or struggles with a request:

2023/10/26 10:30:05 [warn] 12345#0: *1 upstream prematurely closed connection while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/usr/local/var/run/php-fpm/php-fpm.sock:", host: "localhost"

Root Cause Analysis

The 502 Bad Gateway error, when involving a PHP-FPM Unix socket, stems from a communication breakdown between Nginx and PHP-FPM. Here are the most common underlying reasons:

  1. PHP-FPM Not Running: The most straightforward cause. If the PHP-FPM service isn't active, the Unix socket won't exist, and Nginx will have nothing to connect to.
  2. Incorrect Unix Socket Path: Nginx is configured to look for the PHP-FPM socket at one path (e.g., /usr/local/var/run/php-fpm/php-fpm.sock), but PHP-FPM is configured to create it at a different path (e.g., /tmp/php-fpm.sock) or simply isn't creating it at all.
  3. Permissions Issues: Even if the socket exists, the Nginx process user (typically _www on macOS Homebrew installations, or www-data on Linux) might not have the necessary read/write permissions for the socket file itself or its parent directory. This is particularly prevalent on macOS due to differing user/group contexts.
  4. PHP-FPM Configuration Errors: PHP-FPM might be failing to start or crashing due to syntax errors in its configuration files (php-fpm.conf, www.conf), or running into resource limits (e.g., memory_limit, max_children) when processing a request, leading to premature connection closure.
  5. Corrupted or Stale Socket: Occasionally, the socket file might become corrupted or not be properly cleaned up after a previous PHP-FPM shutdown, preventing a new socket from being created correctly.

Step-by-Step Resolution

Follow these steps systematically to pinpoint and resolve the 502 Bad Gateway error.

1. Verify PHP-FPM Status

First, ensure that the PHP-FPM service is actually running. On macOS with Homebrew, this is typically managed as a service.

# List all Homebrew services and their status
brew services list | grep php

You should see an output similar to this:

[email protected] started _www /Users/youruser/Library/LaunchAgents/[email protected]

If the status is stopped or error, restart it:

# Restart the specific PHP version service (adjust for your PHP version)
brew services restart [email protected] # e.g., for PHP 8.8

If you have multiple PHP versions installed via Homebrew, ensure you're starting the one your Nginx configuration expects. Use php -v to check your command-line PHP version.

2. Cross-Check Unix Socket Path

The most common cause is a mismatch between where Nginx expects the socket and where PHP-FPM creates it.

a. Identify Nginx's fastcgi_pass directive:

Open your Nginx configuration file. On macOS with Homebrew, the main config is usually at /usr/local/etc/nginx/nginx.conf, and site-specific configs are often in servers/ or sites-enabled/ directories included by the main config.

# Open Nginx main configuration (adjust path if different)
sudo nano /usr/local/etc/nginx/nginx.conf
# Or your site-specific config, e.g. /usr/local/etc/nginx/servers/your_site.conf

Look for a location ~ .php$ block and the fastcgi_pass directive. It will specify the Unix socket path:

location ~ .php$ {
    fastcgi_pass   unix:/usr/local/var/run/php-fpm/php-fpm.sock; # <-- This path
    fastcgi_index  index.php;
    include        fastcgi.conf;
}

Make a note of the exact path (e.g., /usr/local/var/run/php-fpm/php-fpm.sock).

b. Identify PHP-FPM's listen directive:

Next, open your PHP-FPM configuration. For Homebrew installations, this is typically located in the php-fpm.d directory within your PHP version's configuration.

# Open PHP-FPM www pool configuration (adjust for your PHP version)
sudo nano /usr/local/etc/php/8.8/php-fpm.d/www.conf

Search for the listen directive:

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - Use IPv4 address and port number.
;   '[::1]:port'           - Use IPv6 address and port number.
;   '/path/to/unix/socket' - Use Unix socket.
; Note: This value is in an environment variable when the FPM is in daemon mode
; and it may override the FPM default configuration.
listen = /usr/local/var/run/php-fpm/php-fpm.sock # <-- This path

Ensure the fastcgi_pass path in Nginx exactly matches the listen path in PHP-FPM. Even a minor typo or different directory will cause a 502 error. If they differ, update one to match the other. Typically, it's easier to adjust the Nginx config to match PHP-FPM's default, which is often /usr/local/var/run/php-fpm/php-fpm.sock for Homebrew.

After making changes, restart both services:

brew services restart [email protected] # Adjust version
brew services restart nginx

3. Resolve Permissions for Socket Directory and File

A "Permission denied" error (13) in the Nginx logs points directly to this. Nginx's user needs access to the socket.

a. Identify Nginx and PHP-FPM users:

  • Nginx User: On macOS Homebrew, Nginx often runs as the _www user and group. You can verify this by checking the user directive in nginx.conf or by listing running processes:
    ps aux | grep nginx | grep master
    # Output might show: root     12345   0.0  0.0  4567896  1234 root     0:00.00 nginx: master process /usr/local/opt/nginx/bin/nginx -g daemon off;
    # And then for workers: _www     12346   0.0  0.0  4568816  2048 _www     0:00.00 nginx: worker process
    
    This indicates _www is the worker user.
  • PHP-FPM User/Group: In your www.conf (e.g., /usr/local/etc/php/8.8/php-fpm.d/www.conf), look for user and group directives, as well as listen.owner, listen.group, and listen.mode.
    ; Unix user/group of processes
    ; Note: The user/group specified here must also own the socket file,
    ; so that Nginx can access it.
    user = _www
    group = _www
    
    ; Set permissions for the socket
    listen.owner = _www
    listen.group = _www
    listen.mode = 0660
    
    Ensure user and group (for PHP-FPM processes) and listen.owner/listen.group (for the socket file itself) are set to the same user Nginx runs as (e.g., _www). listen.mode = 0660 allows both the owner and group to read/write the socket.

b. Check and Adjust Directory Permissions:

The parent directory of the socket file also needs appropriate permissions. For /usr/local/var/run/php-fpm, the _www user needs to be able to create files.

# Check permissions of the socket's parent directory
ls -ld /usr/local/var/run/php-fpm

It should ideally be owned by _www and have appropriate permissions (e.g., drwxr-xr-x or drwxr-x---). If not, adjust them:

# Change ownership to the Nginx/PHP-FPM user/group
sudo chown -R _www:_www /usr/local/var/run/php-fpm

# Set appropriate directory permissions (read, write, execute for owner; read, execute for group/others)
sudo chmod 755 /usr/local/var/run/php-fpm

Using -R with chown on directories that might contain other important files should be done carefully. For /usr/local/var/run/php-fpm, it's usually safe as it's typically managed by Homebrew/PHP-FPM.

After adjusting permissions and PHP-FPM configurations, restart both services:

brew services restart [email protected] # Adjust version
brew services restart nginx

4. Check PHP-FPM Error Logs

If Nginx logs show upstream prematurely closed connection or PHP-FPM is running but the socket isn't being created, PHP-FPM itself might be encountering issues.

a. Locate PHP-FPM logs:

On Homebrew, the PHP-FPM log file is usually found at /usr/local/var/log/php-fpm.log or a similar path within your PHP version directory.

# Tail the PHP-FPM error log
sudo tail -f /usr/local/var/log/php-fpm.log

Look for any errors, warnings, or fatal messages during startup or when a request is made. Common issues include:

  • Syntax errors in php.ini or www.conf.
  • Memory limit exhaustion: Allowed memory size of X bytes exhausted – adjust memory_limit in php.ini.
  • Max children limit: If pm.max_children is too low in www.conf, PHP-FPM might become unresponsive.
  • Missing extensions: PHP-FPM might fail to start if a required extension is missing or misconfigured.

b. Restart PHP-FPM in debug mode (if necessary): For deeper debugging, you can stop the Homebrew service and try running php-fpm directly from the terminal to see immediate output:

brew services stop [email protected] # Stop the service first
/usr/local/opt/[email protected]/sbin/php-fpm --fpm-config /usr/local/etc/php/8.8/php-fpm.conf -y /usr/local/etc/php/8.8/php-fpm.d/www.conf

This will run PHP-FPM in the foreground and output errors directly to your terminal. Press Ctrl+C to stop it. Remember to restart the Homebrew service afterwards: brew services start [email protected].

5. Nginx Configuration Syntax and Restart

Even if the socket path and permissions are correct, Nginx itself might have a configuration issue preventing it from handling requests properly.

a. Test Nginx configuration syntax:

sudo nginx -t

This command will check the syntax of your Nginx configuration files. If there are any errors, it will report them with file paths and line numbers. Correct any reported issues.

b. Restart Nginx:

Ensure Nginx has picked up any changes to its configuration or PHP-FPM's availability.

brew services restart nginx

6. Clear Stale Socket (If Applicable)

If all else fails and you suspect a corrupted or stale socket file, you can manually remove it.

# Stop PHP-FPM
brew services stop [email protected]

# Remove the socket file (adjust path to match your configuration)
sudo rm /usr/local/var/run/php-fpm/php-fpm.sock

# Start PHP-FPM again (this will recreate the socket)
brew services start [email protected]

# Restart Nginx
brew services restart nginx

This step is usually not necessary but can resolve rare edge cases where the socket file itself becomes problematic.

By following these systematic troubleshooting steps, you should be able to identify and resolve the Nginx 502 Bad Gateway error caused by PHP-FPM Unix socket misconfigurations on your macOS local environment.

👨‍💻

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.