- Fixed: Slow Query Analyzer no longer suggests an index the table already has when several equalities in the WHERE line up with it.
WHERE post_type = 'x' AND post_status = 'publish' ORDER BY post_dateis served by core'stype_status_dateindex, so it gets no suggestion now. - Fixed: Joined queries get a WHERE index suggestion again. A filter on an aliased table such as
r.parent_object_id = 14was dropped, so a full scan on the joined table got no suggestion at all. - Fixed: Joined queries, self-joins included, no longer get a sort or
GROUP BYindex - on a join that index doesn't remove the temporary table or the filesort.
- Fixed: Slow Query Analyzer no longer suggests an index InnoDB already has. Every InnoDB index already ends with the primary key, so on
wp_postmetathe existingpost_idindex works as(post_id, meta_id), and the analyzer proposed a duplicate. MyISAM and Aria tables don't get that free suffix, so they still get the suggestion. - Fixed: Slow Query Analyzer only suggests an index for a sort or
GROUP BYwhen that index can actually remove it. Queries withINlists of several values, ranges,OR, joins, subqueries, expressions or mixed ASC/DESC sorts no longer get an index that leaves "Using filesort" in place. - Fixed: Slow Query Analyzer no longer suggests indexes MySQL refuses to create - on views, on columns too wide for the key limit, or on engines that can't index them.
- Improved: Tested up to WordPress 7.1.3.
- Fixed: Frontend Optimizer no longer emits a duplicate
deferattribute when another plugin (e.g. Bricks) adds defer. A tag like<script defer defer="defer">broke HTML parsers such as TranslatePress, so the script never loaded. Inline scripts attached to a deferred script are no longer touched either. - Fixed: Backups now find their private folder when the uploads folder is moved outside WordPress through a symlink and
open_basedirnames only the real path.
- Fixed: On hosts that restrict PHP with
open_basedir, such as Kinsta, every admin page logged a string of "open_basedir restriction in effect" warnings while the plugin looked for a private backup folder. It now checks what PHP is allowed to open before it looks, so the warnings are gone. Backups still go to the same folder as before. - Fixed: On a site whose WordPress folder is reached through a symlink, a private backup folder the host does allow could be skipped, and older backups were left in the public uploads folder. Both now work as on any other site.
- Security: If the saved backup folder was the web root itself and no private folder could be created, 1.9.8 switched new backups to the fallback folder but left the old ones in the web root. They are now moved out too. Protection files older versions wrote there are removed, but only exact copies of the plugin's own.
- Fixed: Switching from the full edition to Lite, or turning off the Autoloader Optimizer, while learning mode was on left autoload off for most options for good. The next page load now puts autoload back and ends learning mode, at no extra database cost.
- Fixed: If the database failed to answer while learning mode was ending, it could end with autoload still off. It now stays on and tries again an hour later.
- Fixed: A learning run started before 1.9.8 now switches autoload back on for every option it turned off, even if Quick Optimize or a Site Doctor fix ran during the run.
- Fixed: A crash, or a server still running old code right after the update, could leave learning mode stuck or lose the review notice. Each step now happens in an order that recovers on the next request.
- Fixed: A backup error could wrongly say the public-folder check could not run.
Security update - please update now. If WordPress sits in a subfolder of your web root (for example public_html/wp), Database Optimizer backups could be saved in a folder the web server publishes, and anyone who knew or guessed a file name could download them.
- Security: The backup folder is now always outside every folder the web server publishes when such a folder can be created. The search used to stop at the folder above WordPress, which in a subfolder install is
public_htmlitself. It now skips published folders and uses the next one up, and backups already sitting in a published folder are moved out on update. - Security: Backup folders get
.htaccess,web.configand empty index files on every backup, and they are re-created if they go missing. Before, they were written only when the folder was new, which for the Database Optimizer was never. - Security: Every backup file name carries 24 random characters, and downloads only serve files named that way. Older database dumps with a date-only name are renamed on update.
- Added: Old backups are deleted automatically - after 30 days, keeping the newest 5 - once a day and after every new backup. Change it under Backup retention on the Database Optimizer screen, 0 keeps everything. Config Manager backups follow the same rule, and pinned ones are never deleted.
- Added: A warning appears when backups end up in a public folder. Each new backup's URL is also requested (redirects within your own site are followed), and a file the server hands out (anything but 401, 403 or 404) is deleted and the cleanup is cancelled. If that request cannot be made, an admin warning says so.
- Fixed: Learning mode in the Autoloader Optimizer never ended, and the whole time it ran autoload stayed switched off for most options. It now stops after 7 days, puts autoload back as it was and shows a notice with the results and a button to review and apply them. Nothing is applied unattended, because learning only sees wp-admin traffic. Stop still applies at once. Installs that already had it on get a fresh 7 days from the update, and nothing is stopped.
- Fixed: Multisite sites each get their own backup folder. A folder that is the web root itself is never used for backups. A blank retention field saves the default instead of keeping backups forever.
- Fixed: Learning mode rewrote a 195 KB option on every admin request. Counters are now batched and written at most every 5 minutes, and nothing is loaded or written while it is off.
- Fixed: Options stored as
auto-onwere left out of the autoload sizes and counts in Site Doctor, System Info, WP-CLI and the Autoloader Optimizer screens. - Fixed: A build with an unrecognised edition marker no longer reports itself as the full edition when it checks for updates.
- Improved: Plugin Performance Score now covers about 53,700 plugins, up from about 4,800.
Update if you use Database Optimizer or clean transients. Two SQL pattern bugs in this release could touch rows they had no business touching - one deleted options that were not transients, the other ran OPTIMIZE TABLE on tables belonging to a different WordPress install sharing the same database.
- Fixed: Cleaning transients could back up and delete options that are not transients. An underscore is a single-character wildcard in SQL LIKE, so
_transient_timeout_%also matched names likextransient_timeout_y. Transient counts were inflated for the same reason. - Fixed: Database Optimizer could run OPTIMIZE TABLE on tables belonging to another WordPress install in the same database, because
wp_%also matcheswpXanything. Table stats listed those foreign tables too. - Fixed: Site Doctor no longer reports Performance Lab's Server-Timing drop-in as a dead object cache. Every site running Performance Lab got a critical finding telling the owner to delete
wp-content/object-cache.php, which only breaks Server-Timing measurement. - Improved: If you do want that drop-in gone, Site Doctor now names the constant that actually removes it -
PERFLAB_DISABLE_OBJECT_CACHE_DROPIN. Deleting the file alone does nothing, the plugin writes it straight back. - Added: Site Doctor checks four LiteSpeed misconfigurations - WooCommerce Product Update Interval, Guest Mode blocked by a
wp-content/.htaccessPHP deny-all, Serve Stale turned off, and WebP Express endpoints missing from the allow-list. Config is read through LiteSpeed Cache's own filter and nothing is written back. - Added: Frontend Optimizer can stop the empty-cart WooCommerce session cookie for logged-out visitors. That cookie forces a cache bypass on every anonymous hit behind a CDN or page cache. Off by default - switch it on under Frontend Optimizer. Skipped on cart, checkout, my-account and REST requests.
- Improved: Admin text below 14px has been raised across every screen, muted text now meets WCAG AA contrast, focus rings are visible when you tab, and click targets are at least 24px.
- Fixed: On the Dashboard Widgets screen the widget hook IDs were invisible until you hovered the row. They are readable at rest now, at every width.
- Fixed: The Updates screen rendered in the light WordPress theme instead of the plugin's own - white cards, blue buttons, and a heading you could barely read against the background.
- Improved: Checkboxes on plugin screens use the plugin's own toggle instead of the browser's blue and white box.
- Fixed: Icons inside buttons sat a few pixels below their labels.
- Fixed: Several screens scrolled sideways on a phone, and on the Find Slow Callbacks screen the main button was off-screen entirely.
- Fixed: Action Scheduler updates its stats and recommendations in place after Unstick, Run Queue and Clean. It no longer asks you to refresh the page.
- Fixed: The callback profiler's transient keys no longer sit exactly at the 64-character
option_namelimit.
- Added: Site Doctor's Agent access toggle now exposes six read-only WordPress Abilities (site-rescue-scan, plus quickstart, health, autoload, slow-queries, redis) so AI clients can call them. Off until you turn it on. WordPress 6.9 or newer; below that the toggle does nothing. They report findings and change nothing.
Update now if backups could not be written above the web root - older builds fell back to uploads, which a browser can read.
- Security: If the directory above WordPress exists but PHP cannot write to it, backups now fail instead of falling back to uploads. That fallback is how a .bak or .sql file ends up served as text.
- Added: Site Doctor can optimize what's safe in one click - expired transients, settings left behind by plugins you turned off, finished background jobs. It still will not touch payments, REST, cache files, or indexes. The scan itself stays read-only.
- Fixed: Site Doctor no longer reports Redis as down when Object Cache Pro (or another drop-in that keeps the Redis connection to itself) is answering. The INFO checks are skipped instead of turning into a critical.
- Fixed: Slow Query Analyzer's Apply Index no longer ships CREATE INDEX statements MySQL refuses. TEXT and BLOB get a 191-character prefix, utf8mb4 TINYTEXT gets 63, and the statement is rebuilt from your schema rather than run as the browser posted it - so old suggestions already in the log are repaired too.
- Fixed: utf8mb4 VARCHAR(255) on a COMPACT InnoDB table gets the same 191 prefix WordPress uses on meta_key, so Apply Index does not die with error 1071.
- Improved: On MariaDB, a JSON column can take a prefix index. On MySQL it still cannot, and is skipped.
- Fixed: The "Full table scan" notice on the Slow Queries screen is readable in dark mode.
- Improved: The Performance column on the Plugins screen looks up your installed plugins without loading the whole benchmark table into memory.
- Fixed: Site Doctor's one-click optimize records each step as it finishes, so a timeout during a long Action Scheduler purge still leaves an Undo that restores the autoload leftovers it actually changed.
- Improved: Tested against WordPress 7.1. Every module, admin page and CLI command runs clean on 7.1, with no deprecation notices and no changes needed on your side.
- Fixed: On a server where several WordPress installs share one parent directory, each install now keeps its own backup folder. In 1.9.2 they would have shared one, so every site's admin would have listed the other sites' wp-config backups, and a busy site's rotation could have pruned a quiet site's history. Backups written by 1.9.2 are moved into the right folder automatically.
Update now if you have ever used the Config Manager or the Database Optimizer - both wrote backups inside your web root, where a plain .bak or .sql file is served as text.
- Security: Backups now go above your WordPress install wherever your host allows it - both the wp-config.php copies the Config Manager keeps and the SQL dumps the Database Optimizer writes before a cleanup. The old directory relied on an .htaccess file, which nginx ignores. Existing backups are moved for you the first time you open wp-admin after updating.
- Security: On a multisite network, only a network administrator can change wp-config.php. One file serves the whole network, and the capability guarding it was the per-site one every subsite administrator has.
- Fixed: Saving a constant can no longer leave a truncated wp-config.php. Edits now use the same write-verify-replace sequence as a restore, so a crash or a full disk mid-save cannot take the site down, and an unreadable wp-config.php stops the save instead of being written back empty.
- Added: See what a backup would change before restoring it. Each row says whether it matches your current file or how many lines differ, and View changes lists the exact lines a restore would bring back and drop. Passwords, keys and salts are masked.
- Added: Delete a backup you do not want, and pin one you do. A pinned backup is never pruned and does not count against the ten kept copies.
- Improved: All six languages updated with every new string in this release.
This is a security release. Update it.
- Security: A crash no longer tells the visitor anything about the crash. When a plugin or theme fatals, older versions handed the error message, the full file path and the stack trace to whoever loaded the broken page - including visitors who were never logged in. None of that reaches anyone but you now.
- Security: Recovery Mode is only ever offered to a logged-in administrator, and only through WordPress's own signed link. Everyone else gets the standard error page with nothing in it.
- Fixed: A fatal error now shows WordPress's normal "There has been a critical error" page instead of a blank white screen. The plugin's error template was never actually rendering, so some crashes returned an empty response.
- Improved: A helper file the plugin no longer uses is deleted from your site when you update, instead of being left behind forever.
- Improved: Param and return types across the codebase. Nothing changes for you - this is groundwork that lets the tooling catch a class of bug before it ships.
- Added: Restore a wp-config.php backup. The Config Manager has backed up before every change since 1.8.0, but there was no way to put one back. A Backups list on the Config Manager page now shows the last ten, each with a Restore button, and your current file is backed up first so you can undo the undo.
- Added:
wp multitool config list-backups,wp multitool config backupandwp multitool config restore <name>do the same from the command line. - Added: A rescue command for a site that will not load. A wp-config.php with wrong database credentials kills WordPress before it starts, which takes wp-admin and the normal CLI command with it. Run
wp --require=wp-content/plugins/wp-multitool/cli-rescue.php multitool-rescue restore <name>from your WordPress root and it restores a working config with the database unreachable. Worth writing down somewhere that is not the site. - Improved: A restore will not write anything it cannot verify - the backup must be valid PHP and look like a real wp-config, and the file is read back off the disk and checked again before it goes live.
- Improved: wp-config.php keeps its file permissions through a restore, and there is no point at which a readable copy of your credentials sits in the web root.
- Fixed: Saving a setting without changing it no longer creates a backup. Ten no-op saves used to fill the ten-backup window with identical copies and push the real history out.
Security fix - update if you have ever used the Config Manager.
- Security: Config Manager wrote its wp-config.php backup to
wp-config.php.backupin the site root, where a web server hands it over as plain text. Backups now go to a protected folder under uploads with an unguessable filename, and the exposed copy is deleted automatically the next time you open wp-admin. - Fixed: Config Manager now backs up before every edit and keeps the last ten, refuses to write if the backup cannot be saved, and validates the PHP syntax before touching wp-config.php.
- Added: Duplicate optimization detector - flags Frontend Optimizer toggles that WP Rocket, Perfmatters, Autoptimize, Easy Optimizer or Admin and Site Enhancements are already doing, with a "Turn off here" button next to each one. Also reported by Site Doctor as "Duplicate Optimizations". It never writes to another plugin's settings.
- Improved: The Performance column on the Plugins screen now shows each plugin's own overhead instead of absolute figures that included WordPress's own baseline. Plugins with no benchmark data say so rather than showing an invented score.
- Improved: Find Slow Callbacks now credits each callback with its own work instead of blaming it for everything it triggers, with the full including-everything total shown underneath where the two differ.
- Fixed: Site Doctor recognises an nginx page cache by its HIT/MISS value rather than the header name, and no longer treats a deliberate BYPASS on the homepage as a fault.
- Added: Docs and "Report a bug" links under WP Multitool on the Plugins screen, plus documentation, issue tracker and support links in the plugin footer.
- Added: Two anonymous usage counts - scans run and findings resolved - ride along on the licence check the plugin already makes twice a day. No new network request, no site content, no personal data. Opt out on the Updates screen.
- Improved: Fully translated into German, Spanish, French, Italian, Polish and Brazilian Portuguese.
Your Redis object cache can say "Connected" and still be taking your site down. This release makes Site Doctor prove it either way.
Redis diagnostics that actually diagnose
Almost every Redis health check in the WordPress ecosystem - Site Health, the object-cache plugins' own status screens, most hosting dashboards - answers one question: did the connection succeed? They open a socket, send a PING, get a PONG, and report green.
That test cannot fail in the ways Redis actually fails. A Redis server can answer instantly while it is:
- rejecting every write it receives, because it hit its memory ceiling under a
noevictionpolicy; - throwing keys away as fast as WordPress writes them, because the working set does not fit;
- serving another website's cached data, because two installs share one keyspace with no prefix;
- caching nothing that matters, because the
optionsgroup is on the ignore list; - costing more time than it saves, because the round-trips outweigh the database work avoided.
In every one of those cases the drop-in still reports "Connected", and the site is slower - sometimes catastrophically slower - than it would be with no object cache at all.
While building this we reproduced the worst case on a real server: an 8MB Redis filled to its ceiling under noeviction. The drop-in did not degrade quietly. It threw during bootstrap and WordPress died - HTTP 500, the whole site - while every "Connected" indicator stayed green throughout.
Site Doctor now runs 21 Redis checks that talk to the server and read its running configuration: memory ceiling and headroom, eviction policy and eviction rate, hit rate, key isolation, cache group configuration, replication topology, disk persistence, connection reuse, memory fragmentation and round-trip latency. Each one carries its own severity and its own fix.
Safe by construction
The checks are read-only and run only when you ask - from the scan button or WP-CLI, never on a normal page load. They use INFO, CONFIG GET, DBSIZE and a single bounded SCAN. They never use KEYS, which would block your server for the length of your entire keyspace. A diagnostic that causes an outage is not a diagnostic.
Where your host blocks CONFIG GET - Kinsta, WP Engine and Cloudways among them - the affected checks say "cannot verify this here" and tell you how to find out yourself. They never report "ok" for something they could not measure. No data is not the same as no problem.
Also in this release
- New:
wp multitool redisruns the same diagnosis from the command line, bringing the CLI to 15 subcommands. - Improvement: Site Doctor no longer reports the same autoload problem twice, no longer calls a 3-byte saving an optimization, and no longer lists slow queries that have no fix to apply.
- Fix: Site Doctor's findings no longer link back to the page you are already on.
- Fix: The Autoloader Optimizer's "optimize now" nag no longer appears on the Site Doctor page, which already tells you the same thing.
- Fix: External Call Optimizer no longer optimizes calls your site makes to itself. If something polls your own REST API often enough to look "chatty" - WPML's health checks do exactly this - the optimizer treated your own site as a third-party phone-home and cut its timeout to 5 seconds, which fails under load and surfaces as a false "REST API must be enabled for WPML" warning. A loopback to your own site was never a phone-home and is now never touched. Thank you to the site owner who reported it.
- Fix: The download no longer contains development files that were never meant to ship - local editor settings and a test cache. They were inert and contained no credentials, but they had no business being in the zip.
Full technical reference for every check, including thresholds and what each one deliberately does not claim, is in the plugin repository’s docs/ folder on GitHub.
Version 1.6.3 turns Site Doctor into a single answer to "why is this site slow?", and fixes a CLI bug that broke scripting against the plugin.
Site Doctor now scans the whole site
One click on Find what's slow checks autoload burden, database bloat and captured slow queries alongside this site's configuration: OPcache, the object-cache drop-in, page caching, LiteSpeed and WooCommerce payment gateways. Everything comes back in one list, ranked worst-first, and each finding links straight to the module that fixes it. It stays read-only and only runs when you ask it to - never on a normal page load.
New installs land on it right after activation instead of on System Info, so the first thing you see is where to start rather than a grid of 19 modules.
A specific object cache for your server, not "add Redis"
Site Doctor now looks at what your box can actually run and names the backend and the plugin to install. On shared hosting with no cache server to connect to, that means SQLite Object Cache - which needs no server at all - instead of Redis advice that goes nowhere. And if the plugin it recommends is already installed, it tells you to enable it rather than install it again.
Lists lead with what you can act on
Site Doctor and the Slow Query Analyzer now put the things you can actually do something about at the top, and collapse the rest - findings with nothing to fix, queries already fixed or with no index to apply - into a group you can expand. A query you'd marked as fixed no longer shows a Fix button.
CLI fixes worth knowing about if you script against it
- Fixed:
wp multitool autoload --format=jsonprinted a human-readable line before the JSON, so piping it tojqfailed. It's machine-readable now, like every other command. - Changed:
--format=jsonprints compact JSON instead of pretty-printed, so it pipes and stores cleanly. Pipe it through| jq .when you want to read it. - New:
wp multitool quickstartruns the same whole-site scan from the command line.
No more third-party CDN in wp-admin
The admin pages used to pull a JavaScript library from a public CDN. It now ships with the plugin and is served from your own site, so wp-admin makes no external request for it - and the interactive pages work on locked-down hosts where wp-admin can't reach the public internet.
Updating is all that's needed. No settings change.
Version 1.6.2 is a security and reliability maintenance release. We recommend every user update.
Security & hardening
Following a full internal security review of the plugin, this release tightens permission and request-validation checks on a number of admin actions and closes several edge cases across multiple modules. There is nothing you need to change or configure — the hardening is entirely internal.
Safer clean-up operations
A round of correctness and safety fixes to the modules that modify your database and files — the Database Optimizer, Autoloader Optimizer and Image Manager in particular. Destructive actions now fail safe (if a safety backup can't be written, nothing is deleted), and several operations that could quietly do nothing on the latest versions of WordPress now behave correctly.
No settings changes are required. Updating is all that's needed.
Version 1.6.1 is a fix release. The most important change: the External Call Optimizer (new in 1.6.0) will never again go near your payment or checkout traffic.
Payment traffic is now completely off-limits
In 1.6.0 the optimizer trimmed the timeout on outbound hosts it saw called repeatedly, to stop a slow phone-home server dragging down your admin. On a busy store a payment gateway could get caught by that and have its request timeout cut to a couple of seconds — long enough to fail a legitimately slow payment during checkout (for example "cURL error 28: Operation timed out"). That was never the intent: the module is for background update and license traffic, not commerce.
- Never touches payments. The optimizer now skips non-GET requests entirely (payment charges are POST), bails out of any request made during a WooCommerce or EDD checkout, and carries a built-in list of common payment hosts — Square, Stripe, PayPal, Braintree, Adyen, Mollie, Klarna and more — that it will never optimize.
- Your own exclude list. A new "Never optimize these hosts" box lets you exempt any domain yourself, with wildcards like
*.example.com. There's also a developer filter (wpmultitool_phg_excluded_hosts). - Opt-in, and gentler. The module now ships disabled by default — anything that alters outbound HTTP should be a conscious choice — and its timeout clamp is gentler (5 seconds, matching WordPress's own default) and only applies to hosts seen repeatedly.
If you were affected, updating to 1.6.1 resolves it. You can also simply leave the module off — it stays off after this update until you turn it on.
Also fixed
- Admin tables no longer overflow. Wide cards (including the WooCommerce Order Meta and Custom Tables reports) used to push the page sideways on narrow layouts and long keys were clipped. Long keys now wrap, wide tables scroll within their own card, and table text that could render unreadably on the dark admin theme now meets WCAG AA contrast.
- Menu hardening. A stale server-side code cache after an update can no longer leave a module's page unreachable with a "you are not allowed to access this page" error.
Version 1.6.0 adds a new Pro module — the External Call Optimizer — that fixes one of the sneakiest causes of a slow WordPress admin: update checks that fire on every page load instead of twice a day.
New module: External Call Optimizer
WordPress checks api.wordpress.org for plugin, theme and core updates about twice a day. That schedule is held by a 12-hour timestamp stored inside WordPress's update transients. When a persistent object cache silently drops those transients — a dead Redis, a backend that flushes on every request, an orphaned drop-in — the timer vanishes and WordPress starts firing blocking update requests to api.wordpress.org on every single admin page load, 3–5 seconds each. Your admin crawls and nobody can see why.
- Keeps the update-check gate reliable. The optimizer mirrors the last good update-check timestamp into its own dependable store and re-injects it when the cache loses it, so the 12-hour gate holds and checks drop back to roughly twice a day.
- Faithful by design. It only ever restores a timestamp WordPress itself wrote, never re-injects one older than 12 hours (past that it lets a real check run), honours a manual "check now", and never touches api.wordpress.org connectivity — so security updates are never delayed or hidden.
- Trims chatty third-party phone-homes. Premium-plugin license pings and telemetry that hammer the same endpoint get an automatic timeout-clamp (a proven-slow host costs about 2 seconds instead of 30) and a brief cache on repeated identical polls. No per-host rules, nothing to configure.
- Safe by default. api.wordpress.org and WP Multitool's own updater are always left alone, streamed downloads bypass the cache, the request method is part of the cache key, and anything carrying cookies or an Authorization header is never cached.
It's a silent optimizer, not a monitor — there's no request log, no database tables, and no scan to run. One status card tells you whether the gate is holding and how many redundant checks it has prevented, with a switch to pause it. External Call Optimizer is a Pro module.
This release adds leftover-table detection to the Database Optimizer and sharpens the orphaned-data report.
- New — find tables left behind by uninstalled plugins. The Database Optimizer now scans for custom database tables and adds a "Custom Tables" card listing every non-core table with its size and row count. It works out who owns each table by searching the actual source of your installed plugins, mu-plugins, drop-ins and active theme for a reference to it — so tables that no installed code uses are flagged "no matching plugin installed (manual review)", the likely leftovers of a deleted plugin. This report never deletes or drops anything.
- Improvement — orphaned meta grouped by size. The Orphaned Data preview now groups orphaned post/comment/user meta by key with byte totals, not just row counts, so you can see which leftover meta keys are actually consuming space.
- Accessibility. Database Optimizer — the info (i) buttons on every card now have screen-reader labels, and the scrollable table lists are keyboard-focusable.
This release is a round of fixes and polish, most of it from customer reports. Thanks to everyone who flagged something.
Fixes
- Image Manager (Image Sizes) works under the Tools menu. When WP Multitool's menu was nested under the native Tools menu, the Image Sizes screen's buttons — Find Sizes to Disable, Reclaim Space, the per-size toggles — were inert because the page's scripts didn't load. They now load in both menu locations.
- Config Manager shows feedback again. Saving or removing a constant (and security errors) now shows a visible success/rejection notice. Previously the message area wasn't wired up, so actions gave no on-screen confirmation.
- Autoloader Optimizer — Save Thresholds and option editing work. Both were silently rejected because the security token wasn't included in the request.
- Autoloader Optimizer — the Options List Edit popup no longer closes on the first keystroke. You can type into an option's value normally now; pressing Escape still closes the popup. Previously any key press while editing dismissed it, making edits impossible.
- The "Send Feedback" button is no longer a dead end. It could appear un-clickable (with a "not-allowed" cursor) and no explanation when no star rating was selected. It's now always clickable and tells you what's missing, the rating shows a "(select a rating)" hint until you pick one, and the stars are keyboard-accessible.
Accessibility
- Image Sizes screen contrast + labels. Each size's on/off toggle now has a descriptive label for screen readers, and the analysis badges, status, and text on flagged/disabled rows now meet WCAG AA contrast (they were dimmed below readability).
Safety
- Database Optimizer — clearing orphaned WP Cron hooks now backs up first. It writes a restorable backup (with a download link) before removing anything, matching every other cleanup action. Previously this was the one cleanup that deleted without a restore point.
This release puts the admin menu where you want it, makes every notice readable, and fixes a dead link — plus a small quality-of-life touch.
New: Choose where the menu lives
- WP Multitool can now sit in its own top-level menu (the default) or nested under the native Tools menu — your call. Switch it in Preferences on the WP Multitool screen.
- Enabling or disabling a module updates the menu live in either location, with no page reload.
Fixed: readable notices everywhere
- The "Debug logging is ON in production" warning (and notices in general) could render with near-invisible body text on the dark admin theme. Notices now use a single shared, contrast-safe style, so the text is always legible.
Fixed: no more dead link on disabled modules
- A disabled module's card title used to be a link that led to a "Sorry, you are not allowed to access this page" screen. The title is now a link only when the module is enabled, and it updates the instant you toggle it.
Tweak
- The plugin version is now shown next to the WP Multitool Settings title.
A small release driven by user feedback: move your configuration between sites in one click, and stop two false alarms on managed hosts.
New: Import / Export settings
- Export your entire WP Multitool configuration to a JSON file from the new Import / Export screen.
- Import it on your other sites to clone your set-up instantly — no more reconfiguring each module by hand.
- Licenses, API keys and other secrets are stripped out automatically, so a shared file never leaks credentials or overwrites the destination site's licensing. Imported values merge over the target's settings without touching its own per-site state.
Fixed: false alarms on managed hosts
- Object cache: Kinsta, WP Engine, Cloudways, Pantheon and Flywheel all offer Redis as an add-on. Site Health no longer shows a "change hosts" critical on these — it now nudges you to enable your host's Redis add-on. The critical is reserved for hosts that truly can't cache.
- OPcache: some managed hosts (Kinsta, WP Engine) restrict PHP's OPcache status API, which made WP Multitool think OPcache was off when it was actually running. The check now falls back to the php.ini setting when live stats can't be read — and still warns when OPcache is genuinely disabled.
WordPress has no built-in way to cap or dedupe wp-content/debug.log. One plugin throwing the same PHP deprecation on every request is enough to grow that file to gigabytes and fill your disk — and once it's that big, you can't even open it to find out what happened. 1.5.0 adds a module to deal with exactly that.
New module: Debug Log Guard (Pro)
- See the real culprit in seconds. It streams the log without ever loading it into memory and shows a grouped, deduped table — message, how many times it fired, which plugin or theme it came from, first and last seen. The worst offender sits at the top.
- Compact safely. Compact collapses thousands of repeated lines into one annotated entry while preserving your warnings and fatals. In testing a 404 KB spam log came out at 301 bytes. Every action is backup-first and atomic (temp file + rename), so a failed run never corrupts the log.
- Rotate and Clear too — both keep a backup first — plus a one-click download of the current log.
- An hourly size guard watches the file and compacts, rotates, or clears it when it passes a threshold you set. The check only reads the file size, never the file, so it can't slow your site down.
- It finds the right file.
WP_DEBUG_LOGdoesn't always meanwp-content/debug.log— Debug Log Guard resolves the real active target (honouring WP_DEBUG and php.ini'serror_log), and refuses to act on something it can't safely manage, like/dev/stderr. - It flags the root cause. If the site reports as production and debug logging is on, you get a clear warning. That's almost always why these files balloon, and a web-reachable
debug.logcan leak file paths and internals. WP Multitool's job is to surface the obscure-but-important stuff, and this is exactly that.
Built for the log that's already huge
- Before compacting, it checks free disk and refuses if there isn't room for the temp copy, pointing you at Rotate or Clear instead.
- Past a hard ceiling it rotates rather than compacts, because compacting a multi-GB file under a time limit is the risky path.
- Compaction runs against a time budget and aborts with the original left intact if it would overrun. A multi-GB log is summarised from its most recent slice, so the viewer never hangs.
From the command line
Everything is in WP-CLI, and it works even when wp-admin is unreachable — which is usually the case when the disk is full:
wp multitool debug-log status— where the log is, how big, and whether WP_DEBUG is onwp multitool debug-log summary— the grouped, deduped tablewp multitool debug-log compact— backup, then collapse the repeatswp multitool debug-log clear— backup, then empty it
For people who run a lot of sites
If you maintain client sites, you don't want the first sign of a full disk to be a support ticket from someone whose small blog went down. So 1.5.0 also adds wp multitool healthcheck — a fast check that returns a real exit code (0 ok, 1 warning, 2 critical) and --format=json. Drop it in a cron across every site and you'll know about a runaway debug.log, debug logging left on in production, or autoload bloat before your client does:
wp multitool healthcheck || notify-me— non-zero exit means something needs attention
Debug Log Guard reads and post-processes the log; it doesn't yet prevent the duplicate writes at the source. That's the next step.
A small maintenance release: one front-end fix that prevents stale assets behind aggressive caches, plus a round of internal hardening.
Fixed
- “Remove Version Strings” is now opt-in (off by default): stripping
?ver=can defeat cache-busting behind an aggressive CDN or page cache — after an update the old, version-less asset can keep being served. It already never touched wp-admin; now it stays off until you deliberately turn it on.
Hardened
- The Autoloader Optimizer options view no longer instantiates objects when previewing serialized option values (
unserializenow usesallowed_classes), and internal table-existence checks use prepared statements.
Meet Site Doctor — find the misconfigs that quietly cost you speed
The headline of 1.4 is a brand-new module: Site Doctor, an on-demand health scan that catches the production misconfigurations that don’t show up anywhere else — a thrashing OPcache, an object-cache drop-in pointing at a dead Redis, a page that misses the cache on every hit, a WooCommerce gateway still stuck in test mode — and hands you the exact fix for each one. Alongside it: a WP-CLI rescue kit for when wp-admin won’t even load, a one-shot N+1 query probe, and profiling tools that now ship off by default so a fresh install adds zero frontend overhead.
New
- Site Doctor — on-demand health scan with fixes: a new module that runs only when you click Run scan (or
wp multitool doctor) — never on a page load. It checks OPcache health (OOM restarts, low hit rate, full key table), the object-cache drop-in (a drop-in pointing at a dead backend is flagged critical), LiteSpeed misconfiguration, page caching via a single loopback probe, and WooCommerce payment gateways stuck in test/sandbox mode on a live shop. Every finding comes with concrete remediation — a sizedphp.inisnippet from your live numbers, the exact gateway setting to flip — not generic advice. A staging-guard quietly downgrades severities on dev hosts so your.locclone doesn’t cry wolf. It deliberately doesn’t duplicate transient cleanup or cron chasing — those stay in Database Optimizer and Site Health. Built from real client optimization fires. - Autoload rescue from WP-CLI:
wp multitool autoload analyzeclassifies your autoloaded options and flags the bloat,optimizedisables autoload for safe candidates (after writing a backup), andrestoreputs every flag back exactly as it was. Built for the moment wp-admin is dying of memory exhaustion and the dashboard is the one place you can’t reach. - Module control from WP-CLI:
wp multitool modules list,enable <module>anddisable <module>. Disabling a module also cleans up its scheduled events and any files it deployed; enabling redeploys them — so the command line stays in sync with the admin toggles. - One-shot N+1 query probe:
wp multitool slow-queries probefires a single token-gated request at your front end with query logging on, then shows you which queries repeated, how long they took, and candidate indexes — without leaving query logging running on a live site. The cleanest way to catch a plugin firing the same query 50 times on one page.
Improved
- Profiling modules ship off by default: on a fresh install the Slow Query Analyzer and Find Slow Callbacks now start disabled, so the plugin adds no per-request profiling cost until you deliberately turn it on. Existing sites keep whatever you already had enabled.
- Low-memory warning: health checks and CLI commands now warn when the site’s configured memory limit is low, with the exact constant to raise in
wp-config.php. - Honest autoload expectations: the docs now spell out when autoload optimization actually moves TTFB (large autoload set, no persistent object cache) versus when the win is mostly memory — so you optimize the right thing first.
Fixed / Hardened
- Crash recovery can never be stripped: the Fatal Error Recovery files are now a deliberate always-on safety net, excluded from module ownership — disabling any module can no longer remove your site’s crash recovery.
- No zombie artifacts: on activation the plugin no longer re-deploys mu-plugins or drop-ins belonging to a module you’ve disabled.
- Trustworthy restore counter: autoload restore reports the exact number of options it put back, not an estimate.
Reclaim the image storage you can’t see
The Image Manager grows up. It now finds the image sizes nothing on your site actually uses, then lets you delete the generated files to win back disk — reversibly, and without ever touching your original uploads. Plus a friendlier loader while the heavy tools think.
New
- Image Manager — Reclaim space: one click deletes the generated files for unused, duplicate or disabled image sizes, per row or in bulk. It’s fully reversible — hit Regenerate to rebuild them — and your original uploads are never touched.
- Image Manager — smarter unused detection: a new “Content refs” column looks past the post thumbnail and scans post content, Gutenberg blocks, Elementor data, widgets and theme files before a size is ever called unused, with an honest “verify before disabling” nudge.
- Animated brand loader: the Autoload Optimizer and Database Optimizer overlays now show an animated “M” while they work, so a long scan no longer looks like a frozen screen.
Improved
- Reference scan hardened for big libraries: the unused-size detection runs on demand, keeps its memory footprint low, and caps the rows it walks — so it stays responsive on large, slow sites instead of stalling on a huge media library.
Maintenance release
Three bug fixes: a WordPress 7.0 block-editor regression, a Plugin Profiler stall on LiteSpeed hosts, and an invalid index suggestion in the Slow Query Analyzer.
Bug fixes
- Frontend Optimizer: could break the block editor (a blank "Add Post" screen) on some sites. Its front-end output optimizations (script deferring, moving scripts to the footer,
?ver=stripping,wp_headcleanup) ran on every request — including wp-admin, the REST API, AJAX and cron. They are now confined to genuine front-end page views, and script deferring/relocation never touches a page that loads the block editor (which also covers front-end editing plugins). Reported on WordPress 7.0. - Plugin Profiler: fixed a stall on heavy LiteSpeed-hosted sites.
- Slow Query Analyzer: no longer suggests indexes on SELECT-clause column aliases, which aren't real columns and can't be indexed (issue #8).
Security release
This release ships one P1 (XSS) and one P0 (OOM) reported by the supply.family security audit, plus a P0 production fatal in Image Manager.
Security
- Image Manager: escape size names through
esc_html()in the size-details modal before output. Custom image size names containing HTML/JS no longer execute in the admin (P1 XSS, supply.family audit).
Bug fixes
- Image Manager: fatal
Call to undefined function get_current_screen()on front-end and under WP-CLI. The admin bar hook now guards onfunction_existsbefore resolving the screen (production Recovery Mode trigger on supply.family). - Image Manager: out-of-memory crash in "Find Unused Sizes" scan on sites with large
wp_poststables. Replaced the unboundedGROUP_CONCAT(post_content)with paginated batches (100 posts at a time) and early-exit per size (P0 OOM, supply.family audit).
Internal (no behavior change)
- Slow Query AI Analyzer admin assets extracted from inline
<style>/<script>blocks into properly enqueued files (assets/css/sqaa-admin.css,assets/js/sqaa-admin.js). Better caching. - Auto Updater methods renamed snake_case → camelCase across 4 files (
get_edition,get_license_key,set_license_key,get_state,set_state,clear_cache). Consistent with the rest of the codebase.
Object cache label fix
The Site Health OBJ CACHE badge sometimes labeled SQLite Object Cache as "Redis" because the drop-in scanner matched the word "redis" in plugin comments before "sqlite" in the code.
Detection now checks active plugin slugs first (sqlite-object-cache, redis-cache, etc.) and only falls back to keyword scanning by earliest position in the file — so the label matches the actual cache backend in use.
Improved object cache recommendations
The Site Health module now follows a smarter cascade when recommending a persistent object cache:
- Redis available → recommend the Redis Object Cache plugin (unchanged).
- No Redis, but SQLite extension present → recommend the SQLite Object Cache plugin as a solid fallback.
- Neither available → flag the host as unsuitable and link to hosting recommendations.
Previously the plugin pushed Redis only, leaving users on hosts without Redis without a clear next step.
- Fix: Auto Updater no longer shows a stale "update available" notice after a successful upgrade. The injector now compares the cached
new_versionagainst the installed version and clears its 6-hour cache the moment our plugin finishes upgrading. - Fix: Auto Updater self-heals when WordPress's cached package URL has expired. Previously you'd see "The signed download URL has expired" and have to manually click "Check for Updates Now". Now the plugin refreshes the URL inline and the upgrade just works.
- Reported by Bojan — three of his six sites stuck displaying the 1.2.1 update notice while already on 1.2.1. Thanks for the heads-up.
- Fix: Slow Query Analyzer's "Apply Index" suggestions no longer mangle table names on sites with custom
$table_prefixvalues that begin withwp_(e.g.wp_plugins_). The CREATE INDEX SQL now targets the actual table from MySQL EXPLAIN instead of trying to "fix" it. - Bonus: If you're reading this in WordPress Updates rather than on the site, congratulations — your Auto Updater works.
- New: Auto Updater module — pulls plugin updates from wpmultitool.com using your Polar license key. No more manual zip uploads.
- New: License & Updates admin page (Pro only) — paste your license key once, then updates flow in via the standard WordPress Updates screen.
- New: One-click "Check for updates now" button bypasses WordPress's 12-hour transient cache.
- Security: Update server uses short-lived signed download URLs (HMAC-SHA256, 30-minute TTL) so zips can't be hot-linked.
- Fix: Slow Callback Finder and Slow Query Analyzer table creation now works reliably across all hosting stacks (LiteSpeed, MariaDB, custom
$table_prefix). Replaced WordPress's flakydbDelta()with an explicit Schema_Manager that captures the real MySQL error and falls back through 4 charsets and 2 storage engines. - Fix: "Cannot create database table. Check database user permissions." error replaced with the actual MySQL error message, so you can see the real cause when setup fails.
- Fix: Upgrade no longer hangs when WordPress maintenance mode briefly takes the admin URL offline mid-refresh.
- Change: Lite edition skips the Auto Updater entirely (no spurious update rows, no server hits).
- New: Action Scheduler Optimizer — detects and cleans bloated action queue, shows pending/failed/completed action stats, one-click cleanup with configurable retention
- New: Image Manager — smart size analysis detecting duplicate and unused registered image sizes, with usage stats and cleanup recommendations
- Fix: 9 bugs resolved from code review — critical, high, and medium severity issues across multiple modules
- New: Plugin Performance Score module — adds a Performance column to the Plugins page showing benchmark scores, memory usage, and query counts for ~5,000 plugins (data from makewpfast.com)
- New: Security Headers — adds X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, and Strict-Transport-Security
- New: Sensitive File Removal — auto-deletes readme.html and license.txt (re-deletes after core updates)
- New: User Enumeration Protection — blocks REST API /wp/v2/users and ?author=N for unauthenticated visitors
- New: Login Attempt Limiter — locks out IPs after 5 failed attempts for 15 minutes
- Change: Frontend Optimizer Security section expanded from 1 to 5 toggles
- Fix: Autoloader Optimizer false positives — options from active plugins (EmailKit, Fluent Forms, ACF, Yoast, etc.) no longer incorrectly flagged as orphaned
- Change: Orphan detection now uses get_plugins() with TextDomain metadata instead of guessing prefixes from folder names
- Change: Removed dangerous 2-3 character abbreviated prefix matching that caused false matches
- Change: "Orphaned" category renamed to "Inactive Plugin" — only flags options from deactivated plugins
- New: "Unrecognized" category for options that don't match any installed plugin (informational only, not auto-optimized)
Improvements
- Autoload Optimizer: Fixed optimization card layout - button and description now on separate lines for better readability
- Autoload Optimizer: Added info dialog explaining detection categories (Orphaned, Oversized, Bloat Patterns) with specific rules
- Autoload Optimizer: Added dark theme CSS overrides for the optimization card
- Replaced Datastar PHP SDK with PHP 7.4-compatible single-file implementation
- Fixed memory detection to use WP_MEMORY_LIMIT instead of ini_get
- Fixed memory limit display consistency between dashboard and Config Manager
- Removed AI references from user-facing text
- Added feedback widget with star rating on main settings page
- Feedback emails sent directly to developer via SMTP
- Fixed DataStar PHP SDK not deploying to production (gitignore fix)
- Hidden Upgrade menu in full edition when running from git
- Slow Callback Finder now enabled by default
Bug Fix
- Fix fatal error on activation when hosting provider disables symlink() in PHP (shared hosting compatibility)
- Improved fallback: versioned assets now use file copy when symlink is unavailable
- Added: WP-CLI commands for safe CLI access without wp eval
- Added: Dual-edition conflict prevention
- Added: Pro Features upsell module
- Improved: ClawHub agent skill rewritten to eliminate all wp eval
- Improved: Build system for Lite/Pro edition packaging
- Fix: Fatal error on activation when symlink() is disabled by hosting provider
- Fallback: Copy assets instead of symlink on restricted hosts
- Fix: HTTP/2 detector infinite page reload loop on HTTP/1.1 sites
- Fix: Autoload Optimizer usage table unreadable text in dark theme
- Fix: Config Manager read-only notice now shows file owner and permissions
- Fix: CSS notice styling specificity to avoid overriding plugin-specific notices
- License updated to Proprietary
- Security: Replace addslashes() with var_export() for PHP code generation in Config Manager
- Fix DELETE subquery race conditions in slow query analyzer and slow callback finder
- Add config file caching to reduce file reads
- Improve symlink creation with proper error logging
- Add .releaseignore for cleaner release packages
- Improve Admin UI styling and consistency
- Add additional CSS for better module card presentation
- Major Admin class enhancements
- Improved asset versioning with cache-busting timestamps
- Admin class improvements and code cleanup
- Add service listing assets and screenshots
- Remove deprecated playwright screenshot files
- Compat: WordPress 6.6+ autoload compatibility - supports new on/off/auto values
- New: Updated module screenshots with fresh UI and real data
- Perf: Optimized autoload database queries
- New: Site Health dashboard with clickable stat pills
- New: Brutalist terminal UI redesign across all modules
- Fix: Frontend Optimizer info cards now link to relevant modules
- New: Slow Query AI Analyzer with Anthropic/OpenRouter integration
- Fix: Fixed ORDER BY / GROUP BY column parsing in query analyzer
- Perf: Reduced memory usage in large database scans
Ready to optimize your WordPress site?
Join developers using WP Multitool to debug, optimize, and maintain WordPress.
Get WP Multitool