Skip to content

How to Read and Debug WordPress Error Logs: A Complete Developer’s Guide

Emnes
Terminal window showing WordPress debug log output with colored error lines

Every WordPress site runs into errors. A plugin throws a PHP warning, a theme triggers a fatal exception during an update, a checkout breaks on a single specific product, or the homepage loads fine for visitors but the admin dashboard mysteriously hangs. When that happens, the question is almost never “is there a bug?” — it’s “where is the bug, and what exactly is it saying?”

The answer is almost always in the WordPress debug logs. But “check the error log” is advice that assumes you already know which log, where it lives, how to read it, and what the different message types actually mean. This guide walks through all of that — from turning on logging safely on a live site to interpreting PHP stack traces, WordPress-specific notices, slow query logs, and nginx access logs — so you can go from “something is broken” to “here is the exact file and line that caused it” in under five minutes.

Why WordPress Error Logs Matter

By default, WordPress hides errors. Visitors see a blank white screen or a generic “Something went wrong” page; developers see exactly the same thing. This is a good default for production — you don’t want to leak database credentials or file paths to the public — but it’s a terrible default for debugging. Without logs, you’re reduced to guessing: disabling plugins one at a time, switching themes, rolling back updates hoping one of them fixes it.

With logs enabled, the workflow is the opposite. You reproduce the problem, open the log, read the most recent error, and go directly to the file and line number causing it. Minutes, not hours.

The types of logs that matter for a WordPress site:

  • debug.log — PHP errors, warnings, notices, and anything you’ve explicitly written with error_log() or wp_debug_log()
  • PHP-FPM error log — fatal errors, segfaults, memory exhaustion, worker crashes
  • Nginx / Apache access log — every HTTP request, useful for identifying slow endpoints, attack traffic, and 404 patterns
  • Nginx / Apache error log — server-level failures, upstream timeouts, SSL issues
  • MySQL slow query log — queries taking longer than a threshold, invaluable when the site feels slow
  • WordPress activity log (via plugin) — user actions: logins, content changes, plugin installs

1. Turn On WordPress Debug Logging Safely

WordPress has a built-in logging system controlled by three constants in wp-config.php. Add these above the /* That's all, stop editing! */ line:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

What each one does:

  • WP_DEBUG — turns on WordPress debug mode. Enables notice and warning output and a few internal sanity checks.
  • WP_DEBUG_LOG — sends errors to wp-content/debug.log instead of (or in addition to) displaying them. You can also set this to a custom path: define( 'WP_DEBUG_LOG', '/var/log/wordpress/debug.log' );
  • WP_DEBUG_DISPLAY — set to false to hide errors from visitors. Always false on a production site, even a broken one.
  • display_errors — PHP-level override that prevents any errors from leaking into HTML output even if a misbehaving plugin tries to force them on.

After saving wp-config.php, reproduce the issue and check wp-content/debug.log. If the file doesn’t appear, verify that wp-content/ is writable by the web server user (usually www-data or web).

Using Custom Logs Viewer Instead of SSH

If you don’t have SSH access (shared hosting, managed WordPress) or you just prefer not to tail -f from a terminal, our Custom Logs Viewer plugin reads debug.log, PHP-FPM logs, and nginx logs directly from the WordPress admin. It pretty-prints stack traces, filters by severity, highlights the most recent errors, and lets you clear old entries without touching the filesystem.

2. Reading a PHP Error: Anatomy of a Log Line

A typical debug.log entry looks like this:

[2026-04-18 14:32:11 UTC] PHP Fatal error: Uncaught TypeError: Cannot access offset of type string on string in /srv/www/example.com/web/app/plugins/my-plugin/src/Form.php:142

Decoded:

  • Timestamp[2026-04-18 14:32:11 UTC]. Cross-reference with the moment the user hit the problem.
  • SeverityPHP Fatal error. Everything crashed. Other levels in order of urgency: Parse error (code won’t even run), Fatal error, Warning, Notice, Deprecated, Strict.
  • Error typeTypeError: Cannot access offset of type string on string. Someone tried to do $variable['key'] when $variable was a plain string, not an array.
  • File and lineForm.php:142. That is where to go first.

