Serving full HTML pages from CDN edge nodes drops WordPress Time to First Byte (TTFB) from 600ms down to under 30ms worldwide. But caching HTML without strict bypass rules will break your site fast: logged-in admins see guest pages, visitors see someone else’s WooCommerce cart, and nonces fail across your forms.
To cache WordPress safely at the CDN level, your edge nodes have to inspect incoming request headers, cookies, and query strings before deciding whether to serve a cached copy or hit the origin. Let’s walk through the edge rules, configure origin Nginx response headers, and verify cache bypass behavior using real cURL requests.

The Anatomy of WordPress Edge Caching (And Why It Breaks)
CDNs handle static assets like CSS, JS, and images without fuss because those files are identical for everyone. HTML is different. WordPress tailors HTML depending on who asks for it. If an admin logs in and hits the homepage, WordPress injects the admin toolbar into the markup.
If your CDN caches that response and serves it to a guest, that guest sees the admin bar. Even worse, WooCommerce session cookies like woocommerce_items_in_cart will leak cart data if stored in a shared edge cache.
To keep your cache clean while still serving ~95% of anonymous traffic from the edge, you need three layers of bypass logic:
- Dynamic URIs: Endpoints like
/wp-admin/,/wp-login.php,/cart/, and/checkout/must never be stored. - WordPress Core Cookies: Any request with authentication or session cookies has to bypass the edge cache instantly.
- Origin Cache-Control Headers: The origin server must explicitly tell edge proxies when content is private or short-lived.
Pairing these edge rules with your existing immutable cache-control and cache busting in Nginx setup ensures you only cache what you intend to.
Step 1: Identify WordPress Cookies That Must Bypass Cache
WordPress and WooCommerce rely on specific cookie naming patterns laid out in the official WordPress cookie documentation. Your edge engine needs to check the incoming Cookie header against these patterns.
Here are the core cookie regex patterns your CDN edge must recognize for an immediate cache bypass:
wordpress_logged_in_.*: Set when an admin or user logs into WordPress. Skips cache so they get real-time dashboards and edit buttons.comment_author_.*: Set after someone leaves a comment so their info prefills. Caching this leaks commenter names to strangers.wp-postpass_.*: Set when a visitor unlocks a password-protected post.woocommerce_items_in_cart: Triggered when a shopper adds an item to their cart.woocommerce_cart_hash: Tracks cart state for WooCommerce.wp_woocommerce_session_.*: Stores session tokens for guest checkout.
If any of these cookies show up in a request, the CDN must route directly to your origin server without touching edge cache storage.
Step 2: Configure Origin Nginx Headers for Edge Signaling
Your origin server needs to send clear signals downstream. While you can set edge rules in your CDN dashboard, configuring Nginx to send proper Cache-Control directives prevents accidental caching if an edge rule misfires.
Open your Nginx virtual host config (usually in /etc/nginx/sites-available/yourdomain.com):
sudo nano /etc/nginx/sites-available/yourdomain.comAdd a map block at the http level to detect dynamic cookies, then pass downstream proxy headers inside your PHP location block:
# Detect WordPress auth or cart cookies
map $http_cookie $skip_cache { default 0; ~*wordpress_logged_in_ 1; ~*comment_author_ 1; ~*wp-postpass_ 1; ~*woocommerce_items_in_cart 1; ~*woocommerce_cart_hash 1; ~*wp_woocommerce_session_ 1;
}
server { server_name example.com; root /var/www/html; index index.php index.html; # Bypass paths directly if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") { set $skip_cache 1; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # Send explicit caching headers to edge CDN proxies if ($skip_cache = 1) { add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; add_header X-Accel-Expires "0" always; } if ($skip_cache = 0) { add_header Cache-Control "public, max-age=300, stale-while-revalidate=60" always; add_header X-Accel-Expires "86400" always; } }
}This setup sends Cache-Control: no-store whenever an authenticated user or active shopper hits a PHP script. It lets public anonymous pages advertise a 5-minute TTL with background revalidation. If your origin uses IP filtering, check our guide on how to restrict Nginx access to CDN origin shield IPs so traffic can’t bypass this proxy pipeline directly.
Step 3: Define CDN Edge Rules for Cookie and URI Bypass
Now configure the logic on your CDN edge. Order matters here: put bypass rules at the very top of your ruleset so they evaluate before your catch-all caching rules.
Here is the rule structure you want inside your CDN rule engine:
Rule 1: Hard Dynamic Path Bypass
Matching condition: URI Path matches any of:
/wp-admin/*/wp-login.php/wp-signup.php/cart/*/checkout/*/my-account/*/wc-api/*/addons/*
Action: Set Cache Level to Bypass / Pass-Through (Edge TTL = 0, Browser TTL = 0).
Rule 2: Cookie-Based Bypass
Matching condition: Cookie header matches regex:
(wordpress_logged_in_|comment_author_|wp-postpass_|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session_)Action: Set Cache Level to Bypass and forward all request headers directly to origin.
Rule 3: Query Parameter Exclusions
WordPress uses specific query strings for live previews, nonces, and admin tasks that should never hit cache:
(preview=true|wp_customize=|customize_changeset_uuid|wc-ajax=|add-to-cart=)Action: Set Cache Level to Bypass.
Rule 4: Edge Cache Everything Else
Matching condition: URI Path matches * (all remaining requests).
Action: Cache HTML with an Edge TTL of 4 hours, honor origin headers, and enable stale-while-revalidate to cushion origin spikes.
Configuring origin protection alongside these edge rules will help prevent CDN cache stampedes with origin shield when high-traffic pages expire.
Step 4: Handling Dynamic Query Strings and the REST API
WordPress REST API endpoints (/wp-json/*) serve both public data and authenticated requests. To handle them properly at the edge, check the Authorization header alongside your cookies.
If you’re building headless sites or plugins that use the REST API heavily, drop this conditional logic into your CDN or edge worker:
addEventListener('fetch', event => { event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) { const url = new URL(request.url); const cookieHeader = request.headers.get('Cookie') || ''; const authHeader = request.headers.get('Authorization') || ''; // Regex for bypass cookies const bypassCookiePattern = /(wordpress_logged_in_|comment_author_|wp-postpass_|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session_)/; // Check if request requires dynamic origin processing const isBypassCookie = bypassCookiePattern.test(cookieHeader); const isAuthRequest = authHeader.length > 0; const isProtectedPath = /^/(wp-admin|wp-login.php|cart|checkout|my-account)/.test(url.pathname); const isDynamicQuery = url.searchParams.has('preview') || url.searchParams.has('wc-ajax'); if (isBypassCookie || isAuthRequest || isProtectedPath || isDynamicQuery) { // Force direct pass-through to origin const newHeaders = new Headers(request.headers); newHeaders.set('X-Edge-Cache-Status', 'BYPASS'); return fetch(request, { headers: newHeaders, cache: 'no-store' }); } // Fallback to standard edge cache pipeline return fetch(request);
}This runs before the cache lookup stage. If someone sends a JWT or Bearer token via an Authorization header to /wp-json/wp/v2/posts, it skips the cached public payload and returns their authenticated view.
Step 5: Verify Edge Cache Bypass Behavior with cURL
Don’t just use your browser dev tools for this. Service workers and browser caches can make it hard to tell what the CDN is actually doing. Use curl -I in your terminal to inspect raw headers.
Run these three test cases against your domain:
Test Case 1: Anonymous Visitor Request (Must HIT Cache)
curl -I https://example.com/sample-post/Check the cache status headers in the response:
HTTP/2 200
content-type: text/html; charset=UTF-8
x-cache: HIT
age: 142
cache-control: public, max-age=300, stale-while-revalidate=60The x-cache: HIT (or cf-cache-status: HIT) and a non-zero age confirm the CDN served this directly from edge memory.
Test Case 2: Logged-in Cookie Request (Must BYPASS Cache)
curl -I -H "Cookie: wordpress_logged_in_testcookie=1" https://example.com/sample-post/Expected output:
HTTP/2 200
content-type: text/html; charset=UTF-8
x-cache: BYPASS
age: 0
cache-control: no-store, no-cache, must-revalidate, max-age=0The x-cache: BYPASS header and age: 0 show that your edge rule matched the cookie and sent the request straight to Nginx.
Test Case 3: Admin Path Request (Must BYPASS Cache)
curl -I https://example.com/wp-login.phpExpected output:
HTTP/2 200
content-type: text/html; charset=UTF-8
x-cache: BYPASS
cache-control: no-store, no-cache, must-revalidate, max-age=0If you get a HIT on Test Case 2 or 3, double-check your rule order or look for typos in your cookie regex. If you hit redirect loops during these checks, see our walkthrough on how to fix SSL ERR_TOO_MANY_REDIRECTS on Nginx behind a CDN.
Gotcha: The Set-Cookie Cache Pollution Bug
The first time I deployed HTML edge caching on a high-traffic WordPress site, a nasty bug showed up within hours: anonymous visitors were getting assigned session cookies from earlier users.
The origin was returning a Set-Cookie header on a response the CDN marked as cacheable. When the CDN cached that HTML, it cached the Set-Cookie header along with it. Every guest who hit the cached page had someone else’s session written straight into their browser.
Fix this by stripping Set-Cookie headers from public, cacheable responses in Nginx or at the edge.
In Nginx, add this directive inside your caching block so session headers never attach to cacheable static or HTML payloads:
location ~* .(html|htm)$ { fastcgi_hide_header Set-Cookie; fastcgi_ignore_headers Set-Cookie;
}In your CDN dashboard, enable Strip Set-Cookie on Cached Assets or Ignore Set-Cookie on Cache HIT. If a script issues a cookie, the edge must drop that request to a BYPASS rather than caching the cookie header for everyone else.
WordPress Edge Cache Configuration FAQ
Will caching HTML at the edge break my contact forms and nonces?
WordPress nonces in cached HTML last 12 to 24 hours by default, per the WordPress developer documentation. If your edge TTL is 2 hours or less, anonymous form nonces won’t have issues. For aggressive caching (like 24-hour TTLs), use REST API form endpoints or refresh nonces via AJAX on page load.
Can I use WooCommerce and edge caching together?
Yes. The rules above bypass the cache the moment an item hits the cart, because WooCommerce writes the woocommerce_items_in_cart cookie. Anonymous product pages and archive browsing stay fast on the edge, while carts, checkouts, and active sessions hit PHP-FPM directly.
Why does my CDN return MISS on every curl request?
If repeated requests return MISS instead of HIT, check if your origin sends Cache-Control: private or Pragma: no-cache. Several WordPress security plugins attach private headers across the board. You’ll need to override those headers in Nginx before the CDN will store anything.
Should I cache the WordPress REST API at the edge?
You can cache public read endpoints like GET /wp-json/wp/v2/posts for short windows (60 to 300 seconds) as long as you account for query strings. Just make sure any request with an Authorization header or auth cookie bypasses the cache entirely.
Next Steps: Fine-Tune Your Caching Pipeline
Once your bypass rules are running, keep an eye on origin CPU usage over the next 24 hours. You should see a 70% to 90% drop in PHP-FPM workers since anonymous traffic won’t need to spin up PHP or query MySQL.
To take this further, check our guide on configuring immutable cache-control and cache busting in Nginx to cut out static asset revalidation completely.

