Back to blog

WP Rocket's WordPress 7.1 Fatal Error: Fix It Now, Then Fix Your Update Process

WP Rocket 3.23.2.2 fixes the WordPress 7.1 fatal error. How to recover a site that is already down, and why the crash happened.

WP Rocket's WordPress 7.1 Fatal Error: Fix It Now, Then Fix Your Update Process

If your site went white after updating to WordPress 7.1 and you run WP Rocket, the fix is to update WP Rocket to 3.23.2.2. That release went out on 20 August 2026, roughly a day after sites started falling over, and it resolves the WP Rocket WordPress 7.1 fatal error at its source. If you cannot reach wp-admin to install it, rename the plugin folder over SFTP first, get back in, then update.

That is the whole emergency answer, and it is the reason most people are reading this page. The rest of it is for afterwards: what actually broke, why some sites crashed and others did not, and what a small team can realistically change so that the next major release is less of a lottery. That last part matters more than the version number, because the specific bug is already patched and the pattern behind it is not going anywhere.

TL;DR

  • Update WP Rocket to 3.23.2.2 (released 20 August 2026). That is the permanent fix.
  • If wp-admin is unreachable, rename /wp-content/plugins/wp-rocket/ over SFTP to deactivate the plugin, log in, rename it back, then update.
  • The error signature is a PHP TypeError from substr() at line 562 of WP Rocket's Cloudflare.php.
  • Cause: a WordPress 7.1 performance change made some hook callback keys integers instead of strings, and WP Rocket's Cloudflare code ran a string function on them under strict types.
  • You did not need to be using Cloudflare. That module loads on every request regardless.
  • Not every WP Rocket site crashed. It needed another active plugin registering a closure on one of the hooks WP Rocket walks.
  • Core has fixed its side in trunk and the change is queued for 7.1.1.
  • Skip the manual code edits that circulated on 19 and 20 August. A real release exists now, and a hand-edited plugin file gets overwritten on the next update.

How to get a downed site back and finish the fix

There are two situations, and they need different first moves.

If you can still reach wp-admin

Some sites throw the fatal error on the front end but leave the dashboard reachable, usually because the crashing code path has not been hit in an admin request yet. If that is you, the order is simple:

  1. Deactivate WP Rocket.
  2. Confirm the front end loads again.
  3. Update WP Rocket to 3.23.2.2.
  4. Reactivate and clear the cache.

One wrinkle is worth knowing about. Deactivating WP Rocket can leave the plugin unable to see its own update, because the update check runs through the plugin's licence handshake. WP Rocket publishes a small helper plugin for exactly this, called WP Rocket - Update Notification Recovery, and its knowledge base article on the WordPress 7.1 fatal error walks through installing it, forcing an update check, updating to 3.23.2.2, then removing the helper again. If the update simply appears in your plugins list without any of that, you do not need the helper.

If wp-admin is down too

This is the more common version of the story, because the crash fires during init and takes the whole request with it. The front end, wp-admin, admin-ajax and the REST API all go at once.

  1. Connect over SFTP or a hosting file manager.
  2. Rename /wp-content/plugins/wp-rocket/ to something else, for example /wp-content/plugins/wp-rocket-off/. WordPress cannot find the plugin file, so it deactivates it and the site loads.
  3. Log in to wp-admin and confirm WP Rocket shows as deactivated.
  4. Rename the folder back to /wp-rocket/.
  5. Update to 3.23.2.2, then reactivate.

Important: Do not delete the plugin folder. Renaming it is reversible and keeps your settings; deleting it can take your configuration with it.

Confirm it is actually this bug before you change anything

Not every fatal error after a major release is the same fatal error, and the fastest way to waste an afternoon is to fix the wrong thing. This one has a specific signature in the PHP error log:

text
PHP Fatal error:  Uncaught TypeError: substr(): Argument #1 ($string) must be of type string,
int given in .../wp-content/plugins/wp-rocket/inc/ThirdParty/Plugins/CDN/Cloudflare.php:562

Older PHP versions phrase the same failure differently, and some hosts log it as an E_ERROR referencing line 562 of that file. The file path is the part that identifies it. If your log points somewhere else, this article is not your problem.

If you have shell access, the same check takes one command:

terminal
grep -m 5 "Cloudflare.php:562" /path/to/php-error.log

A word on the manual patches

While the fix was in progress, two workarounds went around: a one-line type guard added directly to Cloudflare.php, and a small drop-in file placed in mu-plugins. WP Rocket documented both, and they did what they promised on 19 and 20 August.

