Ahosting Logo
Knowledge Base

How to Debug WordPress with WP_DEBUG and Logs

The right debugging configuration, and the mistakeThe mistake· turning on display of errors on a live site· which shows paths and versions to every visitor· and sometimes database detailThe right combination· debugging on· display off· logging on· so the error goes to a file only you readA blank page is not an absence of informationThe error exists and is being discarded. Logging is what stops it being discarded.

A white screen looks like an absence of information. It is not. PHP produced an error and something decided not to show it.

The three lines

In wp-config.php, above the line that says to stop editing:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );

Debugging on, display off, logging on. Errors are written to wp-content/debug.log and nothing appears to visitors.

If WP_DEBUG is already defined further up the file, change that line instead of adding a second. The first definition wins and the new one does nothing.

Never leave display on

WP_DEBUG_DISPLAY set to true prints errors onto the page: server paths, database table prefixes, the internals of whichever plugin failed.

That is published to everyone who visits, including automated scanners that specifically look for it. On a live site it is a security problem, not an inconvenience.

Reading the log

tail -50 wp-content/debug.log

Three kinds of entry, and only one of them is your problem.

Fatal error. Execution stopped. This is the white screen, and the message names a file and a line.

Warning. Something is wrong and the page continued. Often the real cause of odd behaviour that is not an outright failure.

Deprecated. Old code against a newer PHP. Usually noise, and usually the bulk of the file, filter it out while you look:

grep -i 'fatal error' wp-content/debug.log | tail -20

The file named is where it stopped

A fatal error reports where execution halted, which is not always where the mistake is.

Plugin A calling a function that Plugin B removed produces an error naming Plugin B's file. Reading the trace matters: the last few lines show what called what, and the plugin at the top of that chain is usually the one to look at.

This is where finding which plugin is causing a problem takes over, because the log has told you where to start in place of the whole answer.

When there is no log file

Either nothing has errored since you enabled it (load the broken page again) or wp-content is not writable, or the edit did not take effect.

Confirm the constant is actually set by checking whether a deliberate reload produces anything at all. A caching layer serving the page from before your change is a common reason nothing appears to happen. There is more on clearing the right one in WordPress caching layers.

Block the log from the web

wp-content/debug.log is inside the web root and is readable by anyone who guesses the path, which is not a guess. It is the standard location.

Either point the log somewhere above the web root:

define( 'WP_DEBUG_LOG', '/home/username/wp-errors.log' );

Or block it in .htaccess. The first is better, because it also survives someone re-enabling debugging later without thinking about it.

Turn it off afterwards

The log has no size limit. A site producing a warning on every page load writes continuously, and this is a genuine cause of accounts filling up, where the symptom is not "the log is large" but every service on the account failing to write at once.

Set WP_DEBUG back to false and delete the file when the investigation is over.

Two extras worth knowing

SCRIPT_DEBUG loads unminified scripts, which matters when the fault is in JavaScript rather than PHP.

SAVEQUERIES records every database query for inspection. It is heavy and belongs on staging only, but it is the direct way to find a plugin running hundreds of queries per page. There is more on acting on what it shows in optimising WordPress performance.

Understanding wp-config.php settings walks through the rest of the file.

Reproduce it deliberately rather than waiting

Logging is only useful if the fault occurs while it is on, and an intermittent problem may not.

So the first job after enabling it is to make the fault happen: the exact page, the exact action, logged in or out. Then note the time, because the log holds everything and the timestamp is how you find the relevant lines.

date; # note this
# reproduce the fault now
awk -v t="$(date '+%d-%b-%Y %H:%M')" '$0 ~ t' wp-content/debug.log | head -40

Where the fault genuinely will not reproduce on demand, leave logging on and check back, but set a reminder to turn it off, because a debug log left running is the file that fills the account. There is more on the other way that happens in understanding inodes.

Separate your errors from the noise

A busy site produces thousands of deprecation notices, and the fatal error is one line among them.

grep -c 'PHP Deprecated' wp-content/debug.log
grep -iE 'PHP (Fatal|Parse) error' wp-content/debug.log | tail -20
grep -iE 'PHP Warning' wp-content/debug.log | sed 's/.*PHP Warning: *//' | sort | uniq -c | sort -rn | head

The third command is the useful one nobody runs: grouping warnings by message shows which one is occurring thousands of times, which is frequently the actual problem even when nothing is fatal.

A warning repeating on every page load is both a fault and a performance cost, since writing to the log is work done on every request.

Trace which plugin, not which file

A fatal error names the file where execution stopped, which is frequently a plugin that was called instead of the one at fault.

The stack trace below the error is what identifies the caller. Read it from the bottom: the earliest entry is where the request began, and the plugin appearing furthest down that is not WordPress core is usually the one to investigate.

Where the trace is absent or unhelpful, the halving method resolves it faster than further reading. See finding which plugin is causing a problem.

The server log holds what WordPress cannot

Some failures never reach WordPress, so no amount of its own logging records them.

tail -50 ~/logs/example.com.error.log
grep -iE 'memory|timeout|segfault' ~/logs/example.com.error.log | tail

A process killed for exceeding memory, a request that timed out, a segmentation fault in an extension: all appear in the server's error log and nowhere in the application's.

That is the file to check when the site fails and the debug log is empty, which is otherwise a genuinely confusing state. Understanding cPanel error logs walks through reading it.