Skip to content
Configuration

WordPress Debug Log Location: Find and Move debug.log

Find the WordPress debug log location, move debug.log outside the web root so visitors cannot fetch it, and fix the WP_DEBUG settings that leave it empty.

Published Updated

WordPress writes its debug log to wp-content/debug.log by default — an absolute path of /path/to/your/site/wp-content/debug.log on disk. The file does not exist until something is actually logged, and it only gets written at all when WP_DEBUG is true and WP_DEBUG_LOG is enabled in wp-config.php. That default location is also a real security problem, because on most hosts anyone can fetch it in a browser.

TL;DR: turn on logging with the three constants below, read the file at wp-content/debug.log (newest entries at the bottom), then either move it outside the web root with a file path or block direct requests to it in your server config — and turn WP_DEBUG back off once you have what you need, because nothing in WordPress rotates or caps that file’s size.

The three constants and what each one does

All three go in wp-config.php, above the line that reads /* That's all, stop editing! Happy publishing. */ (older installs say “Happy blogging”). Anything added below that line runs after WordPress has already booted and is ignored. If you haven’t opened this file before, the wp-config.php guide shows where it is and how to edit it safely.

ConstantDefaultWhat it does
WP_DEBUGfalseMaster switch. Raises PHP error reporting to show notices, warnings and deprecations. Nothing else on this list works without it.
WP_DEBUG_LOGfalseSends errors to a file. true means wp-content/debug.log. A string is treated as the file path to write to.
WP_DEBUG_DISPLAYtruePrints errors into the page HTML, visible to every visitor.

The dependency is the part people get wrong. WP_DEBUG_LOG is read inside the WP_DEBUG branch of WordPress’s startup code. If WP_DEBUG is false, setting WP_DEBUG_LOG to true does nothing at all — no file, no error, no warning. If you have added WP_DEBUG_LOG and no log appeared, check WP_DEBUG first. For a full walkthrough of turning WordPress debug mode on and off, including SCRIPT_DEBUG and SAVEQUERIES, see the WP_DEBUG guide.

A working live-site configuration:

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

If WP_DEBUG is already defined further up the file — it usually is, set to false — edit that existing line rather than adding a second define(). Defining the same constant twice throws a PHP warning, which on a site with display enabled lands right at the top of your page.

Why WP_DEBUG_DISPLAY must be false in production

With display on, PHP errors are echoed into the HTML response. A single deprecation notice from a theme prints your server’s absolute filesystem path to every visitor. A database error prints table names and query fragments. Worse, output that appears before headers are sent breaks redirects, breaks REST and AJAX responses that expect clean JSON, and can produce the white screen people then blame on a plugin.

One subtlety worth knowing: setting WP_DEBUG_DISPLAY to null is not the same as false. null tells WordPress to leave PHP’s display_errors setting alone and inherit whatever the server has configured. false actively forces it off. On a production site, use false and add the ini_set line above as a belt-and-braces measure, since a plugin can flip display_errors back on later in the request.

The default location is publicly fetchable

wp-content/debug.log sits inside the web root. Nothing in a stock WordPress install blocks direct requests to it. On a typical Apache or nginx setup, https://yoursite.com/wp-content/debug.log returns the file as plain text. Debug logs contain absolute server paths, plugin and theme internals, database errors with real query text, and sometimes values passed through failing functions. That is free reconnaissance for anyone who guesses the URL — and it is one of the first URLs automated scanners try.

Two ways to deal with it. The better one is to move the log.

Move the log with a path

Since WordPress 5.1, WP_DEBUG_LOG accepts a string file path instead of a boolean. Point it somewhere outside the document root:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/youruser/logs/wp-errors.log' );
define( 'WP_DEBUG_DISPLAY', false );

Three rules for that path. Use an absolute path — a relative one is resolved against PHP’s working directory, which varies by request and by server, so you will lose track of where the file went. The directory must already exist; PHP will not create it, and if it cannot open the file it fails silently. And the directory must be writable by the PHP user, which on shared hosting is not the same account you log in with over SFTP.

If your hosting confines you to the web root and there is nowhere above it to write, keep the default path and block access at the server level instead. Apache:

<Files "debug.log">
  Require all denied
</Files>

That goes in a .htaccess file inside wp-content, not the root one — WordPress rewrites the root .htaccess when you save permalinks and can wipe your addition. On nginx, .htaccess does nothing at all; you need a location block in the server config, which on managed hosting usually means asking support.

Be honest about what blocking gets you: the file is still on disk in a directory the web server serves from. A path-traversal bug in any plugin can still read it. Moving it out of the web root is the stronger fix.

Reading and tailing the file

Over SSH, watch it live while you reproduce the bug:

tail -f wp-content/debug.log

Reproduce the error in another tab and the new lines appear as they are written. To see only what has happened recently, or to filter to one plugin:

tail -n 100 wp-content/debug.log
grep -i 'plugin-slug' wp-content/debug.log | tail -n 50

PHP appends to the end of the file, so read from the bottom up. Each entry is timestamped in UTC, which will not match your WordPress timezone setting — do not let that make you think you are looking at old entries.

Contents of debug.log with a timestamped PHP fatal error, a stack trace, and the file and line where the error was thrown
One fatal error in our test log: the UTC timestamp, the error, the stack trace, then the file and line.

With WP-CLI you can toggle the constants without editing the file by hand:

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 — without it the value is written as the string "true" rather than the boolean. For the path form, drop --raw so it is written as a quoted string.