Both are now the wrong move. A hand-edit to a plugin file is silently reverted the next time that plugin updates, which means a site patched by hand and then updated routinely a fortnight later can go down a second time for the same reason, with nobody remembering why. The drop-in is safer because it survives updates, but it is code you now own forever unless you remember to delete it. Since 3.23.2.2 exists, take the release. If you already applied either workaround, update the plugin and then remove the workaround so it is not sitting in your mu-plugins folder in a year's time doing something nobody can explain.

What actually broke

The interesting part of this incident is that neither side did anything obviously stupid. Two reasonable decisions met and produced a fatal error.

WordPress stores every registered hook callback in a public property, WP_Hook::$callbacks, keyed by a unique ID that core builds for each callback. For most of WordPress's history that ID came from spl_object_hash(), which returns a 32 character hex string. In a performance change that landed for 7.1, core switched to spl_object_id(), which is considerably faster and was worth around a 20 percent improvement on hook addition and removal.

The switch cast the new ID to a string to preserve the old behaviour. That cast looks correct and is correct, right up until the value is used as an array key. PHP converts a string that is the canonical decimal representation of an integer back into an integer when it becomes an array key. So (string) 5292 goes in as "5292" and comes out as 5292. The cast was undone one layer down, at the point of storage, and the callback keys quietly changed type.

You can see it in three lines:

php
add_action( 'init', function () {} );

global $wp_filter;
var_dump( array_keys( $wp_filter['init']->callbacks[10] ) );
// WordPress 7.0.x: string(32) "0000000000000001..."
// WordPress 7.1:   int(5292)

Only one kind of callback is affected: a bare object, meaning a closure or an instance with an __invoke() method. An array callback like [ $object, 'method' ] produces something like "5292method", which is not a plain decimal string and stays a string. Plain function names and Class::method strings never changed at all. That narrowness is why the breakage looked so random from the outside.

WP Rocket's Cloudflare integration walks those callback keys to unregister its own cache-clearing hooks, and it compares them using substr(). The file declares strict_types, so when PHP 8 is handed an integer where a string is declared, it does not coerce. It throws a TypeError. Under PHP 7 the same mismatch would have been silently converted and nobody would ever have noticed.

That is the general lesson hiding inside a specific bug. PHP 8 turned a large category of quiet sloppiness into hard crashes. That is better in every respect except the one where your site goes down on a Wednesday.

Core's side of it is tracked as ticket #65919, which was opened on 20 August and owned by Weston Ruter. The fix prefixes the generated ID with a non-numeric literal so PHP has nothing to coerce, adds tests that check the type of the resulting array key rather than the return type of the function, and applies the same treatment to WP_Widget_Factory::$widgets, which had the same problem for the same reason. It landed in trunk on 21 August and the ticket was reopened to be considered for the 7.1.1 maintenance release. The existing unit tests had asserted that the ID-building function returned a string, which it did. They never asserted the type of the key once it was stored, which is where the coercion happened.

Why your neighbour's site survived and yours did not

Because only bare-object callbacks changed type, the crash needs one to exist on a hook WP Rocket iterates over. WP Rocket alone on a clean install does not produce it. Add a plugin or theme that registers a closure on the post-transition hooks WP Rocket touches, and the next request that fires init takes the site down.

Anchor Hosting's Austin Ginder, who was first to flag the problem publicly, reported that 124 of the 332 production sites in his fleet running WP Rocket hit PHP fatal errors, or 37 percent, and that every crash involved the same three ingredients: WordPress 7.1, PHP 8.x, and WP Rocket's Cloudflare module. He also reported that Elementor Pro and Contact Form 7 Redirection were two additions that reproduced it on an otherwise clean install.

Treat those two plugin names as examples rather than a boundary. The condition is structural, not a specific product: any active code registering a closure or an invokable object on the relevant hooks is enough. If you run WP Rocket on 7.1 and neither of those plugins, you were not safe, you were lucky, and the ratio of lucky to unlucky sites in one host's fleet was roughly two to one.

The code running on your site is larger than the features you turned on

Here is the detail worth carrying away from this, and it has nothing to do with caching.

The crashing file lives at inc/ThirdParty/Plugins/CDN/Cloudflare.php. Almost everyone who went down was not using Cloudflare in any meaningful sense. Both public bug reports make this explicit: the reporter who filed it in July had no Cloudflare plugin installed at all, and the reporter who filed it on the morning of the outage had Cloudflare only as DNS and proxy, with no WordPress plugin involved. The module loads and runs regardless.

