WordPress HTTP Errors: Fixing Critical Errors, 503s and wp-cron
WordPress sites are built from a core, a theme and often dozens of plugins written by different people, so most HTTP errors on a WordPress site trace back to one piece that broke after an update, clashed with another, or rewrote a file it should have left alone.
The symptoms are familiar: "There has been a critical error on this website", every page except the homepage returning 404, a maintenance notice that never goes away, or 502s whenever scheduled tasks run. This guide shows how to find the piece at fault using WordPress's own tools, fix it without losing content, and stop it happening after the next update.
What each error usually means on WordPress
The status code tells you which part of WordPress to suspect before you open a single file.
| What you see | Code | Most likely WordPress cause |
|---|---|---|
| "There has been a critical error on this website" | 500 | A PHP fatal error in a plugin, theme or custom snippet, often straight after an update |
| Homepage works, every post and page is 404 | 404 | Missing or broken rewrite rules
in .htaccess, or no WordPress rule in nginx |
| New custom post type or shop pages are 404 | 404 | Rewrite rules not flushed after a plugin registered new URLs |
| "Briefly unavailable for scheduled maintenance. Check back in a minute." | 503 | An
update was interrupted and left the .maintenance file behind |
| "Error establishing a database connection" | 500 | Wrong credentials in wp-config.php,
or the database server is down or overloaded |
| Admin or AJAX requests fail with 403 | 403 | A security plugin or server firewall rule; see the 403 guide |
| 502 or 504 at regular intervals | 502/504 | Heavy scheduled tasks run by wp-cron competing with visitors |
| Blank white page, no message | 500 or 200 | A fatal error with fatal error protection switched off, or output suppressed |
Since WordPress 5.2, fatal error protection catches most PHP fatal errors, shows the "critical error" message to visitors, and emails the site's admin address a link to recovery mode. That email is the fastest route in, because it usually names the plugin or theme that failed and lets you log in with that one component paused.
The fix, step by step
Step 1
Send PHP errors to a private debug log
Open wp-config.php and, above the line that says "That's all, stop editing",
set WP_DEBUG to true, WP_DEBUG_DISPLAY to false,
and WP_DEBUG_LOG to a full path outside the public web folder, such as /home/example/logs/wp-debug.log.
Setting it to true writes to wp-content/debug.log, which anyone can download
unless you block it. Load a failing URL from the HTTP Error Audit results, then read
the newest "PHP Fatal error" line.
Step 2
Switch off the plugin named in the error
The fatal error line gives a file path; the folder after wp-content/plugins/
is the plugin at fault. Deactivate it from recovery mode, with wp plugin deactivate plugin-folder
in WP-CLI, or by renaming its folder over SFTP. If no plugin is named, deactivate all of them with wp plugin deactivate --all,
confirm the error clears, then reactivate them in halves until it returns. Check wp-content/mu-plugins
too, because must-use plugins cannot be switched off from the dashboard.
Step 3
Rule the theme in or out
If
the path in the log points into wp-content/themes/, or the error survives with every plugin
off, switch to a default theme with wp theme activate twentytwentyfive, or whichever default
theme is installed. If the site recovers, the fault is in your theme or child theme, very often in functions.php
after a snippet was pasted in. Fix or remove the offending code, which the log line identifies by file
and line number, then switch back and check the pages that failed before.
Step 4
Rebuild the rewrite rules and flush permalinks
When posts and pages return 404 but the homepage works, open .htaccess
in the WordPress folder and check the block between # BEGIN WordPress and # END WordPress
matches the standard rules shown below. Remove any duplicated or half-written blocks left by plugins.
Then go to Settings, Permalinks and click Save without changing anything, or run wp rewrite flush --hard.
On nginx there is no .htaccess; the server block needs a try_files line that
hands unknown paths to index.php.
Step 5
Clear a stuck maintenance file
If
every page shows "Briefly unavailable for scheduled maintenance" long after an update should have finished,
WordPress was interrupted before it could remove the .maintenance file in the site's root
folder. Delete that file over SFTP or in the file manager, and the site returns immediately. Then open
Dashboard, Updates, because the update that was running may have left a plugin or theme half replaced.
Reinstall that component to be sure, and check the debug log for errors from it.
Step 6
Move wp-cron to a real server cron job
By default WordPress checks for due scheduled tasks on page requests, so busy sites run them
constantly and quiet sites run them late. Add define( 'DISABLE_WP_CRON', true ); to wp-config.php,
then create a server cron job that runs every five minutes, either wp cron event run --due-now
through WP-CLI or a request to wp-cron.php. Run wp cron event list to spot
overdue or duplicated events, and remove leftovers from plugins you uninstalled.
Step 7
Turn debugging off and crawl again
Once
the errors have stopped, set WP_DEBUG back to false in wp-config.php
and remove or archive the log file, since it can grow large and may contain file paths and query details.
Clear any page cache and CDN cache so stored error pages are not served again. Run the HTTP Error
Audit across the whole site and confirm that the URLs which failed now return 200, and that
removed pages give a clean 404.
The settings you will need
Debug logging in wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', '/home/example/logs/wp-debug.log' );
@ini_set( 'display_errors', 0 );
Create the logs folder first and make sure the PHP user can write to it. If your host
does not allow paths outside the web folder, use true and block public access to wp-content/debug.log
in the server configuration.
Standard .htaccess rules for Apache
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
If WordPress lives in a subfolder, such as /blog/, the RewriteBase and final
rule change to /blog/ and /blog/index.php. Keep your own rules, such as redirects
and security headers, outside the markers, because WordPress rewrites everything between them when permalinks
are saved.
The equivalent for nginx
location / {
try_files $uri $uri/ /index.php?$args;
}
.htaccess and wp-config.php before editing
either. A single typo in .htaccess produces a 500 on every request, and a mistake in wp-config.php
can stop the whole site loading.Plugin and theme failures in more depth
Most WordPress fatal errors follow an update. The usual patterns are:
- PHP version mismatch. A plugin updated to require a newer PHP version than your host runs, or your host upgraded PHP and an old plugin uses functions that no longer exist. The log line will mention an undefined function, a type error or a syntax error in a file that has not changed for years.
- Two plugins declaring the same thing. "Cannot redeclare function" or "Cannot declare class, because the name is already in use" means two plugins bundle the same library. Update both, or replace one.
- Memory exhausted. "Allowed memory size of ... bytes exhausted" names the file that
happened to be running when memory ran out, which is not always the one using it. Raise
WP_MEMORY_LIMITonly as far as your host's PHP limit allows, and look for the plugin doing heavy work on that page. - Drop-ins. Files such as
object-cache.phpandadvanced-cache.phpinwp-contentload before any plugin. If a caching plugin is removed but its drop-in stays, the site can fail in ways that disabling plugins does not fix. Rename the drop-in to test. - Custom code snippets. Code pasted into
functions.phpor a snippets plugin is a frequent cause, because it has never been tested against later versions.
With WP-CLI you can test without touching the live settings at all. wp --skip-plugins --skip-themes option get home
proves the core and database work, and wp plugin list --status=active gives you the list
to reactivate afterwards.
WordPress 404 quirks
Beyond broken rewrite rules, a few WordPress behaviours produce 404s, or hide them, in ways that confuse audits:
- Slug changes. WordPress remembers old slugs for posts and redirects them, but not always for pages, categories or custom post types. Add redirects for those yourself when you rename them.
- Guessing redirects. For some missing URLs WordPress looks for a post with a similar slug and redirects there. That can send visitors to the wrong post and hide a real 404 from the audit. Check a few redirected URLs to make sure the guesses are sensible.
- Pagination past the end.
/blog/page/40/on a blog with 12 pages returns 404, which is correct. If a theme links to pages that do not exist, fix the theme's pagination. - Custom post types and WooCommerce. After activating a plugin that adds new URL types, save permalinks once so the new rules are written.
For deciding what to do with a large list of 404s once the rules are working, including when to redirect and when to let a URL go, see the 404 Not Found guide. Redirect plugins and their conflicts are covered in fixing WordPress redirects.
Why wp-cron causes errors on busy and quiet sites
WP-Cron is not a real scheduler. On each page load WordPress checks whether any task is due and, if
so, fires a background request to wp-cron.php on the same site. That design causes problems
at both ends of the traffic scale.
- Busy sites and crawls. During a crawl or a traffic spike many requests arrive together, background cron requests pile up, and heavy tasks such as backups, feed imports, email queues or sitemap rebuilds compete with visitors for the same PHP workers. The result is 502s and 504s at the moments you can least afford them.
- Quiet sites. With few visitors, tasks run late or not at all, so scheduled posts miss their time and caches are not refreshed.
- Blocked loopback requests. If the server cannot request its own address, for example because of a firewall or basic authentication on a staging site, cron never runs and Site Health reports a loopback failure.
A server cron job fixes all three. In Plesk this is under Scheduled Tasks for the domain, and in cPanel under Cron Jobs; the Plesk and cPanel guide covers the panel side. A typical entry looks like this:
*/5 * * * * cd /var/www/example.co.uk/httpdocs && wp cron event run --due-now --quiet
Schedule the heaviest jobs, such as full backups, for the quietest hours rather than letting a plugin choose.
Stopping the next update breaking the site
- Update on staging first where your host provides one, and click through the key templates and checkout before updating live.
- Back up before updates, files and database together, and know how to restore without the dashboard.
- Be selective with automatic updates. Minor core and security updates are usually safe to automate; large plugin updates, especially page builders and shop plugins, deserve a check first.
- Remove plugins you do not use. Deactivated plugins still sit on the server, and every active one is another thing that can fail.
- Keep PHP current and check plugin compatibility before your host moves you to a new version.
- Keep WP-CLI available. When the dashboard is down it is the quickest way to switch plugins and themes.
- Run the HTTP Error Audit after updates, since a plugin can break one template while the homepage still looks fine.
1. How do I fix "There has been a critical error on this website"?
Check the admin email inbox for a message from WordPress with a recovery mode link, which usually names the plugin or theme that failed and lets you log in with it paused. If there is no email, turn on debug logging in wp-config.php and read the newest PHP Fatal error line, which gives the file path. Deactivate the plugin or theme in that path using WP-CLI or by renaming its folder, then update, replace or remove it before reactivating anything.
2. Why do all my WordPress pages show 404 except the homepage?
The homepage can load without any rewrite rules, but every other pretty URL relies on the server passing unknown paths to index.php. When those rules are missing, broken or overwritten, the server looks for a real file, finds none and returns 404. On Apache, restore the standard block in .htaccess and save permalinks once. On nginx, add a try_files line that falls back to index.php. After a server move, also check that mod_rewrite is enabled and that overrides are allowed.
3. Is it safe to leave WP_DEBUG turned on?
Not on a live site for long. With display switched on, visitors can see error messages that reveal file paths and other details, and even with display off, a debug log inside wp-content can be downloaded by anyone who guesses the address. The log also grows quickly on busy sites. Turn debugging on while you investigate, write the log to a folder outside the public web root, and switch it off again as soon as the cause is fixed.
4. How do I disable a plugin if I cannot log in to WordPress?
Use WP-CLI if your host provides SSH access, with wp plugin deactivate followed by the plugin folder name, or add the skip-plugins flag to run commands without loading any. Without SSH, connect by SFTP or your host's file manager and rename the plugin folder inside wp-content/plugins, for example by adding -off to the end. WordPress deactivates a plugin whose folder it cannot find. Rename it back once you have fixed or replaced it.
5. What causes the stuck maintenance mode message?
When WordPress updates core, plugins or themes, it creates a file called .maintenance in the site root and shows the maintenance message until the update finishes and the file is deleted. If the update is interrupted, by a timeout, a closed browser tab or running out of memory, the file stays and the site keeps returning 503. Deleting the file restores the site. Then rerun or reinstall whatever was updating, as it may have been left incomplete.
6. Should I disable wp-cron?
Disabling the built-in trigger is a good idea on most sites, provided you replace it with a real server cron job that runs every few minutes. The built-in trigger runs tasks on page loads, which adds load during busy spells and leaves tasks late on quiet sites. Simply setting DISABLE_WP_CRON without adding a server job stops scheduled posts, backups and cache refreshes from running at all, so always set up the replacement before or at the same time.
7. Why does my WordPress site return 500 errors only sometimes?
Intermittent 500s usually come from something that depends on the request or the moment: a plugin that fails only on certain pages, memory that runs out only on heavy pages or during a crawl, a database server that refuses connections when too many are open, or a scheduled task that overloads the server at set times. Leave the debug log running for a day, then match the times of the errors against the audit results and your cron schedule.
The HTTP Error Audit requests every URL in your crawl scope, records the status code and response time, follows redirects and flags anything outside the 200 range, so a template broken by an update does not go unnoticed.
Open HTTP Error Fixes โ