Nginx PHP Error Log: Where to Find It and Fix Missing Entries

A pink banner with a stylized sheet of paper, a red warning icon, and a pencil, overlaid by the words “PHP Error Log in Nginx.”

We may earn a referral fee at no additional cost to you when you shop through the links on this page.

Your PHP-FPM app is throwing 500s and the log file is empty. This DevAnswers walkthrough covers where Nginx and PHP-FPM write logs by default on Ubuntu and Debian, why errors don’t always appear, and how to read them via tail, journalctl, or docker logs.

Default Log Locations on Linux 

Nginx maintains two log files by default on every supported Linux distribution:

  • Error log: /var/log/nginx/error.log; server errors, configuration problems, and PHP-FPM errors that bubble up via fastcgi_pass.
  • Access log: /var/log/nginx/access.log; every HTTP request including status codes, client IPs, and user agents.

PHP-FPM is more variable. The exact log path depends on which PHP version your distribution packages. The default php-fpm metapackage on each release pulls in a different version-pinned package, and that package writes to a version-stamped log file:

DistributionPHP versionDefault FPM log file
Ubuntu 22.04 (Jammy)8.1/var/log/php8.1-fpm.log
Ubuntu 24.04 (Noble)8.3/var/log/php8.3-fpm.log
Ubuntu 26.04 (Resolute)8.5/var/log/php8.5-fpm.log
Debian 12 (Bookworm)8.2/var/log/php8.2-fpm.log
Debian 13 (Trixie)8.4/var/log/php8.4-fpm.log

To check what’s running on your server, look up the metapackage version:

Terminal
apt-cache policy php-fpm

The FPM master log records pool start/stop events, slow-script reports, and segfaults. It does not automatically include every PHP error your application throws. Those usually surface in Nginx’s error log via fastcgi_pass, or they end up captured by systemd’s journal. The next two sections explain why.

Why Your PHP Errors Aren’t Appearing 

This is the most common reason readers land here: PHP-FPM is running, the app is throwing errors, but tail /var/log/nginx/error.log shows nothing. Six things are usually responsible.

  1. log_errors is off. PHP’s display_errors directive controls whether errors render in the browser response; log_errors independently controls whether they go to a log file. Production php.ini sets display_errors = Off for security, which is correct, but if log_errors is also Off nothing gets written anywhere. See our guide to the PHP display_errors directive for the distinction in detail.
  2. The pool’s error_log is unset. PHP-FPM pools (/etc/php/<ver>/fpm/pool.d/www.conf) can declare php_admin_value[error_log]. If they don’t, PHP-FPM falls back to the master FPM log or to stderr, neither of which may be where you’re looking.
  3. catch_workers_output is disabled. By default, FPM worker stdout and stderr are sent to /dev/null. Setting catch_workers_output = yes in the pool config redirects worker output into the FPM error log instead.
  4. Some output lands in the systemd journal, not the log file. On Ubuntu 22.04 and later (and Debian 12+), php-fpm runs as a systemd service. The packaged default does write the master log to the file in the table above, but anything the process prints to stdout or stderr, like a config error that kills the service at startup, lands in journald instead. The same applies whenever error_log points at /proc/self/fd/2 (the Docker default) or is unset. tail won’t find those entries; journalctl will. Covered in the Reading PHP-FPM Logs with journalctl section below.
  5. File permissions block the writer. The PHP-FPM master runs as root but workers drop to www-data. If the pool’s error_log path or its parent directory aren’t writable by www-data, the error is silently dropped. Verify ownership with ls -la /var/log/.
  6. Errors are flowing to Nginx but you’re tailing the wrong file. Nginx’s error_log directive can appear in the http, server, or location block of your config. A per-server-block error_log /var/log/nginx/site-a.log; sends one site’s errors to a different file. Run sudo nginx -T to dump the full effective config and grep for error_log.
Run sudo nginx -T | grep -E '^\s*error_log' to see every error_log directive Nginx actually loaded, in include order. Run it with sudo: unprivileged, the dump aborts on root-only files such as TLS keys and silently comes back incomplete. Since nginx 1.5.2, multiple error_log directives in the same scope all receive log lines; the last one does not override the others.

Configuring PHP-FPM to Log Its Own Errors 

Once you know which directives matter, fixing this is mechanical. Open the default pool config (replace the version with whatever your distribution ships):

Terminal
sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Set or uncomment the following directives in the [www] pool. The two php_admin_* lines force-set per-pool values that override anything in php.ini; catch_workers_output ensures worker stdout/stderr is captured rather than discarded:

/etc/php/8.3/fpm/pool.d/www.conf
; Where this pool writes its errors
php_admin_value[error_log] = /var/log/php8.3-fpm/devans.log

; Make sure errors are logged at all
php_admin_flag[log_errors] = on

; Capture worker stdout and stderr into the error log
catch_workers_output = yes

Save and exit (press CTRL + X, press Y, then press ENTER)

Create the directory and ensure the www-data user owns it before reloading:

Terminal
sudo mkdir -p /var/log/php8.3-fpm
sudo chown www-data:www-data /var/log/php8.3-fpm
sudo systemctl reload php8.3-fpm

If you’re not sure whether reload picked up the change versus a full restart, see our notes on reloading PHP-FPM config without dropping connections. The directive locations above assume the default Debian/Ubuntu layout; if your php.ini lives somewhere unusual (manual builds, custom prefixes), our guide to finding and editing php.ini covers locating it across distros.

There are two directive families: php_value / php_flag are mutable from user code via ini_set(); php_admin_value / php_admin_flag are not. Use the admin variants for production logging settings so applications cannot accidentally turn them off.

Reading PHP-FPM Logs with journalctl 