PHP errors fall into categories worth knowing:

  • Parse error — broken syntax, the file can’t be loaded at all. Usually caused by a missed semicolon, unclosed bracket, or a botched find-and-replace. Fatal on every request that loads the file.
  • Fatal error — code ran, but hit something it couldn’t recover from. Most common: calling a function that doesn’t exist, dereferencing null, exceeding memory limits, or include-ing a missing file.
  • Warning — something went wrong but PHP continued. include failed, a required argument was missing, division by zero. Often a symptom of a bug that hasn’t crashed yet.
  • Notice — usually harmless on its own (undefined index, undefined variable) but in aggregate points at sloppy code that probably has real bugs nearby.
  • Deprecated — a feature is going away in a future PHP version. Safe today, a fatal error in two releases.

3. Reading Stack Traces

Fatal errors usually include a stack trace — a list of function calls that led to the error, most recent at the top. Example:

#0 /srv/www/example.com/web/wp/wp-includes/class-wp-hook.php(308): MyPlugin\\Form->render()
#1 /srv/www/example.com/web/wp/wp-includes/plugin.php(205): WP_Hook->apply_filters()
#2 /srv/www/example.com/web/wp/wp-includes/plugin.php(515): apply_filters('the_content', '...')
#3 /srv/www/example.com/web/wp-content/themes/sage/index.php(12): do_filter('the_content')

Read top-to-bottom. The error happened at frame #0. Everything below is the chain of calls that got you there. The first line not inside WordPress core is usually where the bug lives — core itself is rarely wrong, but it’s the hook runner that dutifully called your broken plugin function.

4. Turn On Query Logging to Find Slow Pages

If the site feels slow but nothing is visibly broken, the cause is almost always database queries. WordPress can log every query it runs, along with the time each took and the backtrace of code that triggered it. Enable it with:

define( 'SAVEQUERIES', true );

With that on, every query is appended to the global $wpdb->queries array. Tools like Query Monitor surface it visually in the admin bar, showing the slowest queries, duplicate queries, and which plugin or theme function triggered them.

On the database side, MariaDB and MySQL can log slow queries independently — any query taking longer than (for example) 1 second gets written to a slow query log. For a site showing occasional lag spikes, the slow query log is almost always where the culprit lives.

5. Common WordPress Errors and What They Actually Mean

“Allowed memory size of X bytes exhausted”

PHP ran out of memory. Bump WP_MEMORY_LIMIT (in wp-config.php) or memory_limit (in php.ini). But first, find why — a query loading 100,000 posts into memory is the bug; more RAM only hides it.

“Maximum execution time of 30 seconds exceeded”

A request took too long. For import jobs and migrations, raise max_execution_time temporarily. For a normal page, find the slow operation — usually a database query or an external API call without a timeout.

“Call to undefined function …”

Either a plugin is active before the function it depends on loads, or the required plugin/library is missing entirely. Check the stack trace for the calling file, then verify its dependencies are installed and active.

“Error establishing a database connection”

WordPress couldn’t talk to MySQL. In order of likelihood: database credentials wrong in wp-config.php, MySQL server down, max_connections exceeded, or DNS issue with the database host. The MySQL error log will tell you which one.

“502 Bad Gateway” / “504 Gateway Timeout”

Nginx reached PHP-FPM but didn’t get a timely response. 502 usually means PHP crashed (look in the PHP-FPM error log); 504 means PHP is still running but slow (look in debug.log and the slow query log).

“Cannot modify header information — headers already sent”

A script tried to set an HTTP header after output had started. The log will point to where the headers were being set, but the cause is usually a stray space or newline at the top of a file that shouldn’t produce output — functions.php, a custom plugin’s main file, wp-config.php.

6. Server Logs: Beyond WordPress

