A colleague flagged a 500 critical error on a WordPress site and traced it quickly to PHP memory. The fix seemed obvious: bump the limit from 128M to 512M and move on. That worked, but it left me uneasy. A 4x increase to stop a crash is a blunt instrument, and I wanted to understand what was actually consuming that memory before we closed the ticket.
What I found was a page builder caching large serialized CSS blobs in the object cache — and PHP unserializing those blobs on every relevant request. The memory increase bought stability, but it didn’t address the underlying pressure.
What the page builder was actually doing #
_builder_data). On save — or on first view — it compiles that JSON into CSS and writes a minified file under wp-content/uploads/builder/css/. So far, so reasonable.
The problem is what happens with object caching enabled. The builder also caches its compiled output — CSS blobs, merged widget styles, metadata pointers — as serialized strings in the object cache, and some of those strings get large. When PHP pulls one of those values from cache and unserializes it, memory spikes. Under 128M, that spike was occasionally fatal. Under 512M, it mostly isn’t — but the spike still happens.
The specific setting that made this worse was CSS Print Method: Embedding. In that mode, the builder injects its compiled CSS inline into the HTML response rather than linking to the external file it already wrote to disk, which means the CSS blob travels through PHP’s memory on every page load, not just at compile time. I didn’t know this setting existed until I started looking at what the builder actually writes to the object cache, and the connection between a UI toggle and a memory spike is not something you’d guess from the setting’s label. I only found it by looking at what the builder writes to post meta and how the object cache interacts with it — not from any log or error message that pointed there directly.
What I changed #
1. Flushed the page builder’s CSS and JS cache via the builder’s own cache-clearing tool. This forced a clean recompile and wrote fresh files to disk, clearing out any stale or oversized blobs sitting in the object cache.
2. Changed CSS Print Method from Embedding to External File in the builder’s performance settings. With this set, the builder links to the CSS file on disk rather than injecting compiled styles inline. The file still exists and is still served — PHP just doesn’t have to hold the blob in memory on every request.
These two changes should reduce the size of what gets serialized and cached, and more importantly, reduce how often PHP has to unserialize large values during normal page rendering. I didn’t capture a before/after memory profile on this one, which I regret — a rough reading of the cached blob size before flushing would have made the diagnosis a lot more concrete than it ended up being.
What this actually taught me #
500 is a production outage and a higher limit stopped it. But stopping there would have left us with a system stable mostly by coincidence — stable because 512M happened to be enough headroom for the current content volume, until it wasn’t. Spending an hour on the why before closing the ticket is what turned this from a band-aid into an actual fix.
The serialized blob pattern is easy to miss if you’re only watching memory totals and not cache contents. Large values can look perfectly fine in the database, then become expensive the moment PHP has to reconstruct them in memory at request time. If you’re running a visual page builder with object caching enabled, check whether compiled CSS is being stored as a serialized value and how large those values are getting. The External File print method is almost always the right default: it keeps CSS on disk where it belongs and out of PHP’s memory on every request. Embedding may have made more sense when avoiding an extra HTTP request mattered more than it does now; on a setup with object caching active, External File is almost always the better choice.