WordPress

Your Site Broke and Told You Nothing: A WordPress Debugging Guide

10 min read

Something on your WordPress site breaks. A page goes blank, a form stops sending, a block editor won’t save — and the site tells you nothing. Just a white screen, or a vague “critical error” notice that names no plugin, no file, and no line number. So you start guessing: deactivate a plugin, refresh, guess again.

You don’t have to work that way. WordPress ships with a full debugging system baked into core, and it’s controlled by a handful of constants you drop into one file. Turn it on and the same broken page will hand you the exact file, function, and line that failed. The last white-screen we walked into was a client’s site right after a routine plugin update; the debug log pointed straight at a fatal “call to undefined function” from a plugin that assumed a newer version of PHP than the host was running. Two minutes of reading beat two hours of guessing.

This guide covers the debugging constants worth knowing, where to put them, how to read the log they produce, and the one setting you must never leave on for the public to see.


Why “It Just Broke” Is a Solvable Problem

By default, a production WordPress site hides its errors. That’s deliberate — you don’t want a PHP warning leaking a server path to a random visitor. But it also means that when something fails, the useful details are being generated and then thrown away. Debug mode simply catches them and puts them somewhere you can read.

There are really two audiences for an error. Visitors should never see one; they should get a clean page or a polite failure. You, the person fixing it, need the raw truth — the file, the line, the stack of what called what. WordPress lets you split those two by writing errors to a private log while keeping the front end clean. That single split is what turns “the site is broken” into “line 214 of that plugin is broken.”

WHY IT MATTERS

A “white screen of death” or a generic critical-error email almost always has a precise cause waiting in the log. Debug mode is the difference between deactivating plugins one at a time hoping the symptom disappears, and reading the exact file and line number that threw the error. It usually turns an afternoon of guessing into a five-minute fix.

The One File That Controls It All

Every debugging switch lives in wp-config.php, the configuration file in your site’s root directory. You edit it over SFTP, through your host’s file manager, or from the command line. WordPress reads these constants early in the boot process, so they govern the whole request.

The single most important rule about editing this file: your constants have to go above the line that reads /* That's all, stop editing! Happy publishing. */. Anything you paste below that comment is ignored, because WordPress has already finished loading its settings by the time it gets there. This trips up more people than any other part of debugging, so it’s worth double-checking.

The master switch: WP_DEBUG

WP_DEBUG is the on/off toggle for the entire system, and it defaults to false. Set it to true and WordPress starts reporting PHP errors, warnings, and notices, plus its own deprecation warnings when a theme or plugin calls a function that’s on its way out.

define( 'WP_DEBUG', true );

On its own, this sends errors to wherever your PHP install is configured to put them, and — unless you change the next two settings — it also prints them right onto the page. That’s fine on a local or staging copy. It is not something you want a live audience to see, which is exactly what the logging and display constants are for.

Catch it quietly: WP_DEBUG_LOG

Set WP_DEBUG_LOG to true and WordPress appends every error to a file at wp-content/debug.log. Nothing shows on screen from this setting alone; the errors just accumulate in a file you can open whenever you like. Since WordPress 5.1 you can also give it a full file path instead of true, and the log writes there instead — handy for keeping the file outside a publicly reachable folder.

define( 'WP_DEBUG_LOG', true );
// or, to choose the location:
define( 'WP_DEBUG_LOG', '/home/you/private-logs/wp-errors.log' );

One catch worth knowing: WP_DEBUG_LOG only does anything while WP_DEBUG is also true. The log is a companion to the master switch, not a replacement for it.

Keep it off the front end: WP_DEBUG_DISPLAY

This is the setting that protects your visitors. WP_DEBUG_DISPLAY controls whether errors are printed into the HTML of the page, and it defaults to true. On a live site you want it false so that errors go to the log and nowhere else.

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

That four-line block is the production-safe pattern: debugging is on, everything is logged, and nothing surfaces to the public. The @ini_set line is a belt-and-suspenders addition that tells PHP itself not to echo errors, in case a plugin or your host has flipped that on somewhere else.

THE GOLDEN RULE

Never leave WP_DEBUG displaying errors on a production site. A visible warning can reveal your server’s absolute file paths, plugin internals, and database structure — a genuine reconnaissance gift to an attacker. Debug on a live site, sure, but log quietly: WP_DEBUG_DISPLAY stays false, and you turn the whole thing back off once you’re done.

Two More Switches Developers Reach For

SCRIPT_DEBUG

WordPress ships minified versions of its core CSS and JavaScript for speed. When you’re chasing a bug in a script — or in something that hooks into one — that minification makes the code nearly unreadable. SCRIPT_DEBUG defaults to false; set it true and WordPress loads the full, un-minified “dev” versions of its bundled assets instead.

define( 'SCRIPT_DEBUG', true );

It only affects core’s own files, not your theme or third-party plugins, but when the problem is in the block editor or a core library, it makes the source legible again.

SAVEQUERIES

If a page is slow and you suspect the database, SAVEQUERIES tells WordPress to record every query it runs — along with the time each took and the function that fired it — into the $wpdb->queries array. It’s a precise way to find the one plugin issuing five hundred queries on your homepage.

define( 'SAVEQUERIES', true );

Because it stores every query in memory, it carries a real performance cost of its own. Turn it on to investigate, read what you need, and turn it back off. Leaving it running on a busy production site is the kind of thing that quietly makes the slowness you were investigating worse.