No SSH? Download the file over SFTP or open it in your host’s file manager. Avoid plugins that surface the log inside wp-admin unless you trust them completely; you are handing a plugin read access to a file full of server internals.

If you want the exact wp-config block for your setup, including a safe out-of-root path and the matching server rule, the WordPress debug log generator will build it for you.

Turn it off when you are done

Nothing in WordPress core rotates or truncates debug.log. On a site throwing a notice on every page load, it grows without limit and can fill the disk — which takes the site down harder than the bug you were chasing. Enable logging, reproduce the problem, read the log, then set WP_DEBUG back to false.

To clear the file without deleting it:

: > wp-content/debug.log

A worked example: tracing a white screen back to one plugin

Say a client site goes to a blank white page right after a plugin update, with nothing in the browser to go on. Here is the sequence that actually finds the cause, in order:

  1. Turn logging on without turning display on. Edit wp-config.php with the block from above: WP_DEBUG true, WP_DEBUG_LOG true, WP_DEBUG_DISPLAY false. A white screen with display already on and still no visible error usually means the fatal happened before WordPress could print anything, which is itself a clue — see step 4.
  2. Reload the broken page once. A fatal error only gets written when it happens, so load the exact URL that showed the white screen. Loading the homepage instead, when the fatal only happens on checkout, writes nothing and wastes a step.
  3. Open wp-content/debug.log and read from the bottom. PHP appends, so the newest entry — the one from the reload just now — is the last line, not the first. Look for PHP Fatal error, note the file path in the message, and check whether that path sits inside wp-content/plugins/<the-plugin-you-just-updated>.
  4. If the file is still empty, the error happened before WordPress turned logging on. WordPress switches the log on before it loads any plugin, so an error in a plugin file, even a parse error, does reach debug.log. What it cannot catch is an error in wp-config.php itself, such as a missing semicolon, because that file runs before WordPress starts. Those go to the server’s own PHP error log instead (its path is set in php.ini or your host’s panel, not in wp-config.php), so check there next rather than assuming logging is broken. If wp-config.php is fine too, work through the checklist in the next section. (We tested this on 2026-11-10 with WordPress 7.1.4 and PHP 8.3 in WordPress Playground: a parse error in a normal plugin and one in a must-use plugin were both written to debug.log as PHP Parse error; a parse error in wp-config.php was not.)
  5. Deactivate just that plugin (via WP-CLI or SFTP-renaming its folder if wp-admin itself is the white screen) and reload. If the page comes back, the stack trace from step 3 told the truth; if it does not, keep reading up the log for an earlier entry, since one fatal can mask another.

Nothing here depends on a specific host or plugin — it is the same five steps for any “site went blank” report, and it is the reason WP_DEBUG_DISPLAY: false with logging on is the safer default than leaving display on: step 1 above only works at all because the error was captured somewhere instead of just vanishing into a blank page.

When the log stays empty

Work through these in order. WP_DEBUG is false — the single most common cause, and it silently disables everything else. The constant is below the “stop editing” line in wp-config.php. File permissions — PHP cannot write to wp-content, or to the custom directory you specified. A fatal error before wp-config finishes loading, such as a syntax error in the config file itself, happens too early for WordPress logging to catch; those go to the server’s own PHP error log instead. Your host overrides it — some managed platforms pin PHP’s error_log setting or route logging into their own dashboard, in which case your constants are set correctly and the output is simply somewhere else. Check the host’s log viewer before assuming your config is broken.

Sources

William Lopez

William Lopez is the founder of Odie Themes. He builds the free WordPress tools and themes on this site and has been on WordPress.org since December 2019.

WordPress.org About

FAQ

Questions

Where is the WordPress debug log located?

By default it is wp-content/debug.log, directly inside your wp-content folder. WordPress only creates the file once an error is actually logged, so an empty wp-content folder does not mean logging is broken. If WP_DEBUG_LOG was given a file path instead of true, the log is written to that path instead.

Why is there no debug.log file in wp-content?

Three common reasons. WP_DEBUG is false, so WordPress never switches logging on regardless of WP_DEBUG_LOG. Nothing has errored yet, so the file has not been created. Or PHP cannot write to wp-content because of file permissions. Check the constants first, then permissions.

Can anyone download my WordPress debug.log?

Usually yes. wp-content/debug.log sits inside the web root with no access rules protecting it, so a browser request to that URL often returns the file. Debug logs routinely contain absolute server paths, database errors, and query fragments. Move the log outside the web root or block it in your server config.

What is the difference between WP_DEBUG_LOG and WP_DEBUG_DISPLAY?

WP_DEBUG_LOG writes errors to a file. WP_DEBUG_DISPLAY prints them into the page HTML where visitors can read them. On a live site you want logging on and display off. Both are ignored entirely unless WP_DEBUG is set to true, which is the step most people miss.

How do I read the WordPress debug log?

Over SSH, run tail -f on the log path to watch new entries appear live while you reproduce the problem. Without SSH, download the file over SFTP or open it in your host's file manager. Read from the bottom up, since PHP appends newest entries to the end of the file.

Does enabling WP_DEBUG slow down a live site?

Slightly, and the bigger risk is disk. Verbose logging on a busy site can grow debug.log to gigabytes and fill the disk, which takes the site down. Enable it, reproduce the bug, read the log, then turn it back off. Nothing in WordPress core rotates or truncates that file for you.