That is not a WP Rocket-specific criticism, and it would be dishonest to frame it as one. It is how nearly every large plugin is built. Third-party compatibility shims load on every request because working out whether they are needed is itself work, and because the check is often more fragile than just running the code. Page builders, security plugins, analytics integrations and caching plugins all carry directories of compatibility files for products you have never installed.

The practical consequence is that "I don't use that integration" is not the protection people assume it is. When you audit what could break your site during an update, the surface is the whole plugin, not the features you ticked. A site owner who reasoned "we're not on Cloudflare, so the Cloudflare bug can't be ours" reasoned sensibly and was wrong.

The report that sat for six weeks

The bug was filed publicly on WP Rocket's GitHub repository on 6 July 2026, during the WordPress 7.1 beta cycle. The report is unusually complete for a community filing. It names the file and line, explains that array keys from the filter registry can be integers because PHP casts numeric keys, gives reproduction steps, states the environment (WP Rocket 3.22.1, WordPress 7.1 alpha, PHP 8.3), and includes a one-line fix. It even notes that the problem was showing up on a core alpha but was not inherently limited to one.

It went unactioned for six weeks. WordPress 7.1 shipped on 19 August. Sites started falling over the same day. WP Rocket's status page records the incident being opened at 05:44 UTC on 20 August, a hotfix released at 08:14, and the incident closed at 10:15. The 3.23.2.2 changelog entry credits that same July issue number as the thing it fixed.

WP Rocket has not been evasive about it. The company said publicly that it is "running a post-mortem on how the July 6 report was triaged" and would explain the following week. It also said it had tested against 7.1 pre-release builds and asked for time to work out why that testing did not catch this. As of 22 August that post-mortem had not been published. It is worth reading when it appears, because the useful question is not whether someone missed a ticket, it is what a triage process looks like when a repository carries hundreds of open issues and one of them is load-bearing.

The reaction has been split, and both halves are right. One group pointed at the vendor: a premium product with a paid support obligation received a diagnosis and a fix and shipped the bug anyway. Another group pointed at the sites: a brand-new major WordPress release went straight onto production on day one with no staging pass and no rollback path, on a lot of sites at once, and that is a choice somebody made too.

An article that only makes the first point is a pile-on. One that only makes the second is blaming people for a bug they did not write. Both things happened, and only one of them is within your control.

What to change before the next major release

The advice to test on staging is correct, universal, and has been repeated so many times that it no longer changes anyone's behaviour. Here is the narrower version this specific incident supports.

Name your five riskiest plugins. Update risk is not spread evenly across your plugin list. It concentrates in code that hooks deep into the request lifecycle: caching, security, page builders, anything shipping a "compatibility" or "ThirdParty" directory. Those are where major-release breakage lives. A five-plugin watchlist you actually check is worth more than a policy of testing everything, because a policy of testing everything is a policy nobody follows past the second release.

Assume premium plugins are the gap in the safety net. Ecosystem-level automated testing runs against the WordPress.org directory, and premium plugins distributed from vendor servers are not in it. The plugins most likely to break a major release are frequently the ones least visible to the tooling meant to catch exactly this, because they are not in the directory at all. If your five riskiest plugins are mostly commercial, your exposure is higher than the ecosystem's safety nets can see.

Watch the tracker for those five during a beta cycle. This sounds like a job and it is one browser tab. The July report was public for six weeks. Anyone with WP Rocket on a watchlist who searched its open issues for "7.1" during the beta would have found it, and would have had the option to hold the update for a week.

Know how you get back into a site that will not load. This is the one that separates a ten-minute outage from a lost day. Every recovery route above assumes you can reach the filesystem without wp-admin. If you have never actually done that on the host you are on, you do not know how long it takes, and finding out for the first time while a client is calling is the worst possible moment.

On MagicWP that means SFTP access on every site, a one-click staging environment you can point at a release candidate, and daily off-site backups with one-click restore as the fallback if a plugin rename is not enough. Core updates run with rollback available, which changes the shape of a bad release day: the question becomes how quickly you noticed rather than how long recovery will take.

The honest version of the pitch is not a feature list, though. It is this: you should know today, before the next release, exactly what you would do if a site stopped loading and the dashboard was gone. If you do not know, that is the thing to fix this week, wherever you host.

What happens next

Two things are moving, and both deserve to be described accurately rather than optimistically.

Core has fixed the underlying type inconsistency, and the change is milestoned for 7.1.1, which is the next maintenance release. Once that ships, the class of plugin code that broke here will work again on 7.1 whether or not the plugin was updated. That does not mean you should wait for it instead of updating WP Rocket. Take the plugin release now.

