A client site I looked at last week took 1,420ms just to return the first byte. It wasn’t an overloaded server, slow PHP execution, or uncompressed media—it was 14.8MB of garbage sitting in the wp_options table, flagged to autoload on every single HTTP request.
Here’s how I audit the table using WP-CLI, pinpoint the specific plugins dumping megabytes into memory, flip the flags that choke page loads, and safely ditch orphaned rows.

How wp_options Autoload Kills WordPress TTFB
On every uncached request, WordPress runs wp_load_alloptions() during early initialization. It fires this exact query:
SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'If that query pulls back 500KB of data, PHP allocates the memory, unpacks the strings, and caches it in runtime memory in a few milliseconds. But when uninstalled plugins, abandoned transients, and old themes bloat that payload to 8MB or 15MB, MySQL burns 100ms+ just reading the disk. Then PHP burns another 200–300ms trying to unserialize huge arrays before your theme code even runs.
In practice, autoloaded data should sit somewhere between 300KB and 800KB. Once you push past 1MB, TTFB starts climbing. At 3MB+, you’re actively tanking your server’s concurrency.
Step 1: Check Total Autoload Size and Find the Culprits
SSH into your server and drop into the WordPress root directory. First, check the total size of everything currently autoloading across every hit.
Run this query through WP-CLI:
wp db query "SELECT 'autoloaded data in bytes' = '', SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';"The output is in bytes. If you see something like 8388608, that’s roughly 8MB of serialized junk loading into PHP memory on every hit.
To see what’s actually hogging that space, grab the top 15 largest rows where autoload = 'yes':
wp db query "SELECT option_name, LENGTH(option_value) AS option_size FROM wp_options WHERE autoload = 'yes' ORDER BY option_size DESC LIMIT 15;"On the site I audited, this was the mess waiting for me:
+---------------------------------------+-------------+
| option_name | option_size |
+---------------------------------------+-------------+
| _transient_thickbox_feed | 4194304 |
| wp_all_export_backup_data | 2854120 |
| rewrite_rules | 184512 |
| woocommerce_admin_report_data | 1520110 |
| abandoned_cart_raw_logs | 1120400 |
+---------------------------------------+-------------+An abandoned RSS feed cache, a leftover migration export string, and raw debugging logs made up 9.6MB on their own. None of them had any business loading inside wp_load_alloptions().
Step 2: Back Up wp_options Before Touching Anything
Don’t run updates or deletes on this table without a standalone backup. If you accidentally wipe an active theme mod or essential core option, the site goes down instantly.
Dump just the wp_options table into your home directory:
wp db export ~/wp_options_backup_$(date +%F).sql --tables=wp_optionsIf you’re dealing with multiple environments or recently moved servers, check our guide on how to safely update URLs after a WordPress migration to see how WP-CLI handles serialized data without corrupting it.
Always verify the dump file isn’t empty before moving on:
ls -lh ~/wp_options_backup_*.sqlStep 3: Clear Expired and Orphaned Transients
Transients are temporary cached values in wp_options. Plugins love to save them with autoload = 'yes', and when those plugins get deleted—or when their garbage collection fails—the transients sit there forever.
First, clear the transients that WordPress already knows are expired:
wp transient delete --expiredNow check how many autoloaded transients are still hanging around:
wp db query "SELECT COUNT(*) FROM wp_options WHERE autoload = 'yes' AND option_name LIKE '%transient%';"If you see hundreds or thousands here, sloppy plugin code likely bypassed the Transients API and used raw add_option() calls, or never set expiration timestamps.
You can flush all remaining transient keys safely with WP-CLI:
wp transient delete --allWordPress will rebuild the transients it actually needs on the next page view.
Step 4: Flip Autoload Flags from Yes to No
A lot of rows need to stay in the database for specific admin panels or cron routines, but they shouldn’t be loaded on frontend pages. Large error logs, old setup wizard payloads, and migration records should only load when a function explicitly calls get_option('option_name').
If an active plugin still needs an option occasionally, don’t delete it. Just toggle its autoload flag to no.
For a single option:
wp db query "UPDATE wp_options SET autoload = 'no' WHERE option_name = 'woocommerce_admin_report_data';"For an entire prefix left behind by a heavy builder or plugin that only needs backend access:
wp db query "UPDATE wp_options SET autoload = 'no' WHERE option_name LIKE 'elementor_backups_%' AND autoload = 'yes';"That single query instantly pulls those rows out of wp_load_alloptions() without breaking things when Elementor asks for that data directly in the dashboard.
Step 5: Delete Leftover Options from Uninstalled Plugins
Most plugins don’t clean up after themselves when you delete them from the admin panel. Over a few years, a site collects debris from old contact forms, SEO tools, abandoned sliders, and popup builders.
Search for prefixes belonging to plugins you removed months ago. For instance, old backup tools like Updraft or Duplicator:
wp db query "SELECT option_name, LENGTH(option_value) FROM wp_options WHERE option_name LIKE 'duplicator_%';"Once you’ve confirmed the plugin is gone and the site doesn’t rely on it, delete the row using WP-CLI:
wp option delete duplicator_settings duplicator_package_activeIf you’re staring at dozens of rows sharing that prefix, wipe them with a direct query:
wp db query "DELETE FROM wp_options WHERE option_name LIKE 'duplicator_%';"Check the official WP-CLI command documentation if you need other flags available under the wp option command.
Step 6: Optimize and Re-index the Table
Dropping thousands of rows or removing megabytes of data leaves behind fragmented table space (overhead). For InnoDB tables, running an optimize operation reclaims that space and rebuilds the indexes.
Run the optimization command via WP-CLI:
wp db optimizeOr target the options table specifically:
wp db query "OPTIMIZE TABLE wp_options;"Now, rerun the size query from Step 1 to verify your work:
wp db query "SELECT 'autoloaded data in bytes' = '', SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';"On that audit site, autoload size fell from 14.8MB down to 512KB. Time to first byte dropped from 1,420ms to 240ms on uncached visits.
Step 7: Automate Database Health and Stop Future Bloat
Cleaning this manually fixes the immediate bottleneck, but plugins will bloat the table right back up within six months if you don’t keep tabs on it.
First, keep autoload reads out of MySQL entirely by setting up an external cache. If you’re on a VPS, follow our guide to configure Redis object cache for WordPress so that alloptions lives in RAM instead of hammering the disk.
Second, stop relying on site visits to trigger maintenance routines. Core transients and cleanup jobs fail to fire if your traffic drops or if you cache aggressively at the edge. You should disable WP-Cron and run a system cron job from the server’s crontab:
*/15 * * * * /usr/local/bin/wp cron event run --due-now --path=/var/www/html >/dev/null 2>&1This ensures core background jobs—including transient garbage collection—run on a strict schedule, regardless of visitor traffic.
Frequently Asked Questions
What is a safe total size for autoloaded data in WordPress?
Keep it under 800KB. A lean WordPress build with a lightweight theme usually lands somewhere between 300KB and 500KB. Once it creeps past 1.5MB, you’ll see noticeable spikes in PHP memory and slower query execution.
What happens if I change autoload to ‘no’ for a core option?
WordPress can still access it whenever code calls get_option('option_name'). The difference is MySQL fetches it in an isolated single-row query instead of bundling it into the initial bulk load. Only options needed across every single page request (like siteurl, home, and active_plugins) need to stay set to yes.
Did WordPress 6.6 change how autoload works?
Yes. WordPress 6.6 introduced logic that flags large options as autoload = 'no' automatically if they exceed specific byte limits during add_option(). But it doesn’t touch existing data retroactively—it won’t clean up the bloated rows already sitting in your database.
Will Redis Object Cache fix a bloated wp_options table?
Redis speeds up reads because it serves alloptions straight from RAM. But it doesn’t fix payload size. If the autoload array is 12MB, PHP still has to receive and unserialize that 12MB blob on every single worker thread.
Next Steps
Once your options table is dialed in, make sure your static files aren’t dragging down response times. Take a look at our guide on how to configure WordPress browser cache headers and CDN edge caching on Nginx to keep static asset traffic off your PHP workers altogether.

