D
P
0
← All articles Baca dalam Bahasa Indonesia

Caching & CDN: Deployed, but Nothing Changed

wp-admin Acting Haunted (Expired Nonce, AJAX Not JSON, Sidebar Vanishing)? That's CDN/Edge Cache, Not a Theme Bug

· · 4 min read
wp-admin Acting Haunted (Expired Nonce, AJAX Not JSON, Sidebar Vanishing)? That's CDN/Edge Cache, Not a Theme Bug

A client reported that wp-admin felt haunted. The dashboard's theme and plugin lists showed stale data that no longer matched reality. Saving anything threw the classic WordPress nonce error:

The link you followed has expired.

Then a custom panel that called admin-ajax started failing outright. In the browser console, the JS was screaming because a response that should have been JSON arrived as HTML instead:

// Admin-side JS that expected JSON
fetch(ajaxurl, { method: 'POST', body: data })
  .then(function (res) { return res.json(); })
  .catch(function (err) {
    // Uncaught (in promise) SyntaxError:
    // Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    console.error(err);
  });

And the creepiest part: the admin sidebar sometimes rendered, sometimes vanished. On the same page. Just from reloading. I spent a good while chasing a theme bug and a plugin bug. The application code was fine the whole time.

Why this happens

The cause was not in the theme, not in a plugin, and not in my PHP. It lived in a layer I never thought to suspect at first: the CDN / edge cache. Specifically, overly aggressive Cloudflare page rules were caching /wp-admin/ and /wp-admin/admin-ajax.php responses at the edge.

Once you see the mechanism, every "phantom" symptom snaps into focus:

The signature: the weirdness changes on every reload and differs per URL. A real code bug is deterministic: the same input produces the same failure. This was not. And one experiment closed the case: hard refresh or bypass the cache, and it all goes away. If skipping the cache cures the problem, the problem is in the cache, not in your application.

There is a quieter danger here too. Caching a logged-in response and serving it to a different request is not just a display glitch. It can leak one user's content to another. For an admin area, that is a security issue, not merely an annoyance.

The fix

The rule is simple: /wp-admin/ and /wp-admin/admin-ajax.php must never be cached at the edge. Neither should requests from a logged-in user. WordPress already marks a logged-in session with a cookie, so the presence of that cookie is exactly the signal I want to use to bypass the cache entirely.

On Cloudflare, the fix means creating rules that bypass cache for the admin paths and ensuring the WordPress login cookie breaks the cache. Conceptually the rules look like this:

# Admin paths: never cache at the edge
URL match: *old-site.com/wp-admin/*
  -> Cache Level: Bypass
 
URL match: *old-site.com/wp-admin/admin-ajax.php*
  -> Cache Level: Bypass
 
# Also bypass any request carrying the WordPress login cookie
Cookie contains: wordpress_logged_in_
  -> Cache Level: Bypass

If you prefer to do this at the web server level, the principle is identical: never let anything under /wp-admin/ land in a shared cache, and never cache a request carrying the wordpress_logged_in_ cookie. The vendor and the syntax do not matter. What matters is that admin paths and logged-in requests always punch straight through to the origin.

Once those rules were live, I did a single hard refresh to purge the stale versions from the edge. The nonces became valid again, admin-ajax returned proper JSON, the plugin list was accurate, and the sidebar stopped flickering out of existence.

The takeaway

Sometimes wp-admin behaves inconsistently: different per page, changing between reloads, healed by a hard refresh. When that happens, do not start tearing apart the theme. That pattern is the signature of an edge-cache artifact, not an application bug. Application code fails deterministically; caches fail at random.

So my first question now when admin acts strange is no longer "which plugin is misbehaving?" but "what is the hosting and CDN stack, and is anything caching /wp-admin/?" Diagnose the infrastructure before the theme. More often than you would think, the answer sits one layer above the code you are staring at.