Separately, the incident prompted a proposal on Trac from an independent core committer for a GitHub Actions workflow that would automatically test the most popular WordPress.org directory plugins against unreleased WordPress builds, so that fatal errors surface before a release reaches production. There is a draft pull request and the ticket is milestoned for 7.2. Four caveats belong in the same breath: it is a draft rather than a shipped thing, 7.2 is months away, the milestone may not hold, and its own author has pointed out that it would not have caught this incident, because WP Rocket is premium and the directory API only covers free plugins.

That is still worth having. The ecosystem is starting to build testing infrastructure that a platform this size should have had a decade ago. In the meantime, the person responsible for whether your site is up on release day is you.

Frequently Asked Questions

Which WP Rocket version fixes the WordPress 7.1 fatal error? WP Rocket 3.23.2.2, released on 20 August 2026. The changelog entry describes it as a fix for the fatal type error appearing after updating to WordPress 7.1 in some configurations. Any earlier version running on WordPress 7.1 is potentially exposed, depending on what else is active on the site. Version numbers move, so check WP Rocket's changelog before assuming 3.23.2.2 is still current; if a later release exists, take that instead.

My site is down and I cannot reach wp-admin. What is the fastest way back? Connect over SFTP or your host's file manager and rename /wp-content/plugins/wp-rocket/ to /wp-content/plugins/wp-rocket-off/. WordPress deactivates plugins it cannot find, so the site loads immediately. Log in, rename the folder back, then update to 3.23.2.2 and reactivate. Do not delete the folder. This is a one-minute operation once you have filesystem access, which is why having that access arranged in advance matters.

Do I need to use Cloudflare for this bug to affect me? No. The crash happens in WP Rocket's Cloudflare compatibility file, but that file loads on every request whether or not your site uses Cloudflare. Both public bug reports came from sites without the Cloudflare WordPress plugin installed. Ruling yourself out because you do not use the integration is the single most common misreading of this incident.

Should I apply the manual code fix from WP Rocket's documentation? Not any more. Those workarounds existed to bridge the gap before 3.23.2.2 shipped, and that gap has closed. Editing the plugin file directly is worse than doing nothing long-term, because the edit disappears at the next plugin update and the site can go down again with no obvious cause. If you already applied a workaround, update the plugin and then remove the workaround.

Is this a WordPress bug or a WP Rocket bug? Both, in different proportions. WordPress changed the type of a value inside a public property that plugins read, which is a backwards-compatibility break even though the property is not a documented contract. WP Rocket ran a string function on that value under strict types without guarding it, and had a detailed report with a suggested fix six weeks before release. Core is fixing its side for 7.1.1; the plugin has already fixed its side.

I run WP Rocket but have not updated to WordPress 7.1 yet. What should I do? Update WP Rocket first, then WordPress. That is the order WP Rocket recommended publicly, and it avoids the window where a 7.1 site is running unpatched plugin code. Take a backup before either step, and if you have a staging environment, run the sequence there first. There is no reason to rush 7.1 onto production; letting a major release settle for a week or two costs nothing.

Could other plugins have the same problem? Yes, and that is the reason core is patching its side rather than treating this as one vendor's mistake. Any plugin that reads WP_Hook::$callbacks and runs string operations on the keys is exposed, and profiling, debugging, security and caching plugins all do this. Most will coerce silently rather than crash, because most do not declare strict types. If you saw an unexplained fatal error on 7.1 pointing at a different plugin, this is worth checking.

Conclusion

The immediate answer to the WP Rocket WordPress 7.1 fatal error is a version number: update to 3.23.2.2, and if the dashboard is gone, rename the plugin folder over SFTP to get back in first. That part will be irrelevant within weeks.

What will still be true next year is the shape of it. A performance improvement in core changed the type of an internal value that a plugin was reading. Under PHP 8 that mismatch is fatal rather than quiet. The plugin's compatibility code for a service most affected sites were not even using ran on every request anyway. A public report with a working fix sat in a queue for six weeks. And a large number of sites took a brand-new major release straight to production with no way back that did not involve a support ticket.

Only the last of those is yours to change, and it is the cheapest one. Before the next major WordPress release, work out which five plugins on your site carry the most update risk, and confirm you can reach the filesystem of every site you run without needing wp-admin. If that second one is currently a maybe, managed WordPress hosting with staging, SFTP and one-click restore turns a bad release day into an inconvenience rather than an outage. Either way, decide it now, while nothing is on fire.

A
Alex
MagicWP
Writing about WordPress, performance, and the infrastructure that makes sites fast.

Get the best of MagicWP in your inbox.

Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.

Join 12,000+ builders. We send one email a month.