Nine times out of ten, the WordPress white screen of death is one thing: a PHP fatal error that WordPress can’t show you because display_errors is off. The remaining 10% is OPcache, Nginx timeouts, or a host-side circuit breaker. That’s the entire diagnostic surface. You don’t need to check 47 things. You need to get the error message, read it, and fix the file it points at.
Every other WSOD guide on the internet will walk you through backing up your database, clearing your cache, reinstalling WordPress core, deactivating every plugin, switching to Twenty Twenty-Five, and checking your memory limit — in that order, as if they’re all equally likely. They’re not. You’re wasting time on the first five steps when the log file is two commands away.
This is triage. We’re going to rank the causes by real-world frequency, teach you the one skill that solves most WSODs (reading a PHP stack trace), and give you the commands to run right now — whether you can reach the dashboard or not.
60 seconds: is it actually a white screen?
Before you do anything, confirm what you’re looking at. Modern WordPress (5.2+) replaced the literal white screen with a “There has been a critical error on this website” message for most errors. If you’re seeing that text, you are not looking at a classic WSOD — you are looking at Recovery Mode, and there is probably an email waiting for you. Check it before doing anything else.
If the page is genuinely blank — View Source shows an empty HTML shell or nothing at all — that’s a true WSOD. It means PHP died before WordPress could render its own error template. This is the scenario we’re solving here.
One more thing to rule out in 30 seconds: open a terminal and curl -I https://yoursite.com. What HTTP status code comes back?
- 500 — PHP fatal error. Keep reading.
- 504 — Nginx/Apache gateway timeout. This is not a WSOD. Your PHP process is hanging or deadlocked. Different problem, different fix (usually a long-running query, a stuck cron, or an infinite loop).
- 503 — Your host or a plugin put the site in maintenance mode. Check for a .maintenance file in the webroot and delete it.
- 200 with empty body — This is the nastiest case. PHP returned successfully but output nothing. Usually means the error happened before the first byte and error output is suppressed. We’ll catch this in the next step.
Knowing whether it’s a 500 or a 504 saves you from doing PHP debugging on a database problem. Always check.
Get the actual error message (fastest path first)
Everything that follows depends on seeing the error. Here are three ways to get it, ordered from fastest to most invasive.
Method 1: Read the existing debug log (10 seconds)
If WordPress was already configured with debug logging, the error is already on disk:
ssh user@yourserver.com
cd /path/to/wordpress
tail -100 wp-content/debug.log
If you see a recent stack trace — done, jump to the next section. If the file doesn’t exist or is empty, move on.
Method 2: Enable debug logging (2 minutes)
Edit wp-config.php via SSH or FTP and add these four lines above the /* That’s all, stop editing! */ comment:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );
WP_DEBUG_DISPLAY is intentionally false. You do not want the error rendering on the live site where customers can see your file paths and database names. You want it in the log file only.
Reload the page that was showing the white screen. Then:
tail -50 wp-content/debug.log
You’ll see something like this:
[11-Apr-2026 14:23:45 UTC] PHP Fatal error: Uncaught Error: Call to undefined function wpforms_is_admin_page() in /path/wp-content/plugins/broken-addon/includes/class-admin.php:42
Stack trace:
#0 /path/wp-includes/class-wp-hook.php(308): Broken_Admin->init('')
#1 /path/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters(NULL, Array)
#2 /path/wp-includes/plugin.php(517): WP_Hook->do_action(Array)
#3 /path/wp-settings.php(559): do_action('init')
That is the answer. The file, the line, the function. You do not need to guess what plugin is broken — the stack trace literally names it.
Method 3: PHP error log (fallback)
If WordPress can’t even start far enough to write its own log (rare but it happens with a syntax error in wp-config.php or a broken mu-plugin), check PHP’s own error log:
# Location varies by host. Common paths:
tail -100 /var/log/php8.3-fpm.log
tail -100 /var/log/nginx/error.log
tail -100 ~/logs/error_log # cPanel
Managed hosts expose this in the control panel. Kinsta’s MyKinsta dashboard has a “Logs” tab. WP Engine’s User Portal has one too. Use them — they’re faster than SSH.
Read the stack trace (the 90% skill)
A PHP stack trace has three pieces of information and you only need the first two.
- The error message — tells you what went wrong
- The file and line — tells you where
- The call stack — tells you how you got there (useful for complex cases, ignore for now)
Here are the four stack-trace patterns you’ll see most often and what each one means:
Pattern A: “Uncaught Error: Call to undefined function X() in /wp-content/plugins/some-plugin/…”
Translation: Some plugin tried to call a function from another plugin that wasn’t loaded (usually because of plugin load order or a missing dependency). The plugin named in the path is the culprit. Deactivate it.
Pattern B: “Allowed memory size of X bytes exhausted”
Translation: PHP ran out of memory. Either a legitimately heavy operation (a big import) or a runaway loop in a plugin. Bump WP_MEMORY_LIMIT to 256M in wp-config as a short-term fix, then find what’s eating the RAM.
Pattern C: “Parse error: syntax error, unexpected…”
Translation: Somebody edited PHP through the dashboard editor or SFTP and made a typo. The file path in the error is the file you need to revert. This is why define( ‘DISALLOW_FILE_EDIT’, true ); exists.
Pattern D: “Fatal error: Uncaught Error: Class ‘WC_…’ not found in /wp-content/plugins/…”
Translation: A plugin is trying to use a class from another plugin (often WooCommerce or ACF) that isn’t active or has been updated in a breaking way. Either the dependency got deactivated or the dependent plugin hasn’t caught up to a major version change.
Ninety percent of WSODs map to one of these four patterns. Once you’ve seen a dozen of them you’ll diagnose at a glance.
Ranked causes — what’s actually most likely
When we get called in on a WSOD, this is the frequency we see in practice. Stop spending time on rare causes before you’ve eliminated the common ones.
| Cause | Frequency | Time to diagnose |
|---|---|---|
| Plugin update introduced a fatal error | 45% | 2 minutes |
| PHP version upgrade broke an old plugin | 20% | 3 minutes |
| Memory limit exhausted on a specific action | 15% | 5 minutes |
| Theme function edited directly | 10% | 2 minutes |
| Corrupt core file / bad upload | 5% | 10 minutes |
| Database corruption or hosting-side issue | 5% | 15 minutes |
Notice what’s not at the top: “you need to clear your cache,” “reinstall WordPress,” “check your .htaccess.” Those are the least likely causes. Check the log first.
The fastest plugin-deactivate if you can’t reach the dashboard
You’ve read the log, you know which plugin broke the site, and you can’t log in. Here’s how to deactivate it, ordered by speed.
WP-CLI (fastest)
# Deactivate one specific plugin
wp plugin deactivate problem-plugin-slug
# Or deactivate all plugins as a nuclear option
wp plugin deactivate --all
# Reactivate known-good ones one at a time
wp plugin activate woocommerce
wp plugin activate advanced-custom-fields
WP-CLI is on every managed host (Kinsta, WP Engine, Cloudways, Pressable). SSH in and run it. This is the fastest fix for “site is down, I need it up in 60 seconds.”
SQL (if WP-CLI isn’t available)
If you only have phpMyAdmin or Adminer, deactivate all plugins with one query:
UPDATE wp_options
SET option_value = 'a:0:{}'
WHERE option_name = 'active_plugins';
This empties the active_plugins array. Every plugin will appear deactivated in the dashboard. You can then reactivate them one at a time and watch which one re-breaks the site. Do this on a staging copy first if you can.
FTP (if your host is truly bare-bones)
Rename the plugin’s folder from wp-content/plugins/problem-plugin to wp-content/plugins/problem-plugin-disabled. WordPress can’t find it, so it gets silently deactivated on the next page load. Rename the whole plugins folder to plugins-off to disable them all at once.
Recovery Mode: powerful when it works, silent when it doesn’t
WordPress 5.2 introduced Fatal Error Protection and Recovery Mode. When WordPress detects a fatal error, it’s supposed to email the site admin with a secret link that bypasses the broken plugin and lets you log in to deactivate it from the dashboard.
When this works, it’s great. When it doesn’t, here’s why:
- The admin email is wrong or not receiving mail. Check the admin_email in wp_options — if it’s an old address, you won’t get the email.
- Your site doesn’t send email. This is extremely common. Default PHP mail() is unreliable on most hosts. If you don’t have SMTP configured, Recovery Mode emails silently fail.
- The error happens before Recovery Mode loads. Errors in wp-config.php, in must-use plugins, or in drop-ins like object-cache.php happen too early for Fatal Error Protection to catch.
- Your SMTP plugin is the plugin that’s broken. Recursive failure. Yes, this happens.
If you’re not seeing the Recovery Mode email, don’t wait. Use the methods above.
OPcache: the invisible reason your fix didn’t work
This one catches developers who should know better. You fix the broken file, reload the page, and the white screen is still there. You check the file — your fix is saved. You check the log — new errors have stopped. But the site is still broken.
That’s OPcache. PHP caches compiled bytecode in memory to avoid recompiling on every request. When you edit a file, OPcache should notice and recompile — but if opcache.validate_timestamps is set to 0 (common on production for performance), it won’t. Your edit is saved on disk but PHP is still running the old version.
The fix is to flush OPcache or restart PHP-FPM:
# Option 1: flush OPcache from PHP
php -r 'opcache_reset();'
# Option 2: restart PHP-FPM (requires sudo, or your host's control panel)
sudo service php8.3-fpm reload
# Option 3: delete and recreate a file to force a recompile
touch wp-config.php
On managed hosts, every control panel has a “clear PHP OPcache” button. Use it. If you’re on WP Engine they have a one-click cache purge that includes OPcache. Kinsta has one in MyKinsta. Use the host tool and save yourself the SSH trip.
Host-specific WSOD patterns
Managed hosts have their own failure modes worth knowing.
Kinsta auto-disables plugins it believes are security risks. If a site went white right after a Kinsta notice, check the “Security” tab in MyKinsta — they may have disabled a plugin for you without a clear error.
WP Engine has a cron blocker and an object cache drop-in that can conflict with plugins that expect standard behaviour. If your WSOD only happens on cron-triggered events, check the WP Engine transient cache and the object-cache.php drop-in in wp-content/.
Cloudways runs Varnish in front of Nginx. A misconfigured Varnish VCL can serve a cached 500 page indefinitely. Purge Varnish from the Cloudways console if a fix doesn’t propagate.
Pantheon and other container-based hosts have read-only filesystems in live — you can’t edit files on production. You have to deploy through Git or the Pantheon Dashboard. If you’re trying to edit wp-config.php directly and seeing no effect, this is probably why.
If your host is doing something unusual, read their status page and their log viewer before assuming it’s your code.
PHP version upgrades — the second-biggest cause
When your host bumps PHP from 7.4 to 8.3 without you noticing, plugins that were never updated to handle PHP 8 syntax changes will throw fatals on the next page load. The telltale sign: the site was working yesterday, nothing changed on your end, and today it’s white.
Check your current PHP version:
php -v
# Or from inside WP
wp eval 'echo PHP_VERSION;'
If it’s 8.x and you weren’t expecting that, your host upgraded you. Most good hosts roll back automatically on detected errors; some don’t. You can either roll PHP back in the host control panel (buying time) or update the plugins that broke (the real fix).
Common PHP 8 breaks we see: passing null to functions that expect strings (now a deprecation warning that fails on strict mode), removed each() function, changed behaviour of string-to-number comparisons, and the array_key_exists() checks that used to paper over missing keys now throwing on ArrayAccess objects. If you maintain custom code, run it through PHPStan or Psalm before the next PHP upgrade and you’ll never hit this. We run PHPStan at level 5 on every client project and it catches more PHP 8 regressions than QA ever does.
One more gotcha with PHP version upgrades: mu-plugins load before regular plugins, and if an mu-plugin has a syntax error, the site dies so early that WordPress can’t even render its own critical error screen. That’s the scenario where Recovery Mode silently fails and the debug log stays empty. If you have an mu-plugin directory (wp-content/mu-plugins/), check it first — rename the directory to force-disable everything in it, reload, and see if the site comes back. If it does, you’ve found the scope of the problem and can bisect from there.
Stop it from happening again
The WSOD is almost always preventable. Three things will catch 95% of them before they reach production:
1. A staging environment. Every managed WordPress host gives you one for free. Use it. Update plugins there first, click around the site, check the debug log. Then push to production. This alone eliminates the plugin-update WSOD.
2. Server-side uptime monitoring. Not “my site looks fine” — a real check from an external service (UptimeRobot, Pingdom, BetterStack) that hits the homepage every minute and alerts you on 500 status. You should know within 60 seconds of the site going down, not when a customer emails you.
3. WP_DEBUG_LOG always on, WP_DEBUG_DISPLAY always off. Even on production. The log is cheap, the display is dangerous. You want the data when you need it.
If you can’t commit to the staging-environment discipline and you’re tired of getting paged, our WordPress Maintenance & Care Plans include monitored updates on staging, proactive log review, and emergency WSOD recovery — starting at $99/month. It’s cheaper than one afternoon of lost sales on a broken checkout.
For related reading, see our guides on WordPress cron jobs and WordPress hooks and filters — both cover the debugging fundamentals that also apply when you’re hunting down a fatal error.