What does “There has been a critical error on this website” mean?
It means PHP hit a fatal error while WordPress was building the page, and the request stopped. Since version 5.2, WordPress catches the fatal error and shows this message instead of a blank white screen. The server answers with HTTP 500.
The message itself says nothing about the cause. The cause is in the PHP error log. It is usually one plugin, one theme file or a server change such as a PHP upgrade. This guide finds it in that order, with two faults we reproduced on our own test site.
WordPress shows two versions of the message. On the front end you see only “There has been a critical error on this website.” On wp-admin and the login page it adds “Please check your site admin email inbox for instructions.” That second line appears even when no email was sent, as our test below shows. If you would rather hand this to someone, our website maintenance and care team can take it on as part of a care plan.
What is the safe order for fixing a critical error?
Guessing turns a small fault into a long outage. Deleting plugins at random, updating everything at once or restoring last month’s backup over new orders all make it worse. Work in this order:
Steps
-
Back up first
Copy the database and
wp-contentas they are now, broken. A restore can lose orders, form entries and posts made since the last backup. -
Reproduce on staging
If your host offers a staging copy, clone the broken site and test fixes there. If the live site is already down, record every change you make on it.
-
Read the log
Turn on the debug log, outside the web root, and load the failing page once. The fatal error line names the file and line.
-
Isolate
Disable only the plugin or theme the log names. If the log is unclear, disable plugins in halves.
-
Fix the cause
Roll the plugin back to its last working version, install the vendor’s fixed release or correct the code.
-
Verify
Load the front end,
wp-admin, forms and checkout, and confirm the log stays quiet. -
Keep a rollback ready
Know how you will undo each change before you make it: the previous plugin zip, the previous PHP version or the backup from step 1.
How do you find the error in the logs?
Add these lines to wp-config.php, above the line that says “That’s all, stop editing”. They write errors to a file and keep them off the screen, so visitors never see file paths:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/example/logs/wp-debug.log' ); // outside the web root
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', '0' );Point WP_DEBUG_LOG at a folder outside the public web root. If you set it to true, WordPress writes to wp-content/debug.log, which sits inside the web root.
That matters. On our test site, a log at wp-content/debug.log could be downloaded by anyone:
public fetch of wp-content/debug.log: 200That file held full server paths and stack traces. Apache can block it with .htaccess rules, but nginx ignores .htaccess files. A private path avoids the question.
What does a plugin fatal error look like?
We wrote a small test plugin, Lab Order Notes. Version 1.0.0 printed a line in the footer and worked. Then we replaced it with a version 1.1.0, as an automatic update would. The new version calls a WooCommerce function on every page load, on a site without WooCommerce. That is a common real-world pattern: a plugin that assumes another plugin is active.
add_action('init', function () {
$order = wc_get_order(get_option('lab_last_order_id', 1));
});Every URL on the site failed, including the dashboard and the login page:
500 /
500 /wp-login.php
500 /wp-admin/The private log had the cause on the first line:
[11-Nov-2026 03:01:07 UTC] PHP Fatal error: Uncaught Error: Call to undefined function wc_get_order() in /wordpress/wp-content/plugins/lab-order-notes/lab-order-notes.php:10
Stack trace:
#0 /wordpress/wp-includes/class-wp-hook.php(353): {closure}('')Read the path after wp-content/plugins/. The folder name, lab-order-notes, is the plugin to disable. If the path points into wp-content/themes/, it is the theme.
The hook decides how much breaks. In a first run we attached the same code to the wp hook, which only runs on front-end pages. The front end returned 500, but wp-login.php returned 200 and wp-admin redirected to the login as normal. In that case you can still deactivate the plugin from the Plugins screen.
Does the recovery mode email help?
Sometimes. WordPress can email the site admin a special link that opens the dashboard with the faulty plugin paused. Our tests and the WordPress 7.1.4 source show three conditions that decide whether that email arrives:
- It is only sent for errors on protected pages. When the error hit only the front end, WordPress sent nothing. The source returns “Error occurred on a non-protected endpoint.” WordPress tried to send it only once the error also broke
wp-adminand the login page. - It is limited to one per day. The default rate limit is one day, set by the
recovery_mode_email_rate_limitfilter. - A failed send still counts. WordPress saves the send time before it sends. Our test site cannot send mail, and
wp_mail()returnedfalse. Yetrecovery_mode_email_last_sentwas saved, which blocks a new email for a day.
To see what the email would have offered, we generated the same link with WordPress’s own recovery link service, then opened it and logged in. The Plugins screen showed:
Lab Order Notes Deactivate | Resume
Version 1.1.0
This plugin failed to load properly and is paused during recovery mode.
An error of type E_ERROR was caused in line 10 of the file
/wordpress/wp-content/plugins/lab-order-notes/lab-order-notes.php.
Error message: Uncaught Error: Call to undefined function wc_get_order()
You are in recovery mode. This means there may be an error with a theme or plugin.
To exit recovery mode, log out or use the Exit button.Recovery mode pauses the plugin for your session only. Visitors still see the error until you deactivate the plugin or fix it.
How do you deactivate a plugin when wp-admin is down?
Rename the plugin’s folder. Use SFTP or your host’s file manager, and add a suffix such as .disabled. WordPress can no longer find the plugin file, so it skips the plugin. On our test site:
mv wp-content/plugins/lab-order-notes wp-content/plugins/lab-order-notes.disabled200 /
200 /wp-login.phpThe plugin stays listed as active in the database until an admin opens the Plugins screen. Then WordPress removes it and shows:
The plugin lab-order-notes/lab-order-notes.php has been deactivated due to an error: Plugin file does not exist.Renaming the folder back does not reactivate it, which is what you want. Install the previous version or the vendor’s fix before you activate it again.
With shell access and WP-CLI, one command does the same job. The --skip-plugins flag stops the broken plugin from loading while WP-CLI runs. We did not run WP-CLI on this test site; this comes from the WP-CLI documentation.
wp plugin deactivate lab-order-notes --skip-pluginsWhy does a critical error appear after a PHP update?
Hosts move sites to newer PHP versions, and PHP 8.0 removed functions that old plugins still call. The PHP 8.0 migration guide lists create_function() and each() as removed. Code that used them ran on PHP 7.4, often with only a deprecation notice, and stops dead on PHP 8.
We reproduced it with a second test plugin, Lab Legacy Badges 2.3.0, written in the old style:
$lab_badge_sort = create_function('$a, $b', 'return strcmp($a, $b);');On PHP 7.4 the site worked, and the log recorded only a deprecation:
HTTP/1.1 200 OK
x-powered-by: PHP/7.4.34-dev
Badges: accessible, fast, secure
PHP Deprecated: Function create_function() is deprecated in .../lab-legacy-badges.php on line 9That deprecation line is the warning sign. If your log shows lines like it, the site will break on the next PHP upgrade. We then restarted the same site, same database and same files, on PHP 8.3:
500 /wp-login.php
500 /wp-admin/
HTTP/1.1 500 Internal Server Error
x-powered-by: PHP/8.3.36
There has been a critical error on this website.
PHP Fatal error: Uncaught Error: Call to undefined function create_function() in /wordpress/wp-content/plugins/lab-legacy-badges/lab-legacy-badges.php:9
#0 /wordpress/wp-settings.php(597): include_once()The stack trace points at wp-settings.php, which loads plugins. The plugin fails as soon as it loads, so every page breaks, not only the ones that use it.
How do you roll back, and what is the real fix?
The quick rollback is to switch PHP back in your hosting panel. We restarted the site on PHP 7.4 with the original plugin, and it returned 200 with the badges in the footer again. PHP 7.4 lost security support on November 28, 2022, so treat that as a stopgap for hours or days.
The real fix is to update or replace the plugin. In version 2.3.1 of our test plugin, one line changed:
$lab_badge_sort = function ($a, $b) { return strcmp($a, $b); };HTTP/1.1 200 OK
x-powered-by: PHP/8.3.36
Badges: accessible, fast, secure
new log lines: 0If a plugin vendor has not shipped a PHP 8 release, that plugin is abandoned. Plan to replace it rather than patch it yourself on every update.
Before a PHP upgrade, search your plugins and theme for functions PHP 8 removed. This rough check found our test plugin:
grep -rnE "create_function\(|[^_a-z]each\(|mysql_[a-z_]+\(" wp-content/plugins/ wp-content/themes/ --include=*.phpwp-content/plugins/lab-legacy-badges/lab-legacy-badges.php:9:$lab_badge_sort = create_function('$a, $b', 'return strcmp($a, $b);');A grep misses many changes, so it does not replace a test on staging. Run the site on the new PHP version on a staging copy, click through the key pages and read the log before you switch production.
What if the log does not point to a plugin?
- A theme file. Rename the active theme’s folder. WordPress falls back to a default theme if one is installed, so keep one installed.
- A must-use plugin or drop-in. Files in
wp-content/mu-plugins/and drop-ins such asobject-cache.phpload without activation. Renaming the normal plugin folder does not touch them. - Memory. “Allowed memory size exhausted” names the file that ran out of memory, not always the one that used it. Raise
WP_MEMORY_LIMITas a test, then find what grew. - Core files. A failed core update can leave missing files. Reinstall the same WordPress version over the top, which leaves
wp-contentalone.
How do you verify the fix and close the incident?
- Load the homepage, a post, the login page and the dashboard while logged out and logged in.
- Run the tasks that make money: submit a form, add to cart and reach checkout.
- Watch the private log through a few page loads and confirm no new fatal lines appear.
- Leave
WP_DEBUG_DISPLAYfalse. Keep the log in its private path, or turnWP_DEBUGoff once the fault is closed. - Write down the cause, the fix and the rollback, so the next person does not start from zero.
The fastest repair is the one you prepared for. Our WordPress maintenance checklist includes a timed restore drill, so you know your backup works before you need it. If a server limit is the problem instead, see our guide to the 414 Request-URI Too Large error.
Limits: what this guide does not cover
- We tested on WordPress Playground, which uses SQLite and a PHP build compiled to WebAssembly. Hosted sites use MySQL or MariaDB and native PHP. The error handling is the same WordPress code; host log locations differ.
- Playground cannot send mail, so we did not see a recovery email arrive. We generated the same link with WordPress’s own code to show what it opens.
- We did not test WP-CLI, multisite, or a theme fatal error. The WP-CLI command comes from its documentation.
- Fatal errors from a failed database connection show a different message, “Error establishing a database connection”, and are not covered here.