THE CONSTANTS AT A GLANCE
  • WP_DEBUG — master switch; enables error, warning, and deprecation reporting. Default false.
  • WP_DEBUG_LOG — writes errors to wp-content/debug.log (or a path you specify). Needs WP_DEBUG on.
  • WP_DEBUG_DISPLAY — shows errors in the page HTML. Set false in production.
  • SCRIPT_DEBUG — loads un-minified core CSS/JS. Default false.
  • SAVEQUERIES — records every DB query for profiling. Costs memory; use briefly.

Reading the Log Without Losing Your Mind

Once logging is on, reproduce the problem — load the broken page, submit the failing form — and then open wp-content/debug.log. Each entry carries a timestamp, an error type, a message, and a path. A typical fatal looks like this:

[23-Jul-2026 03:14:52 UTC] PHP Fatal error:  Uncaught Error:
Call to undefined function acme_render_widget() in
/wp-content/plugins/acme-widgets/render.php:214

Read it from the file path backward. The line tells you the failing function, the file, and the exact line number — render.php at line 214, inside the acme-widgets plugin. That’s your suspect, no guesswork required. Fatal errors halt the request, so they’re what caused your white screen; warnings and notices don’t stop the page but often explain flaky, intermittent behavior.

Not every line is urgent. A live site with a few active plugins will log deprecation notices and minor warnings that have nothing to do with your problem. Scan for the word Fatal first, note the timestamp that matches when you reproduced the bug, and ignore the background noise. If the file is enormous, delete it, reproduce the issue once, and read the fresh, short log that results.

A safer default: move the log somewhere private

The default location, wp-content/debug.log, sits inside a folder your web server can serve. On many setups that file is reachable at yoursite.com/wp-content/debug.log by anyone who guesses the URL, and an error log can leak plenty about how your site is built. Either point WP_DEBUG_LOG at a path outside your web root, or block direct access to .log files at the server level. Cleaning up after yourself matters too: delete the log and switch debugging off when the investigation’s over.

Faster Iteration: WP-CLI and Query Monitor

Flip the switches from the command line

If you have shell access, WP-CLI edits wp-config.php for you without opening an editor. The --raw flag matters here — it writes an actual boolean instead of the string "true":

wp config set WP_DEBUG true --raw
wp config set WP_DEBUG_LOG true --raw
wp config set WP_DEBUG_DISPLAY false --raw

When you’re finished, wp config set WP_DEBUG false --raw turns it all off again. It’s quick enough that leaving debug mode on “just in case” stops being tempting.

Query Monitor turns the log into a dashboard

For anything beyond reading a text file, Query Monitor is the tool most WordPress developers keep installed. It’s a free, GPL-licensed developer panel that surfaces database queries, PHP errors, hooks, HTTP API calls, and slow queries right in your admin bar — no log-diving required. It reads far better than a raw file, and it shows you which plugin or theme owns each query and error. Keep it on staging; there’s no reason to run it on a live production site.

Turning It Off Is Part of the Job

Debugging is a temporary state, not a permanent configuration. Once you’ve found and fixed the problem, set WP_DEBUG back to false (or clear it with WP-CLI), and delete any debug.log you generated. Leaving a site in debug mode is the most common security slip we see in otherwise well-run installs — a logging file quietly filling with paths and stack traces, sitting in a folder anyone can reach. Fix, verify, then flip it off.


Frequently Asked Questions

Add your debugging constants anywhere above the line that reads “/* That’s all, stop editing! Happy publishing. */” in wp-config.php, which lives in your site’s root directory. WordPress finishes loading its settings at that comment, so any constant placed below it is ignored. If WP_DEBUG already exists in the file set to false, change that existing line rather than adding a second one.

By default it is created at wp-content/debug.log. If it is not there, check that both WP_DEBUG and WP_DEBUG_LOG are set to true, since the log only records anything when the master switch is also on. The file is only created once an error is actually logged, so reproduce the problem first, then look. If you pointed WP_DEBUG_LOG at a custom file path, the log is written there instead.

You can, but only if you also set WP_DEBUG_DISPLAY to false so errors are written to the log rather than printed on the page. Visible error output can expose server file paths, plugin internals, and other details useful to an attacker. The safe production pattern is WP_DEBUG true, WP_DEBUG_LOG true, WP_DEBUG_DISPLAY false — and then turn debugging off again once you have found the cause.

WP_DEBUG_LOG controls whether errors are written to a log file, while WP_DEBUG_DISPLAY controls whether they are printed into the page HTML for anyone viewing it. They are independent, so the usual approach is to enable logging and disable display: you get a full record to read privately without exposing anything to visitors. Both depend on WP_DEBUG being enabled first.

No. The core debugging constants — WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY, and the rest — are built into WordPress and require no plugin. A tool like the free Query Monitor plugin makes the same information much easier to read by surfacing queries, errors, and hooks in your admin bar, but it is an optional convenience layer on top of the logging that core already provides.

SCRIPT_DEBUG forces WordPress to load the full, un-minified “dev” versions of its bundled core CSS and JavaScript files instead of the compressed production versions. That makes the source readable when you are debugging something in the block editor or another core script. It affects only WordPress core’s own assets, not your theme or third-party plugins, and it defaults to false.

Next time your site breaks without explanation, don’t start guessing — turn on the log and let WordPress tell you exactly where it hurts. A few minutes with WP_DEBUG beats an afternoon of deactivating plugins one at a time.

Built by amplifi.studio — see also our guide to wp-config.php.