Bali, Indonesia
Insights

Auditing Autoloaded Options to Stop Recurring WordPress Out-of-Memory Errors

October 16, 2026
0:00
When a WordPress site hits Fatal error: Allowed memory size of ... bytes exhausted, the instinctive response is almost always to raise memory_limit in php.ini or .htaccess. If the limit was set to 128 MB, it gets bumped to 256 MB, or from 256 MB to 512 MB. The site comes back online, everything looks resolved, and then a few weeks or months later the exact same fatal error returns during a routine traffic bump or cron run. Raising the ceiling does not fix a leak; it merely gives the problem more room to expand before it crashes the PHP worker again. In hosting support, this pattern is extraordinarily common. When we look at server process logs for sites suffering from recurring memory exhaustion, the consumption rarely stems from complex template loops or heavy image transformations. More often than not, the baseline memory required just to bootstrap the WordPress core—before a single theme template or page builder widget even begins rendering—has quietly ballooned. The primary culprit behind this silent baseline bloat is the autoload mechanism in the wp_options table.
To understand why this happens, imagine preparing for a short day hike. A sensible hiker packs only the universal essentials into their backpack: water, a small first-aid kit, and an identification card. These are items you know you will need regardless of which trail you pick. However, imagine if every time you bought a piece of specialized gear—a snow shovel, a climbing harness, deep-sea diving weights—you threw it directly into that same backpack and never took it out, even when walking down a flat suburban sidewalk. This is precisely how the wp_options table treats records marked with autoload = 'yes' (or simply on in newer WordPress database schemas). The wp_options table is where WordPress and installed plugins store settings, configuration values, license keys, and transient cache data. Because many configuration values are needed on virtually every page request (such as the site URL, active theme name, active plugin list, and core routing rules), running an individual SQL query for each single setting would generate dozens of unnecessary database roundtrips. To optimize this, the WordPress database bootstrap runs a single query early in the lifecycle: SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'. It fetches every matching record at once and stores the entire collection into a global memory array called alloptions. When a plugin calls get_option('my_setting'), WordPress does not query MySQL again. It simply checks whether 'my_setting' is already present in the in-memory alloptions array. If it is, the value returns instantaneously. This design is brilliant for a clean site with lean options, but it turns into a liability the moment data hygiene breaks down.
Many website owners assume that clicking "Deactivate" and then "Delete" on a plugin in the WordPress admin panel thoroughly purges the software from their server. In reality, the WordPress core architecture leaves data deletion entirely up to third-party developers. A developer can include an uninstall.php file or an uninstall hook that drops custom tables and cleans up rows created in wp_options. However, an enormous number of plugins intentionally skip this cleanup step. Plugin authors often leave configuration data behind so that if a site owner deactivates the plugin temporarily or reinstalls it later, their settings, API tokens, and configurations remain intact. Problems arise when plugins store massive amounts of information directly inside single option values. Common offenders include:
  1. Form plugins that accidentally stash raw submission logs or incomplete JSON payloads inside autoloaded configuration options.
  2. Page builders and style customizers that store minified CSS or complex serialized styling trees in an option row rather than static filesystem assets.
  3. Broken analytics or redirect trackers that append every visitor referral or 404 hit directly to a serialized array marked as autoloaded.
  4. Old security or translation tools that cache heavy data structures in transient rows where the autoload flag was mistakenly set to active.
When these plugins are removed from the filesystem, PHP never executes their code again, but the database rows they created remain untouched. Because the autoload column is still set to load on every request, the WordPress database abstraction layer continues fetching those massive text fields into server memory on every single page hit, API call, and background cron run. If you have ever wondered why navigating the backend dashboard feels sluggish even when front-facing caching plugins are masking frontend latency, this bootstrap weight is frequently part of the reason. We explored that administrative overhead in detail when looking at why WordPress admin is slow even when the frontend loads fast.
A healthy WordPress site generally maintains an autoloaded dataset under 800 kilobytes, and rarely exceeds 1 megabyte. When an autoload cache reaches 3, 5, or even 20 megabytes in MySQL, the impact on PHP memory is magnified significantly. In MySQL, a large text blob is just stored as raw bytes on disk or in the InnoDB buffer pool. But when PHP pulls that data over the database socket via mysqli or PDO, it does not just sit there as raw bytes. PHP has to allocate memory to receive the string, and because WordPress frequently stores settings as serialized PHP objects or arrays, WordPress runs maybe_unserialize() across those values. Unserializing a complex, deeply nested text string into active PHP array structures and internal hash tables often inflates the memory footprint by several multiples compared to the raw database row size. If your base autoload size is 15 megabytes, your PHP worker may consume 40 to 60 megabytes of memory before the core has even loaded your theme's functions.php. If a visitor hits a page that genuinely requires 200 megabytes of memory (such as generating a complex WooCommerce report or processing a high-resolution image upload), the worker crashes into the memory_limit ceiling because it began the journey already carrying dozens of megabytes of unnecessary baggage.
You do not need expensive monitoring agents to identify bloated autoload options. A simple, read-only diagnostic query executed through the MySQL CLI, phpMyAdmin, or your hosting control panel's database interface will reveal both the total size and the specific culprits.
SQL
-- 1. Check total size of all autoloaded data (displayed in megabytes)
SELECT ROUND(SUM(LENGTH(option_value)) / (1024 * 1024), 2) AS autoload_size_mb
FROM wp_options
WHERE autoload IN ('yes', 'on');

-- 2. Identify the top 10 largest individual autoloaded options
SELECT option_name,
       ROUND(LENGTH(option_value) / 1024, 2) AS size_kb,
       autoload
FROM wp_options
WHERE autoload IN ('yes', 'on')
ORDER BY LENGTH(option_value) DESC
LIMIT 10;
Note: Replace wp_ with your site's actual database table prefix if you use a custom prefix. When reviewing the output of the second query, look closely at the names in the option_name column. You will often spot option prefixes tied to plugins you uninstalled months or years ago. You may also find transients—temporary cached data items prefixed with _transient_ or _site_transient_—that should never have had their autoload flag set to true in the first place.
Pruning the wp_options table requires surgical care. A common mistake is using blanket DELETE queries on any row larger than a certain threshold. Doing this indiscriminately will break active functionality. First, always generate a complete database backup before modifying or deleting any options. Restoring a single table from an SQL dump takes seconds if something goes wrong, but hunting down missing license tokens or serialized widget settings after the fact is exceptionally painful. Second, understand the difference between deleting an option and disabling its autoload status. If an active plugin genuinely needs a large configuration array, but only needs it when an administrator visits that specific plugin's settings page in the dashboard, you do not want to delete the data. Instead, you can update the record's autoload column from 'yes' to 'no'. When set to 'no', WordPress will not fetch it into the global alloptions array during bootstrap, but the plugin can still fetch it on demand via get_option() when explicitly requested. Third, beware of serialized data corruption. If you attempt to clean or edit serialized PHP strings manually inside phpMyAdmin or a text editor, modifying the string length without updating the serialization byte counters (the s:number: indicators) will break PHP's ability to parse the array, causing the associated plugin to reset its settings or throw PHP warnings. Finally, inspect your persistent object cache behavior if you use Redis or Memcached. When WordPress runs with an active object cache drop-in (object-cache.php), the alloptions array is stored inside the cache backend. If you alter options directly via SQL in MySQL, the cached alloptions payload in Redis may continue serving the old, bloated data until you run wp cache flush or restart the caching service. Keeping your WordPress site's baseline memory low ensures that when server traffic spikes or administrative tasks run, your PHP workers have the headroom they need to finish their jobs without hitting arbitrary resource walls.