When debug.log is empty but the site is clearly broken, the problem is below the WordPress layer. Check, in order:

  • PHP-FPM error log/var/log/php8.3-fpm.log or similar. Records crashes, memory exhaustion, worker timeouts.
  • Nginx error log/var/log/nginx/error.log. Upstream timeouts (PHP-FPM not responding), SSL handshake errors, file-not-found issues.
  • Nginx access log/var/log/nginx/access.log. Every HTTP request with status code, response time, user agent. Invaluable for identifying attack traffic and spotting which exact URLs are slow or erroring.
  • System log/var/log/syslog or journalctl -u php8.3-fpm. OOM killer activity, service restarts, disk-full events.

7. Log Rotation and Disk Space

Logs fill disks. A busy site with WP_DEBUG_LOG on can produce gigabytes of notices in a week. Configure log rotation so this doesn’t cascade into a site outage from a full disk:

  • logrotate for system logs (nginx, PHP-FPM) — most Linux distros ship with sensible defaults. Verify they’re running.
  • Manual rotation for debug.log — WordPress does not rotate it automatically. Delete or archive it weekly, or use a plugin (including Custom Logs Viewer) that can rotate and cap it at a fixed size.
  • Monitoring — set up a disk-space alert at 80% full. Logs are the most common cause of “why did the site go down at 3 AM” and the easiest to prevent.

8. A Practical Debugging Workflow

When something breaks, work in this order:

  • Reproduce the problem with WP_DEBUG_LOG turned on
  • Open debug.log and read the most recent entries
  • If there’s a fatal error, go to the file and line it names
  • If debug.log is silent, check the PHP-FPM error log for crashes
  • If PHP-FPM is fine, check the nginx error log for upstream or SSL issues
  • If the site is slow not broken, enable SAVEQUERIES or install Query Monitor
  • If slowness is database-related, check the MySQL slow query log
  • Once fixed, turn WP_DEBUG_LOG off in production (or leave it with a custom path outside the web root)

Frequently Asked Questions

Is it safe to leave WP_DEBUG on in production?

Only with WP_DEBUG_DISPLAY set to false and WP_DEBUG_LOG pointing to a path outside the web root (or protected from direct HTTP access). Exposed debug output can leak server paths, database structure, and credentials.

Where is the WordPress debug.log file stored?

By default, wp-content/debug.log. You can override the location by passing a full path to WP_DEBUG_LOG. Bedrock-based installs (like ours) use web/app/debug.log.

Why is my debug.log file empty even after enabling logging?

Usually one of three things: the constants are set below the “stop editing” line in wp-config.php (so they never load), the wp-content/ directory isn’t writable by the web server user, or an object caching layer is caching a broken wp-config.php. Test by adding error_log('test'); to a plugin file and reloading.

What’s the difference between debug.log and the PHP error log?

debug.log is written by WordPress when WP_DEBUG_LOG is on. The PHP error log is written by the PHP interpreter itself, regardless of WordPress. In practice, most WordPress-level errors end up in both — but fatal crashes that happen before WordPress finishes loading only appear in the PHP log.

How do I read logs without SSH access?

Install a log viewer plugin. Our Custom Logs Viewer reads debug.log, PHP-FPM logs, and nginx logs directly from the WordPress admin with filtering, search, and pretty-printed stack traces. Works on any host — no shell access required.

Stop Guessing, Start Reading

The difference between a developer who fixes WordPress bugs in minutes and one who spends hours guessing is almost entirely about logs. Turn them on. Read them. Learn to recognize the most common patterns. Over time, the error messages stop looking cryptic and start looking like the answer to the question you’re holding.

If you want a cleaner way to read logs without SSH, try Custom Logs Viewer — it’s free, works on any host, and does exactly one thing well. For security-specific logs (who’s attacking your login, which IPs are blocked), see Emnes Oversight.

Related reading: WordPress plugin security guide, speeding up a WooCommerce store, and our complete WordPress backup strategy.