
Something is misbehaving on a production site and you need real error output to find it. The instinct is to flip on WP_DEBUG and refresh the page. Do that on a live site and you risk showing visitors (and search bots, and anyone poking at your source) file paths, plugin names, database details, and deprecation notices right there on the screen. The fix is simple: log errors to a file instead of displaying them, read that file properly, and clean up afterwards so nothing lingers.
What WP_DEBUG actually controls
WordPress debugging is really three separate switches, and conflating them is where most live-site mishaps come from:
- WP_DEBUG turns on WordPress’s internal debugging mode, which surfaces PHP notices, warnings, and deprecation messages that are normally suppressed.
- WP_DEBUG_LOG tells WordPress to write those messages to a log file instead of (or in addition to) showing them.
- WP_DEBUG_DISPLAY controls whether debug output is printed on the page itself. This is the one that leaks information to visitors when left on.
The combination you want on a live site is WP_DEBUG on, WP_DEBUG_LOG on, and WP_DEBUG_DISPLAY off. That gives you full logging with nothing visible in the browser.
Setting it up safely
Open wp-config.php and add these lines above the line that reads /* That's all, stop editing! Happy publishing. */:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );
The ini_set line is a belt-and-braces move. Some plugins or misconfigured PHP setups will print errors regardless of WordPress’s own display setting, so forcing display_errors off at the PHP level closes that gap.
If you’d rather not hand-edit the file, wp-cli does the same job and is easier to reverse cleanly:
wp config set WP_DEBUG true --raw
wp config set WP_DEBUG_LOG true --raw
wp config set WP_DEBUG_DISPLAY false --raw
The --raw flag matters here. Without it, wp-cli writes the value as a quoted string, and WP_DEBUG_LOG in particular has special meaning when it’s a real boolean versus a string.
Choosing where the log goes
By default, setting WP_DEBUG_LOG to true writes to wp-content/debug.log. That file sits inside a directory that’s often web-accessible, which means anyone who guesses the URL can read your error output too. Two ways to handle that:
- Give the constant a custom absolute path outside the web root, e.g.
define( 'WP_DEBUG_LOG', '/home/yourapp/logs/wp-debug.log' );instead oftrue. WordPress will write there instead. - If you keep the default location, add a rule to block direct access to
debug.logat the server level (an.htaccessdeny rule on Apache/LiteSpeed, or an equivalent location block if you’re on Nginx). Many managed hosts already block dotfiles and log files by default; check before assuming.
Either way, treat the log as sensitive. It can contain file paths, query fragments, and occasionally data that reveals more about your setup than you’d want public.
Reading the log without guesswork
Once traffic starts hitting the site, the log fills up fast. Tail it live from the command line so you see new entries as they happen:
tail -f wp-content/debug.logTo reproduce a specific bug, trigger the action (load the page, submit the form, run the cron job) and watch what lands in the terminal at that moment. If the log is large and noisy, grep for the plugin or file you suspect:
grep -i "my-plugin" wp-content/debug.logYou can also confirm your current debug settings without opening the config file at all:
wp config get WP_DEBUG
wp config get WP_DEBUG_LOG
wp config get WP_DEBUG_DISPLAYThis is worth doing before you start, since a plugin or a previous debugging session may have already changed these values.
A wrinkle with OPcache
If the site runs PHP 8.3 with OPcache (a solid default for performance), and opcache.validate_timestamps is disabled for extra speed, PHP may keep serving a cached compiled version of wp-config.php instead of noticing your edit. If you flip the constants and nothing seems to change, that’s the likely cause. Restarting PHP-FPM, or clearing OPcache through your hosting control panel, forces it to re-read the file. wp-cli’s config set commands don’t get around this either, since the underlying file still has to be re-parsed by PHP.
Turning it all off cleanly
Once you’ve found and fixed the issue, don’t leave debugging running. A live site accumulating a debug log indefinitely is a slow leak of disk space and a standing security consideration, even if display is off. Reverse the settings:
wp config set WP_DEBUG false --raw
wp config set WP_DEBUG_LOG false --raw
wp config set WP_DEBUG_DISPLAY false --rawThen remove the log file itself so old entries aren’t sitting around waiting to be read by someone else:
rm wp-content/debug.logIf you set a custom log path outside the web root, delete that file too, and remove the custom WP_DEBUG_LOG path constant from wp-config.php if you no longer need it. Circle back and confirm the ini_set line for display_errors is either removed or harmless with debugging off; it doesn’t hurt to leave it, but tidy config files are easier to reason about six months from now.
Some managed hosts, ServerBorn included, offer centralized logging or error visibility through the hosting panel, which sidesteps the need to hand-edit constants for routine troubleshooting. Worth checking what your host already exposes before reaching for wp-config.php directly.
Takeaway
Debugging in production is sometimes unavoidable, but it doesn’t have to mean airing your PHP internals to every visitor. Log to file, keep display off, protect the log location, read it deliberately with tools built for the job, and turn everything back off (log file included) the moment you’re done. That’s the difference between a quick diagnostic session and an accidental information leak.