In this short post, we’ll see what the correct way is to monitor PHP errors on WordPress sites using WP_DEBUG_LOG.
Like most things related to WordPress development, the best way to understand a specific function is to search for it in WordPress core files and look at the code.
Here is the code for the wp_debug_mode function. The important and relevant part appears in wp-include/load.php:
function wp_debug_mode() {
if ( WP_DEBUG ) {
error_reporting( E_ALL );
if ( WP_DEBUG_DISPLAY )
ini_set( 'display_errors', 1 );
elseif ( null !== WP_DEBUG_DISPLAY )
ini_set( 'display_errors', 0 );
if ( in_array( strtolower( (string) WP_DEBUG_LOG ), array( 'true', '1' ), true ) ) {
$log_path = WP_CONTENT_DIR . '/debug.log';
} elseif ( is_string( WP_DEBUG_LOG ) ) {
$log_path = WP_DEBUG_LOG;
}
if ( isset( $log_path ) ) {
ini_set( 'log_errors', 1 );
ini_set( 'error_log', $log_path );
}
} else {
error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR );
}
if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || wp_doing_ajax() || wp_is_json_request() ) {
ini_set( 'display_errors', 0 );
}
}
In essence, if you set WP_DEBUG_LOG to true, WP_DEBUG_DISPLAY to false, and WP_DEBUG is also true, all PHP errors will be recorded in the debug.log file created in the wp-content directory but will not be displayed in the browser.
Since WordPress 5.1, you can also pass a custom file path to WP_DEBUG_LOG instead of just true. This lets you store the log file outside the web root for better security.
To configure this, use the following code:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );
@ini_set( 'display_errors', 0 );Make sure to place these constants in your wp-config.php file above the line that says /* That's all, stop editing! */. Never enable WP_DEBUG on production sites.
But why do this? Here are three reasons why it’s preferable to use WP_DEBUG_LOG over WP_DEBUG:
Hidden Error Messages
You’re used to seeing PHP error messages in the browser, but are you sure you see all of them? What about elements hidden in CSS and AJAX calls?
The user interface is not a reliable place to catch all error messages. The PHP log file, on the other hand, shows all of them, and you won’t miss any.
Missing Error Messages
Maybe you like to use plugins like Debug Bar or Query Monitor to monitor and receive error messages, and you’re very satisfied with them.
They are great plugins. However, I’ve encountered situations where they miss errors.
This may happen because these plugins are ultimately plugins and can be affected by other plugins loading before them.
You can change the order of plugin loading using Plugin Organizer, but what about plugins in the MU plugins directory or errors in WordPress core files themselves?
As convenient as these plugins are, relying on them alone to detect all PHP errors on your WordPress site is a mistake.
PHP itself is the most reliable source for error reporting, and you’ll receive it through the debug.log file.
Third-Party Plugins
If your code is problematic and throws errors, you can fix that. But what if external plugins you installed throw errors and comments on your entire interface?
When WP_DEBUG is active, it displays all errors without exception.
It can be very difficult to work when error messages and comments flood your entire screen, especially when certain plugins are active.
At this point, you’ve added those lines to wp-config.php and you’re monitoring errors through the debug.log file. Here’s an example of how errors look in this file:
[18-Dec-2017 09:18:08 UTC] PHP Fatal error: Call to undefined function get_slocale() in /Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/includes/home.php on line 9
[18-Dec-2017 09:18:08 UTC] PHP Stack trace:
[18-Dec-2017 09:18:08 UTC] PHP 1. {main}() /Users/roeeyossef/Sites/startapp/index.php:0
[18-Dec-2017 09:18:08 UTC] PHP 2. require() /Users/roeeyossef/Sites/startapp/index.php:17
[18-Dec-2017 09:18:08 UTC] PHP 3. require_once() /Users/roeeyossef/Sites/startapp/wp-blog-header.php:19
[18-Dec-2017 09:18:08 UTC] PHP 4. apply_filters() /Users/roeeyossef/Sites/startapp/wp-includes/template-loader.php:73
[18-Dec-2017 09:18:08 UTC] PHP 5. WP_Hook->apply_filters() /Users/roeeyossef/Sites/startapp/wp-includes/plugin.php:203
[18-Dec-2017 09:18:08 UTC] PHP 6. call_user_func_array:{/Users/roeeyossef/Sites/startapp/wp-includes/class-wp-hook.php:286}() /Users/roeeyossef/Sites/startapp/wp-includes/class-wp-hook.php:286
[18-Dec-2017 09:18:08 UTC] PHP 7. thesis_skin->_skin() /Users/roeeyossef/Sites/startapp/wp-includes/class-wp-hook.php:286
[18-Dec-2017 09:18:08 UTC] PHP 8. thesis_skin->_template() /Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/lib/core/skin.php:1003
[18-Dec-2017 09:18:08 UTC] PHP 9. thesis_html_body->html() /Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/lib/core/skin.php:1065
[18-Dec-2017 09:18:08 UTC] PHP 10. thesis_box->rotator() /Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/lib/core/skin/boxes.php:604
[18-Dec-2017 09:18:08 UTC] PHP 11. call_user_func_array:{/Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/lib/core/box.php:389}() /Users/roeeyossef/Sites/startapp/wp-content/themes/thesis/lib/core/box.php:389
The strange file path is due to the use of a local WordPress environment.
Custom Log File Path
Since WordPress 5.1, you can pass a custom file path to WP_DEBUG_LOG instead of just true:
define( 'WP_DEBUG_LOG', '/path/outside/webroot/debug.log' );This is useful for storing the log file outside the publicly accessible web root, which is a security best practice.
Additional Debug Constants
Beyond the three core constants, WordPress offers two more that are useful during development:
SCRIPT_DEBUG forces WordPress to use the development (non-minified) versions of core CSS and JavaScript files:
define( 'SCRIPT_DEBUG', true );This is helpful when you need to debug or modify core scripts and styles.
SAVEQUERIES saves all database queries, along with execution time and the calling function, into a global array:
define( 'SAVEQUERIES', true );You can then inspect the queries using $wpdb->queries. Note that this has a performance impact and should only be used during development.
Securing the debug.log File
By default, the debug.log file is created inside the wp-content directory and is publicly accessible. Anyone who knows the URL can read it, potentially exposing sensitive information like file paths, database queries, and plugin details.
Always protect your debug.log file. Either store it outside the web root using a custom path, or block access to it via your .htaccess file or server configuration.
To block access via .htaccess, add the following rule to the .htaccess file in your wp-content directory:
<Files debug.log>
Order allow,deny
Deny from all
</Files>For more WordPress security tips, check out our dedicated guide.
You can also prevent PHP errors from displaying entirely on your production site.
FAQs
Common questions about WP_DEBUG_LOG:
Summary
Using WP_DEBUG_LOG instead of relying on WP_DEBUG alone is a best practice for WordPress development. It captures all PHP errors – including those hidden in AJAX calls, REST API responses, and early plugin loading – without cluttering your browser.
Combine it with WP_DEBUG_DISPLAY set to false, and you get a clean development experience with a complete error record. Remember to protect the log file from public access and never leave debug mode enabled on production sites.
This post was translated from a post on deliciousbrains.com