On Ubuntu 22.04+ and Debian 12+, php-fpm runs as a systemd unit. Per the systemd.exec man page, when a service doesn’t set StandardOutput= or StandardError=, those streams default to journal. On a stock install the master log still goes to the file from the table above, but startup failures printed to stderr, and any setup where error_log targets /proc/self/fd/2, end up in journald rather than a file under /var/log/.

Tail the journal for the FPM unit (replace the version number with whatever’s installed):

Terminal
sudo journalctl -u php8.3-fpm.service -n 200

To watch new entries as they’re written:

Terminal
sudo journalctl -u php8.3-fpm.service -f

Filter by time window, severity, or PID with the standard journalctl flags: --since "10 minutes ago", --priority err, or _PID=12345. If you’d rather force FPM logs into a file (so other tooling like logrotate or log shippers can grab them), set php_admin_value[error_log] in the pool config as shown in the previous section. Once that’s set, the journal stops being the source of truth for PHP errors.

Common Log Entries and What They Mean 

Real-world Nginx and PHP-FPM error log lines tend to fall into a few recurring patterns. Recognising them shortcuts triage:

  • 502 Bad Gateway: PHP-FPM isn’t running, crashed, or its socket/port doesn’t match what Nginx is configured to forward to. Check systemctl status on the FPM unit and confirm the socket path in the Nginx fastcgi_pass directive.
  • “connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied)”: Nginx’s worker user can’t read the PHP-FPM socket. Usually fixed by aligning the FPM pool’s listen.owner/listen.group with Nginx’s user.
  • “upstream prematurely closed connection”: The PHP-FPM worker died mid-request, killed by request_terminate_timeout in the FPM pool config, a fatal error, or a segfault. (If Nginx gives up waiting past fastcgi_read_timeout, the message is “upstream timed out” instead.) Check the FPM log for the kill, then raise the limit or fix the slow code.
  • “upstream sent too big header while reading response header from upstream”: PHP is sending headers larger than Nginx’s FastCGI buffers. Increase fastcgi_buffer_size and fastcgi_buffers; this often surfaces with apps that send oversized cookies.
  • “PHP message: PHP Warning…” inside Nginx’s error log: PHP-FPM successfully forwarded a PHP-level warning. The log line is right; your application code is the issue.

Enabling Verbose / Debug Logging 

If the default severity isn’t surfacing what you need, both Nginx and PHP-FPM accept a per-service severity level. Bump them temporarily during a debugging session, then revert.

Nginx’s error_log directive accepts eight severity levels in increasing order: debug, info, notice, warn, error, crit, alert, emerg. Setting a level captures that level and everything more severe; the default is error. Add debug only when you genuinely need full request tracing:

/etc/nginx/nginx.conf
http {
    # ...
    error_log /var/log/nginx/error.log debug;
    # ...
}

Save and exit (press CTRL + X, press Y, then press ENTER)

Reload Nginx after editing:

Terminal
sudo systemctl reload nginx

PHP-FPM has its own log_level directive in php-fpm.conf with five valid values: alert, error, warning, notice, debug. The default is notice. To raise it:

/etc/php/8.3/fpm/php-fpm.conf
; Possible values: alert, error, warning, notice, debug
log_level = debug

Save and exit (press CTRL + X, press Y, then press ENTER)

Then restart PHP-FPM:

Terminal
sudo systemctl restart php8.3-fpm
Debug logging is verbose enough to fill a disk on a busy server. Revert to error (Nginx) and notice (PHP-FPM) once you’ve finished investigating, and keep an eye on df -h while debug is enabled.

PHP-FPM Logs in Docker Containers 

The official php:fpm and php:fpm-alpine Docker images take a different approach. Containers don’t run systemd; they have one foreground process whose stdout and stderr the Docker daemon captures. To make PHP-FPM logs visible via docker logs, the official image redirects the FPM error log to the container’s stderr stream.

That’s done in /usr/local/etc/php-fpm.d/docker.conf shipped with the image (its companion zz-docker.conf only forces foreground mode). The relevant directive is:

/usr/local/etc/php-fpm.d/docker.conf
[global]
error_log = /proc/self/fd/2

[www]
access.log = /proc/self/fd/2
catch_workers_output = yes
decorate_workers_output = no

By writing to /proc/self/fd/2, FPM is writing to its own file descriptor 2, which is the container’s stderr. Per Docker’s logging documentation, docker logs shows the container’s STDOUT and STDERR by default, and the json-file logging driver (Docker’s default) stores those streams as JSON files on local disk so the daemon can replay them on demand.

To watch logs from a running PHP-FPM container:

Terminal
docker logs -f my-php-fpm-container

If you’ve built your own image starting from php:fpm and your application’s logs aren’t appearing, check that you haven’t accidentally removed the docker.conf overrides. A custom error_log = /var/log/php-fpm.log inside the container will write to a path that docker logs can never see.

Next Steps 

For a deeper reference on Nginx’s two log files alone (formats, severity levels, custom log layouts), see our companion guide to Nginx error and access log locations. If you’re standing up a fresh server and want PHP-FPM correctly wired into Nginx from the start, work through our ultimate LEMP stack guide for Ubuntu 26.04; it covers the FPM pool config and Nginx fastcgi_pass integration end-to-end. Once your logs are flowing, logrotate handles disk pressure on its own; the relevant policies live under /etc/logrotate.d/nginx and the version-stamped FPM file (e.g. /etc/logrotate.d/php8.1-fpm on Ubuntu 22.04, /etc/logrotate.d/php8.3-fpm on Ubuntu 24.04, or /etc/logrotate.d/php8.4-fpm on Debian 13).

Support DevAnswers
Written, tested and maintained by one developer. Your support keeps these guides accurate and ad-light.
Buy me a coffee

1 reply

Leave a reply

  1. Santos Valencia

    yes, it helped