WordPress Error Logs: How to Enable Them
-
Last Updated On
WordPress error logs are critical troubleshooting files that record PHP errors, warnings, and notices affecting your website when debug mode is enabled. By default, WordPress hides these messages from public view to protect your website’s internal structure and security.
In other words, if your website is broken, the page is white, and nothing on screen tells you why, the error log is your answer. The error log is the answer. The catch is that WordPress ships with logging switched off, hides fatal errors behind a recovery page, and writes the log to a publicly accessible location that anyone can download, exposing sensitive site information.
This guide covers all three. You will enable logging without exposing it, get WordPress to show you the actual fatal error, and read a real stack trace to identify the plugin responsible.
The error log is actually four different files, and knowing which is which saves you from searching an empty file for an hour.
Records PHP notices, warnings, deprecations, and fatal errors generated by WordPress core, plugins, and your theme. When debugging is enabled, these messages are typically written to wp-content/debug.log, helping developers to identify and troubleshoot issues.
Your hosting environment’s server-level PHP log, configured through PHP and your hosting/server settings. You can often access it through cPanel or your hosting dashboard under the “Error Log” or “Logs” section. It can capture PHP errors that prevent WordPress from loading properly, such as syntax errors, fatal errors, and memory-limit exhaustion.
The web server’s own log, typically from Apache or Nginx. It records server-level problems such as 500 errors, 502 Bad Gateway errors, connection failures, and timeouts, including issues that occur before or while the request reaches PHP. If the WordPress debug log is empty, but the site still returns server errors, this is the next place to check.
After detecting a fatal PHP error, WordPress emails the site admin with details about the error and a Recovery Mode link. The email may identify the affected plugin or theme and include the error message, file, and line number. It helps identify and recover from fatal errors, although PHP or server error logs may offer more complete diagnostic details.
Edit wp-config.php in your site root, adding the following line:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/username/logs/wp-debug.log' );
define( 'WP_DEBUG_DISPLAY', false );
Because placement matters, add these definitions before the following line, which is near the bottom of wp-config.php:
require_once ABSPATH . 'wp-settings.php';
Any code added below that line runs after WordPress has already configured error handling. WordPress will ignore these constants if placed incorrectly, which is why users often enable logging but see no results.
The following is what each code does:
true for any other debug constants to work. If you set WP_DEBUG_LOG to true instead of a file path, WordPress saves the log to wp-content/debug.log – inside your public webroot. This means anyone can read yoursite.com/wp-content/debug.log in their browser.
That file contains absolute server paths, your directory structure, the plugins you run and their versions, and sometimes fragments of database queries. It is a free reconnaissance document for anyone probing your site.
You can handle it in two ways, but the better one is what the snippet above does: set WP_DEBUG_LOG to a path outside the webroot. On most shared hosts, a logs directory sits alongside public_html, so /home/username/logs/wp-debug.log works. Check that the path exists and is writable before you rely on it.
If your host will not let you write outside the webroot, block access at the server. Apache, in the .htaccess at your site root:
<Files "debug.log">
Require all denied
</Files>
Nginx, in your server block:
location ~* /wp-content/debug\.log$ {
deny all;
}
Test it by loading the URL in a browser. You want a 403, not a file download.
Both WP Debugging and Query Monitor toggle these constants from the admin area without you touching wp-config.php. That is reasonable if you don’t have file access.
Two things you should know: First, they write the same constants, so all the placement and exposure caveats above still apply, including the default log location. Second, Query Monitor offers real-time debugging features that log files cannot provide. It profiles database queries, tracks hook execution, and identifies which specific plugin triggered each error, all visible in your admin bar.
The important part, which most guides leave out, may be one of the reasons why people follow every step correctly and still find nothing.
WordPress has had a fatal error handler since 5.2. When a plugin causes a fatal error, WordPress catches it, shows visitors ‘there has been a critical error on this website’, emails the admin a recovery link, and pauses the offending plugin. This feature keeps your site accessible to visitors, but it interferes with debugging. When the handler catches an error, it prevents the full error message from appearing, meaning you can’t see what actually broke.
While you are debugging, switch it off:
define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true );
After this, fatal error behave the way it should. You will get a white screen, and the full error with its stack trace will go into your log.
Turn this back off when you are done. The handler exists for a reason, and you want it running on a live site.
Note the following two things:
Follow the following steps to find and open the log file:
The log only records what happens while logging is on. Load the page that breaks, or repeat the action that fails, and note the time to the minute. The timestamp is how you find your entry in a file with a thousand lines.
SFTP with FileZilla, or your host’s File Manager in cPanel or Plesk; both will work. SFTP is faster after having saved the connection.
Navigate to wherever you pointed WP_DEBUG_LOG. Download the file and open it in a plain text editor, such as Notepad, VS Code, or TextEdit, but not Word.
You will find the newest entries at the bottom. Scroll to the end and work upward to your timestamp.
If you have SSH access, skip the download:
tail -n 50 /home/username/logs/wp-debug.log
Or watch it live while you reproduce the error in another window:
tail -f /home/username/logs/wp-debug.log
Need Help Troubleshooting WordPress Issues?
Check out our Top WordPress Development Companies.
Here is a real fatal error, the kind that takes a site down:
PHP Fatal error: Uncaught Error: Call to undefined function wc_get_product() in /home/username/public_html/wp-content/plugins/custom-orders/includes/class-order-sync.php:88
Stack trace:
#0 /home/username/public_html/wp-includes/class-wp-hook.php(324): CustomOrders\OrderSync->sync_order(1420)
#1 /home/username/public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array)
#2 /home/username/public_html/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#3 /home/username/public_html/wp-admin/post.php(230): do_action('save_post', 1420)
#4 {main}
thrown in /home/username/public_html/wp-content/plugins/custom-orders/includes/class-order-sync.php on line 88
Read it in this order:
The error thrown in class-order-sync.php on line 88 is where the error actually happened. That path includes plugins/custom-orders, so the Custom Orders plugin is the culprit. That single detail is the answer to ‘which plugin broke my site,’ and that’s why this skill is worth ten minutes.
‘Call to undefined function wc_get_product()’ means the code called a WooCommerce function that did not exist at that moment. Almost always, one of three things is happening:
Read #4 upward, and you get the chain of events. The request hit wp-admin/post.php, which fired the save_post action, which ran through WordPress’s hook system, which called OrderSync->sync_order() on post 1420. So the error only fires when someone saves a post, which tells you exactly how to reproduce it.
Understanding the difference between notice, warning, deprecated, and fatal errors in your WordPress error log is the key to maintaining a healthy website. These entries represent different levels of PHP severity, telling you whether your website has minor informational issues or catastrophic crashes.
However, among the four severity levels, only one is an emergency.
PHP Warning: Undefined array key “post_type” in /home/username/public_html/wp-content/themes/mytheme/functions.php on line 47
A warning means the code did something wrong but carried on; this one is an undefined array key. You will notice it before PHP 8, and it got promoted to a Warning, which is why upgrading PHP suddenly fills a lot of logs. A warning is worth fixing, but not on an urgent basis.
PHP Deprecated: Automatic conversion of false to array is deprecated in /home/username/public_html/wp-content/plugins/old-slider/slider.php on line 212
Deprecated means it works today but might break in a future PHP upgrade. This is an early warning list before a PHP upgrade and is not a live issue. Deprecations from a plugin that hasn’t been updated in three years are also a strong signal to replace it.
Notice is informational. On a busy website, these make up most of your log and are the core reason it grows quickly.
A fatal error is a critical severity level; you will get a white screen, and you should always consider fatal errors to fix first.
Common Log Entries and What They Mean
| Log entry | What is happening | Where to look |
|---|---|---|
| Allowed memory size of X bytes exhausted | PHP ran out of memory | Raise WP_MEMORY_LIMIT, then find what is consuming it |
| Maximum execution time of 30 seconds exceeded | A script ran too long | Usually an import, cron job, or external API call with no timeout |
| Call to undefined function | A function was called before its plugin loaded, or it was removed | The file path in the trace names the plugin at fault |
| Cannot modify header information – headers already sent | Output before headers, often a blank line after ?> in a file | The trace gives you the file and line to clean up |
| Uncaught mysqli_sql_exception | Database query failed | Often a missing table after a failed plugin update |
| Undefined array key | Code assumed a value that was not there | Harmless individually, worth fixing in your own theme |
| Deprecated: entries in bulk | Plugin code predates your PHP version | Update the plugin, or plan to replace it |
| Failed to open stream: Permission denied | PHP cannot read or write a file | File ownership and permissions on the named path |
Now you have enabled everything, but the file is not there, or it is there but completely blank. Work through these in a particular order.
WordPress does not rotate the debug log; it appends forever. One plugin throwing a deprecation notice on every page load will produce a file measured in gigabytes, and on a shared host that means hitting your disk quota and taking the site offline.
Truncate rather than delete, so the file handle and permissions stay intact:
truncate -s 0 /home/username/logs/wp-debug.log
Without SSH, open the file in your File Manager’s editor, select all, delete, save.
It’s best not to leave logging on. Enable it, reproduce the problem, read the log, turn it off. If you need continuous logging on a busy site, set up rotation with logrotate or use a plugin that caps the file size.
Set the constants back: define( ‘WP_DEBUG’, false );
You can leave the other lines in place, since they do nothing while WP_DEBUG is false, or comment them out to keep the file tidy. Now, remove WP_DISABLE_FATAL_ERROR_HANDLER completely, so the recovery handler protects your website again.
If you are using a plugin, toggle debugging off there and deactivate the plugin if you won’t use it again. Then delete or truncate the log file, especially if it ever sat in wp-content.
What we discussed so far works on a live site, and often you have no choice because the bug only happens with real traffic and data. If you do have a choice, debug on a staging copy.
The reason is not caution for its own sake. WP_DEBUG changes behavior, not just reporting. It turns off some caching, and if WP_DEBUG_DISPLAY is ever wrong, you leak file paths to visitors. Both are fine on a staging URL nobody is buying from.
Take a backup before you edit wp-config.php either way. A stray character in that file takes the entire site down, admin included, and the fix requires SFTP access.
By default, the WordPress error log is located in the /wp-content/ directory of your website’s root folder, and the file is named debug.log.
WordPress does not have built-in audit logs by default, as core WordPress only tracks basic content revisions. An audit log showing who logged in, who changed a setting, and who edited a post requires a plugin such as WP Activity Logs or Simple History.
You can truncate the file rather than deleting it. Over SSH: truncate -s 0 wp-content/debug.log. Without SSH, open it in your host’s file manager editor, select the file, delete it, and save. WordPress will keep writing to the same file.
Your debug.log file is often empty because the constants sit below the require_once ABSPATH. ‘wp-settings.php’; line in wp-config.php, where they are ignored. Another reason is the caching layer serving the page, so your PHP never runs; an unwritable log directory; or a fatal error being absorbed by WordPress’s error handler.
No, it is not safe because anyone can download wp-content/debug.log unless you block it, and it exposes your server paths and plugin inventory. The log also grows without limit, so a plugin can fill your disk quota and take the site down. Enable it to solve a problem, then turn it off.
Read the file path in the log entry. A line ending in …/wp-content/plugins/plugin-name/some-file.php on line 88 names the plugin directly. If the trace only shows core files, the problem is likely in your theme’s functions.php or in a plugin hooking into that core function, and Query Monitor will attribute it for you.