D
P
0

WordPress & PHP

Checking a WordPress Site With `curl` Returns Empty Placeholders? WP Rocket Strips Content for Unknown UAs

June 25, 2026·4 min read
Checking a WordPress Site With `curl` Returns Empty Placeholders? WP Rocket Strips Content for Unknown UAs

After deploying a copy update to a WordPress booking platform I built, I did what I always do: verify the result from the terminal before I trust a screenshot. Opening a browser is slow, curl is instant. So I ran:

curl https://old-site.com/listings/ | head -n 40

What came back made me squint. Instead of the markup I expected, I got an empty skeleton: wrapper divs with no content, a few odd data- attributes, and placeholders where the hero text, the listing grid, and the descriptions were supposed to be. To the terminal, the page looked completely broken. The content I had just deployed seemed to have vanished.

My first instinct was a bad one: I assumed the deploy had failed, or that some PHP fatal was stripping the output. I checked error_log — clean. I opened the same page in a real browser, and everything rendered perfectly. Hero text present, every listing present, descriptions present. So the page was fine for humans but lying in the terminal. That is a telltale combination, and the moment I remembered this site runs WP Rocket, the puzzle came apart.

Why this happens

WP Rocket has a few optimizations that defer work until the page is actually used in a browser: lazy-render and delay JavaScript. The intent is good for performance. Instead of shipping the whole DOM and executing every script immediately, WP Rocket serves a leaner payload and lets the browser hydrate the rest once there is interaction or once elements enter the viewport. The key detail here: the decision about what to send depends partly on the user-agent.

A plain curl sends a user-agent like curl/8.4.0. To WP Rocket, that is an unknown UA, not a browser. So what I received was a reduced version of the page: the content meant to hydrate later was deferred, and all that remained was the placeholder skeleton. Not a failed deploy, not a PHP fatal. I had simply probed an optimized site with a tool that does not disguise itself as a real visitor, then drawn conclusions from a payload that was deliberately incomplete.

Once that clicked, everything made sense. The browser gets the full version because it is a browser. curl gets the bones because it is not. Nothing was actually broken. The only broken thing was my assumption that curl sees what a visitor sees.

The fix

The fix is not to turn off WP Rocket. The fix is to probe the site in a way that sees what visitors and Google actually receive. There are a few routes.

The fastest: keep using curl, but send a recognized user-agent. I most often use a Googlebot UA, because what I usually want to know is what the crawler sees:

curl -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' https://old-site.com/listings/

With that UA, the returned payload contains the full markup immediately: hero text, every listing, the descriptions. Exactly what the browser shows. Now I can trust my grep again.

When I need certainty about what a real user sees, I use a real browser, usually via headless or Playwright. That runs the JavaScript, triggers hydration, and gives me the true final DOM rather than just the raw server response.

There is also a handy diagnostic trick to separate a WP Rocket problem from a genuine content problem. Append ?nowprocket to the URL to bypass WP Rocket entirely for that request:

curl 'https://old-site.com/listings/?nowprocket'

If the page renders fully with ?nowprocket but comes back empty without it, the culprit is definitively WP Rocket, not your PHP or template.

The bonus gotcha: the HTML cache does not get purged

While tracing this, I hit one more trap worth recording. I assumed bumping the ?ver= on a changed asset was enough to force the new content to show. That is wrong. Bumping an asset's ?ver= query string does not purge WP Rocket's HTML page cache. The ?ver= only cache-busts that CSS or JS file itself; the server-rendered HTML is still served from the old cache.

So whenever I deploy a change that touches server-rendered copy, I purge the WP Rocket cache manually afterward. Otherwise visitors — and me, mid-verification — keep seeing the old HTML even though the PHP is already producing the new output.

The takeaway

Plain curl is a misleading probe on an optimized WordPress site. It is not lying about the bytes the server sent, but those bytes are intentionally different for an unknown user-agent. If you want to see what visitors and Google actually get, match the user-agent to a real one, ideally Googlebot, or just drive a real browser. And remember that caching in WordPress is layered: asset cache-busting and the HTML page cache are two separate things, so purge the HTML explicitly after any deploy that changes server-rendered copy. Since then, I have stopped panicking at an empty terminal and started asking first: who am I pretending to be?