A Contact Form 7 auto-update that leaves wp-admin showing a white screen is almost always the same incident. The plugin’s recent release will not run on older versions of PHP, and the update reached servers that could not execute it. The site is recoverable in a minute if there is shell access to the host, or in two from SFTP; the pin after the recovery keeps every other plugin’s auto-updates alive.
Get the site back up
The fastest path is through WP-CLI over SSH. Named deactivation with the plugin loader skipped:
wp plugin deactivate contact-form-7 --skip-plugins
--skip-plugins tells WP-CLI not to load the plugins directory for the duration of the command, which is the piece that matters when the plugin itself is the thing throwing the fatal. Without it the deactivation command dies the same way wp-admin does. The flag is a global WP-CLI parameter, documented in the
config reference.
If the host does not expose SSH, rename the plugin folder over SFTP:
wp-content/plugins/contact-form-7 -> wp-content/plugins/contact-form-7.off
WordPress treats a plugin whose main file is missing as deactivated at load time, so wp-admin comes back on the next request. Rename, not delete: the pinning fix below depends on keeping the plugin installed.
Before anything else, confirm the fatal is in contact-form-7.php and not in a different plugin that happened to update around the same time. The last few entries in wp-content/debug.log, or the host’s PHP error log if WP_DEBUG_LOG is off, carry the stack trace. If the top frame points at any other file, the recovery is still the same shape (deactivate the named plugin with --skip-plugins) applied to that plugin instead.
For sites on WordPress 6.6 or later, core’s auto-update rollback (
Trac #58281) should have fired a loopback request after the update, caught the fatal, and restored the previous version. When it does not, the usual causes are a loopback request that cannot reach the site (DNS pointing somewhere else during local maintenance, a WAF that blocks self-requests) or an unwritable backup directory. Sites on 6.5 and earlier have no auto-update rollback at all; the manual recovery above is the whole fix.
Pick a direction, then stay there
The next step depends on what the host can do.
The long-term correct move is upgrading PHP to a currently supported branch. PHP 8.2 and newer are the branches
still receiving security fixes in 2026, and 8.3 is what CF7 v6.2 wants. On most hosts the switch is a one-click flip in the control panel; the compatibility risk is in other plugins and theme code, which the
PHP Compatibility Checker will scan for before the flip. Once PHP is on 8.3, re-enable CF7 and the problem is gone for this release and the ones that follow.
The short bridge, if the host cannot upgrade PHP today, is holding CF7 at v6.1.5 until it can. Acceptable only after confirming the pinned version has no open security advisories: check the plugin’s wordpress.org page under Advanced View for the version download, and
Patchstack’s CF7 vulnerability page for CVEs against 6.1.5. The pin step below prevents a future auto-update from re-breaking the site while the bridge is in place.
Core auto-updates themselves should stay on. Disabling them to work around one plugin’s PHP bump trades a once-in-a-release incident for ongoing exposure to WordPress security releases. The pin below applies to the plugin, not to core.
Pin CF7 without disabling the rest
Per-plugin auto-update toggles have been in WordPress core since 5.5 (
dev note). On wp-admin/plugins.php, each row carries an Automatic Updates column; the link in that column toggles auto-updates for that one plugin only, with no effect on the others. For most operators this is the whole fix.
For sites that manage plugin state in code (CI-deployed mu-plugins, a config-managed stack), the equivalent is the ![]()
auto_update_plugin filter. The filter below returns false for contact-form-7 and leaves the auto-update behaviour of every other plugin alone:
add_filter( 'auto_update_plugin', function( $update, $item ) {
if ( isset( $item->slug ) && 'contact-form-7' === $item->slug ) {
return false;
}
return $update;
}, 10, 2 );
Drop this in a file under wp-content/mu-plugins/. Must-use plugins load before regular plugins and cannot be deactivated through the UI, which is the behaviour you want for a policy file.
Managed hosts (Kinsta, WP Engine, SpinupWP, Cloudways) intercept core and plugin updates at the control-panel level with their own policy UI; on a site built on their stack, the host’s dashboard is the one place to set the pin, because an in-code filter can be overridden by the host’s own update layer.
This is not a Contact Form 7 bug
CF7 set Requires PHP: 8.3 for v6.2 in the plugin header. The gap is on WordPress’s side: three update paths skip the PHP check.
WordPress core does have PHP guards against updating a plugin a server cannot run. WP_Automatic_Updater::should_update() reads $item->requires_php from the update offer and returns false when the server’s PHP is lower. ![]()
Plugin_Upgrader::bulk_upgrade() attaches check_package() to each item for the same reason. Both paths honour Requires PHP:.
The three paths that do not:
- The single-plugin “Update now” click in wp-admin calls

Plugin_Upgrader::upgrade(), which attaches hooks for deactivation, cache-clearing and the backup, but does not attachcheck_packageand does not readrequires_phpfrom the update offer. A reader who clicked that link to apply the pending CF7 update got no PHP check at all. - A stale update offer. The guards rely on
$item->requires_phpbeing present and current. If the wordpress.org update API returns an offer whoserequires_phpfield has not yet caught up to the plugin’s new header, the guard either passes (field missing) or lets a lower required version through (stale value) and the update runs. - Managed-host auto-updaters that intercept updates outside core entirely. Core’s checks do not run on a path core does not execute.
Any plugin that raises its PHP floor in a future release can land on the same reader through any of those paths. The real fix is a host on a currently supported PHP branch plus a short watchlist of plugins that have historically bumped their floor (Contact Form 7, Yoast, WooCommerce, Gravity Forms).
The Contact Form 7 review page carries the plugin’s broader fit for a WordPress site. On this specific incident, the plugin did what its header said it would.
