{"id":1343,"date":"2025-11-04T21:03:04","date_gmt":"2025-11-04T18:03:04","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/the-cdn-caching-playbook-for-wordpress-and-woocommerce-cache%e2%80%91control-and-edge-rules-that-just-work\/"},"modified":"2025-11-04T21:03:04","modified_gmt":"2025-11-04T18:03:04","slug":"the-cdn-caching-playbook-for-wordpress-and-woocommerce-cache%e2%80%91control-and-edge-rules-that-just-work","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/the-cdn-caching-playbook-for-wordpress-and-woocommerce-cache%e2%80%91control-and-edge-rules-that-just-work\/","title":{"rendered":"The CDN Caching Playbook for WordPress and WooCommerce: Cache\u2011Control and Edge Rules That Just Work"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><p>So there I was, staring at a WooCommerce site at 6:07 AM with coffee in one hand and a spinning loader in the other. The store had a flash sale. Traffic was pouring in. The homepage felt snappy thanks to the CDN, but the cart and checkout were acting like they were stuck in molasses. Ever had that moment when everything is \u201coptimized\u201d on paper, and yet real customers still struggle? That morning reminded me of a simple truth: CDN caching is powerful, but the wrong cache rules can quietly break WooCommerce in ways that don\u2019t show up until you\u2019re under pressure.<\/p>\n<p>In this guide, I want to walk you through the practical, real\u2011world way I set up CDN caching for WordPress and WooCommerce. We\u2019ll talk about what to cache and what to leave alone, the Cache\u2011Control headers I actually use, and the edge rules that keep carts and checkouts safe. I\u2019ll share the little gotchas I\u2019ve learned from client sites, plus a few simple, copy\u2011and\u2011paste ideas you can test today. Think of this as a friendly playbook: not theory, but the settings that consistently work.<\/p>\n<div id=\"toc_container\" class=\"toc_transparent no_bullets\"><p class=\"toc_title\">\u0130&ccedil;indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#Why_CDNs_Love_Static_Files_but_Hesitate_on_HTML\"><span class=\"toc_number toc_depth_1\">1<\/span> Why CDNs Love Static Files but Hesitate on HTML<\/a><\/li><li><a href=\"#CacheControl_in_Plain_English_And_What_I_Set_on_Real_Sites\"><span class=\"toc_number toc_depth_1\">2<\/span> Cache\u2011Control in Plain English (And What I Set on Real Sites)<\/a><ul><li><a href=\"#Decoding_the_usual_suspects\"><span class=\"toc_number toc_depth_2\">2.1<\/span> Decoding the usual suspects<\/a><\/li><li><a href=\"#What_I_actually_put_on_assets\"><span class=\"toc_number toc_depth_2\">2.2<\/span> What I actually put on assets<\/a><\/li><li><a href=\"#What_I_put_on_WordPress_HTML_nonWooCommerce\"><span class=\"toc_number toc_depth_2\">2.3<\/span> What I put on WordPress HTML (non\u2011WooCommerce)<\/a><\/li><li><a href=\"#What_I_put_on_WooCommerce_HTML\"><span class=\"toc_number toc_depth_2\">2.4<\/span> What I put on WooCommerce HTML<\/a><\/li><\/ul><\/li><li><a href=\"#Edge_Rules_That_Keep_You_Fast_Without_Breaking_WooCommerce\"><span class=\"toc_number toc_depth_1\">3<\/span> Edge Rules That Keep You Fast Without Breaking WooCommerce<\/a><ul><li><a href=\"#Paths_I_always_bypass\"><span class=\"toc_number toc_depth_2\">3.1<\/span> Paths I always bypass<\/a><\/li><li><a href=\"#WooCommerce_endpoints_that_must_stay_dynamic\"><span class=\"toc_number toc_depth_2\">3.2<\/span> WooCommerce endpoints that must stay dynamic<\/a><\/li><li><a href=\"#Query_strings_and_hit_rate\"><span class=\"toc_number toc_depth_2\">3.3<\/span> Query strings and hit rate<\/a><\/li><li><a href=\"#HTML_caching_for_anonymous_users_only\"><span class=\"toc_number toc_depth_2\">3.4<\/span> HTML caching for anonymous users only<\/a><\/li><li><a href=\"#Respecting_ETag_and_Last-Modified\"><span class=\"toc_number toc_depth_2\">3.5<\/span> Respecting ETag and Last-Modified<\/a><\/li><\/ul><\/li><li><a href=\"#A_Practical_Walkthrough_From_WordPress_Basics_to_a_WooCommerce_Store\"><span class=\"toc_number toc_depth_1\">4<\/span> A Practical Walkthrough: From WordPress Basics to a WooCommerce Store<\/a><ul><li><a href=\"#Step_1_Make_static_assets_a_home_run\"><span class=\"toc_number toc_depth_2\">4.1<\/span> Step 1: Make static assets a home run<\/a><\/li><li><a href=\"#Step_2_Cache_HTML_for_anonymous_users_nonWooCommerce\"><span class=\"toc_number toc_depth_2\">4.2<\/span> Step 2: Cache HTML for anonymous users (non\u2011WooCommerce)<\/a><\/li><li><a href=\"#Step_3_Bypass_for_loggedin_users_and_sensitive_paths\"><span class=\"toc_number toc_depth_2\">4.3<\/span> Step 3: Bypass for logged\u2011in users and sensitive paths<\/a><\/li><li><a href=\"#Step_4_WooCommercecache_the_catalog_protect_the_cart\"><span class=\"toc_number toc_depth_2\">4.4<\/span> Step 4: WooCommerce\u2014cache the catalog, protect the cart<\/a><\/li><li><a href=\"#Step_5_Normalize_marketing_parameters\"><span class=\"toc_number toc_depth_2\">4.5<\/span> Step 5: Normalize marketing parameters<\/a><\/li><li><a href=\"#Step_6_Automate_cache_purging_on_content_changes\"><span class=\"toc_number toc_depth_2\">4.6<\/span> Step 6: Automate cache purging on content changes<\/a><\/li><\/ul><\/li><li><a href=\"#RealWorld_Header_Recipes_You_Can_Borrow\"><span class=\"toc_number toc_depth_1\">5<\/span> Real\u2011World Header Recipes You Can Borrow<\/a><ul><li><a href=\"#Static_assets\"><span class=\"toc_number toc_depth_2\">5.1<\/span> Static assets<\/a><\/li><li><a href=\"#Public_WordPress_HTML_anonymous_users\"><span class=\"toc_number toc_depth_2\">5.2<\/span> Public WordPress HTML (anonymous users)<\/a><\/li><li><a href=\"#WooCommerce_cartcheckoutaccount\"><span class=\"toc_number toc_depth_2\">5.3<\/span> WooCommerce cart\/checkout\/account<\/a><\/li><li><a href=\"#API_and_admin_endpoints\"><span class=\"toc_number toc_depth_2\">5.4<\/span> API and admin endpoints<\/a><\/li><li><a href=\"#Optional_SurrogateControl_for_CDNs_that_support_it\"><span class=\"toc_number toc_depth_2\">5.5<\/span> Optional: Surrogate\u2011Control for CDNs that support it<\/a><\/li><\/ul><\/li><li><a href=\"#Cookies_Variants_and_the_Subtle_Ways_Caches_Go_Sideways\"><span class=\"toc_number toc_depth_1\">6<\/span> Cookies, Variants, and the Subtle Ways Caches Go Sideways<\/a><ul><li><a href=\"#Bypass_on_cookie_not_Vary_Cookie\"><span class=\"toc_number toc_depth_2\">6.1<\/span> Bypass on cookie, not Vary: Cookie<\/a><\/li><li><a href=\"#Device_variants\"><span class=\"toc_number toc_depth_2\">6.2<\/span> Device variants<\/a><\/li><li><a href=\"#CDN_default_behavior_versus_your_intent\"><span class=\"toc_number toc_depth_2\">6.3<\/span> CDN default behavior versus your intent<\/a><\/li><\/ul><\/li><li><a href=\"#How_I_Test_and_Troubleshoot_Without_Losing_My_Weekend\"><span class=\"toc_number toc_depth_1\">7<\/span> How I Test and Troubleshoot (Without Losing My Weekend)<\/a><ul><li><a href=\"#Check_headers_with_curl\"><span class=\"toc_number toc_depth_2\">7.1<\/span> Check headers with curl<\/a><\/li><li><a href=\"#Simulate_edge_behavior\"><span class=\"toc_number toc_depth_2\">7.2<\/span> Simulate edge behavior<\/a><\/li><li><a href=\"#Watch_the_query_strings\"><span class=\"toc_number toc_depth_2\">7.3<\/span> Watch the query strings<\/a><\/li><li><a href=\"#Fight_double_caching_carefully\"><span class=\"toc_number toc_depth_2\">7.4<\/span> Fight double caching carefully<\/a><\/li><\/ul><\/li><li><a href=\"#A_Story_About_Fragments_Midnight_and_a_Lesson_I_Still_Use\"><span class=\"toc_number toc_depth_1\">8<\/span> A Story About Fragments, Midnight, and a Lesson I Still Use<\/a><\/li><li><a href=\"#Putting_It_All_Together_A_Simple_Reliable_Blueprint\"><span class=\"toc_number toc_depth_1\">9<\/span> Putting It All Together: A Simple, Reliable Blueprint<\/a><\/li><li><a href=\"#Advanced_Touches_When_Youre_Ready\"><span class=\"toc_number toc_depth_1\">10<\/span> Advanced Touches When You\u2019re Ready<\/a><ul><li><a href=\"#Stale_TTLs_for_resilience\"><span class=\"toc_number toc_depth_2\">10.1<\/span> Stale TTLs for resilience<\/a><\/li><li><a href=\"#Tagbased_purging\"><span class=\"toc_number toc_depth_2\">10.2<\/span> Tag\u2011based purging<\/a><\/li><li><a href=\"#Smart_cache_keys\"><span class=\"toc_number toc_depth_2\">10.3<\/span> Smart cache keys<\/a><\/li><li><a href=\"#Monitor_whats_actually_happening\"><span class=\"toc_number toc_depth_2\">10.4<\/span> Monitor what\u2019s actually happening<\/a><\/li><\/ul><\/li><li><a href=\"#Common_Gotchas_I_See_And_How_I_Dodge_Them\"><span class=\"toc_number toc_depth_1\">11<\/span> Common Gotchas I See (And How I Dodge Them)<\/a><\/li><li><a href=\"#If_Youre_New_to_CDNs_A_Gentle_Ramp\"><span class=\"toc_number toc_depth_1\">12<\/span> If You\u2019re New to CDNs, A Gentle Ramp<\/a><\/li><li><a href=\"#Edge_Rules_in_Human_Language\"><span class=\"toc_number toc_depth_1\">13<\/span> Edge Rules in Human Language<\/a><\/li><li><a href=\"#What_About_Security_Headers_and_HTTPS\"><span class=\"toc_number toc_depth_1\">14<\/span> What About Security Headers and HTTPS?<\/a><\/li><li><a href=\"#WrapUp_Your_Site_Only_Fasterand_Still_Correct\"><span class=\"toc_number toc_depth_1\">15<\/span> Wrap\u2011Up: Your Site, Only Faster\u2014and Still Correct<\/a><\/li><\/ul><\/div>\n<h2 id=\"section-1\"><span id=\"Why_CDNs_Love_Static_Files_but_Hesitate_on_HTML\">Why CDNs Love Static Files but Hesitate on HTML<\/span><\/h2>\n<p>Let\u2019s start with the basics. A CDN is brilliant at moving static files\u2014images, CSS, JS\u2014closer to your visitors. No database calls, no PHP, no waiting around. With WordPress, those assets are easy wins: cache them for a long time and your site already feels faster. HTML is trickier. It changes more often, it can be personalized, and in WooCommerce it depends on who\u2019s logged in, what\u2019s in the cart, what coupons are applied, and twenty other tiny decisions.<\/p>\n<p>Here\u2019s the thing: a lot of folks either cache everything (and accidentally cache carts), or they cache nothing (and miss out on huge wins). The sweet spot is caching HTML for anonymous users and leaving dynamic experiences alone. That means letting your CDN serve cached HTML when nobody\u2019s logged in, but bypassing cache the moment a user is recognized\u2014or when you hit sensitive paths like checkout.<\/p>\n<p>Think of it like ordering coffee at a cafe. The regular latte doesn\u2019t need to be made from scratch every time; it\u2019s a standard recipe that\u2019s easy to speed up. But your friend\u2019s oat milk, extra-hot, decaf, triple-shot situation? That needs special attention. WooCommerce dynamic paths are that custom order. We just need to label them clearly so the CDN doesn\u2019t try to pre\u2011make them for everyone.<\/p>\n<h2 id=\"section-2\"><span id=\"CacheControl_in_Plain_English_And_What_I_Set_on_Real_Sites\">Cache\u2011Control in Plain English (And What I Set on Real Sites)<\/span><\/h2>\n<p>Cache\u2011Control is the instruction sheet you hand to browsers and CDNs. It tells them who can cache what, and for how long. Most performance problems I see come down to a few header mistakes. Once these are right, things click into place.<\/p>\n<h3><span id=\"Decoding_the_usual_suspects\">Decoding the usual suspects<\/span><\/h3>\n<p>Here\u2019s how I explain the big ones to clients:<\/p>\n<p><strong>public<\/strong> means a shared cache (like a CDN) can store the response. <strong>private<\/strong> means only the end user\u2019s browser should cache it. <strong>max-age<\/strong> is how long the browser can keep it. <strong>s-maxage<\/strong> is the CDN\u2019s version of that\u2014what a shared cache should use. <strong>no-cache<\/strong> doesn\u2019t mean \u201cdon\u2019t cache\u201d; it means \u201cyou must revalidate before using the cached copy.\u201d <strong>no-store<\/strong> means \u201cdo not store this at all,\u201d which is what you want on sensitive responses like checkout. <strong>must-revalidate<\/strong> enforces revalidation. <strong>stale-while-revalidate<\/strong> lets you serve a slightly old copy while you fetch a fresh one in the background. <strong>stale-if-error<\/strong> is a safety net if the origin is down.<\/p>\n<p>If you want a deeper reference, I keep the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Cache-Control\" target=\"_blank\" rel=\"noopener nofollow\">MDN guide to Cache\u2011Control directives<\/a> handy. It\u2019s clear and concise when you\u2019re double\u2011checking your mental model.<\/p>\n<h3><span id=\"What_I_actually_put_on_assets\">What I actually put on assets<\/span><\/h3>\n<p>For static assets with versioned filenames (or query versioning WordPress loves to append), I go aggressive:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: public, max-age=31536000, immutable\n<\/code><\/pre>\n<p>Immutable tells the browser \u201cdon\u2019t even ask again\u201d because that filename won\u2019t change. If your build doesn\u2019t fingerprint filenames, you can still use a long max-age, but be ready to purge when you deploy.<\/p>\n<h3><span id=\"What_I_put_on_WordPress_HTML_nonWooCommerce\">What I put on WordPress HTML (non\u2011WooCommerce)<\/span><\/h3>\n<p>For blogs, brochure sites, or content that doesn\u2019t depend on sessions, I like smart edge caching with quick revalidation:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: public, max-age=0, s-maxage=1800, stale-while-revalidate=60, stale-if-error=86400\n<\/code><\/pre>\n<p>This tells browsers to revalidate immediately (max-age=0), but lets the CDN hold a 30\u2011minute version (s-maxage=1800). During bursts of traffic, the CDN can serve a warm copy for a minute while it quietly refreshes. If your origin has a hiccup, stale-if-error gives you a day of breathing room.<\/p>\n<h3><span id=\"What_I_put_on_WooCommerce_HTML\">What I put on WooCommerce HTML<\/span><\/h3>\n<p>WooCommerce changes the rules. Anything personalized or sensitive shouldn\u2019t be cached. Anything public and stable can be. Product pages are technically public, but they sometimes show mini\u2011cart counts or personalized banners from plugins. So the safest approach is: cache public landing pages and product\/category pages for anonymous users only, and bypass as soon as you detect a cart or login.<\/p>\n<p>For cacheable product\/category pages, I\u2019ll still use the same public + s\u2011maxage policy as above for anonymous users. For cart, checkout, account, and any logged\u2011in sessions, I\u2019ll send headers like:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: no-store\n<\/code><\/pre>\n<p>No\u2011store leaves no room for ambiguity. It\u2019s a firm \u201cdon\u2019t keep this.\u201d<\/p>\n<h2 id=\"section-3\"><span id=\"Edge_Rules_That_Keep_You_Fast_Without_Breaking_WooCommerce\">Edge Rules That Keep You Fast Without Breaking WooCommerce<\/span><\/h2>\n<p>Edge rules are where the magic happens. Your Cache\u2011Control headers tell caches how to behave, but edge rules decide when to cache and when to step aside. This is where we describe \u201clatte\u201d versus \u201ctriple\u2011shot decaf oat flat white.\u201d<\/p>\n<h3><span id=\"Paths_I_always_bypass\">Paths I always bypass<\/span><\/h3>\n<p>There are a few URLs I never let a CDN cache for WordPress:<\/p>\n<p>1) \/wp-admin\/ and \/wp-login.php \u2014 always bypass and don\u2019t even try to be clever. 2) \/wp-cron.php \u2014 definitely bypass; it\u2019s not a page. 3) \/wp-admin\/admin-ajax.php \u2014 bypass entirely; it\u2019s the workhorse for many plugins. 4) Preview and customizer URLs \u2014 anything with preview=true or customize_changeset_uuid should bypass.<\/p>\n<h3><span id=\"WooCommerce_endpoints_that_must_stay_dynamic\">WooCommerce endpoints that must stay dynamic<\/span><\/h3>\n<p>Now the WooCommerce essentials: 1) \/cart\/, \/checkout\/, \/my-account\/ and anything under those. 2) wc-ajax endpoints, like ?wc-ajax=whatever \u2014 bypass every time. 3) add-to-cart actions \u2014 often a GET with ?add-to-cart=123 or a POST; treat either as bypass. 4) Account orders, order pay, and order received pages \u2014 they may contain sensitive data and should not be cached.<\/p>\n<p>As you set this up, you\u2019ll notice a pattern: anything carrying a cookie that identifies a logged\u2011in user or cart session should bypass. Most CDNs let you define \u201cbypass cache on cookie.\u201d This is huge for WooCommerce. In practice that often includes cookies like wordpress_logged_in_, woocommerce_items_in_cart, and wp_woocommerce_session_. When any of these appear, edge caching switches off.<\/p>\n<h3><span id=\"Query_strings_and_hit_rate\">Query strings and hit rate<\/span><\/h3>\n<p>One thing that quietly destroys cache hit rate is marketing parameters. UTM tags, fbclid, gclid\u2014they\u2019re useful for analytics but useless for content. If your CDN supports it, ignore or normalize those parameters so ?utm_source= doesn\u2019t create infinite variants of the same page. Keep the few that actually change content (like language or currency), and toss the rest from the cache key.<\/p>\n<h3><span id=\"HTML_caching_for_anonymous_users_only\">HTML caching for anonymous users only<\/span><\/h3>\n<p>If your CDN supports a \u201ccache HTML for anonymous users\u201d switch, use it. Otherwise, build rules like: 1) If Cookie contains wordpress_logged_in_ or wp_woocommerce_session_, then Bypass. 2) If URL matches cart|checkout|my-account|\/wp-|wc-ajax|add-to-cart, then Bypass. 3) Else, Cache and honor s\u2011maxage.<\/p>\n<p>Different vendors name this differently\u2014page rules, cache rules, policies\u2014but the idea is the same. If you need a quick orientation on how one popular provider structures this, the <a href=\"https:\/\/developers.cloudflare.com\/rules\/cache\/\" target=\"_blank\" rel=\"noopener nofollow\">Cloudflare cache rules overview<\/a> gives you the flavor of what to look for, even if you\u2019re using another platform.<\/p>\n<h3><span id=\"Respecting_ETag_and_Last-Modified\">Respecting ETag and Last-Modified<\/span><\/h3>\n<p>CDNs can revalidate content using ETag or Last-Modified. Both are fine. I tend to let WordPress send Last-Modified on HTML and ETag on assets if my stack generates them reliably. Don\u2019t overcomplicate it: ensure your origin returns one or the other, and your CDN can revalidate efficiently without refetching the whole page unnecessarily.<\/p>\n<h2 id=\"section-4\"><span id=\"A_Practical_Walkthrough_From_WordPress_Basics_to_a_WooCommerce_Store\">A Practical Walkthrough: From WordPress Basics to a WooCommerce Store<\/span><\/h2>\n<p>Let me walk you through a pattern I\u2019ve set up dozens of times. It works on small blogs and high\u2011traffic shops, and it\u2019s simple enough to explain to a client over a call without making their eyes glaze over.<\/p>\n<h3><span id=\"Step_1_Make_static_assets_a_home_run\">Step 1: Make static assets a home run<\/span><\/h3>\n<p>Lock down asset caching first. If your theme and plugins use versioned filenames or query strings (like style.css?ver=6.5), you can safely set very long cache lifetimes with immutable. Make sure your deploy process purges the CDN cache when you update. That alone removes a ton of repeat traffic off your origin and speeds up the page\u2019s first paint dramatically.<\/p>\n<h3><span id=\"Step_2_Cache_HTML_for_anonymous_users_nonWooCommerce\">Step 2: Cache HTML for anonymous users (non\u2011WooCommerce)<\/span><\/h3>\n<p>On a content\u2011heavy WordPress site, turn on HTML caching for pages and posts, with s\u2011maxage around 15\u201360 minutes depending on how fast your content changes. I often pick 30 minutes. Add stale-while-revalidate for an extra cushion during traffic spikes. Your page cache plugin should purge the CDN when you publish or update, so the next visitor sees the new content instantly while everyone else enjoys edge\u2011level speed.<\/p>\n<h3><span id=\"Step_3_Bypass_for_loggedin_users_and_sensitive_paths\">Step 3: Bypass for logged\u2011in users and sensitive paths<\/span><\/h3>\n<p>WordPress admin sessions, preview links, customizer sessions, and any user\u2011specific pages should always bypass. It sounds obvious, but I\u2019ve lost count of the number of times a staging setting or an experimental rule accidentally cached wp-login.php. If you ever see a login form loading suspiciously fast from a far\u2011away edge location, check your rules. It shouldn\u2019t be cached.<\/p>\n<h3><span id=\"Step_4_WooCommercecache_the_catalog_protect_the_cart\">Step 4: WooCommerce\u2014cache the catalog, protect the cart<\/span><\/h3>\n<p>This is where stores get a big win without breaking anything. Let the CDN cache product and category pages for anonymous users. The moment a cart or login cookie shows up, bypass the cache. Also bypass for cart, checkout, my account, and wc-ajax endpoints. Add the add-to-cart param to your bypass list so actions happen in real time. Here\u2019s the nice side effect: your hottest product pages will feel instant for new visitors, and you won\u2019t disrupt actual buyers moving through checkout.<\/p>\n<h3><span id=\"Step_5_Normalize_marketing_parameters\">Step 5: Normalize marketing parameters<\/span><\/h3>\n<p>Configure your CDN to ignore parameters like utm_*, fbclid, and gclid in the cache key. Your analytics will still log them; your cache won\u2019t explode into a million nearly identical variants. Don\u2019t forget search query pages\u2014\/ ?s=query\u2014should generally bypass caching too, both for freshness and to avoid polluting your cache with low\u2011value entries.<\/p>\n<h3><span id=\"Step_6_Automate_cache_purging_on_content_changes\">Step 6: Automate cache purging on content changes<\/span><\/h3>\n<p>Your life gets easier when WordPress tells the CDN what changed. Some CDNs support tag\u2011based purging or surrogate keys. If yours does, great\u2014tag posts with their IDs or taxonomies and purge only what\u2019s necessary. If not, set your plugin to purge the homepage, the updated post, and key archives on publish. Don\u2019t over\u2011purge if you can help it. I had a client who purged the entire CDN cache on every minor edit; their origin was constantly rebuilding pages. A smaller, targeted purge kept the site fast and the server calm.<\/p>\n<h2 id=\"section-5\"><span id=\"RealWorld_Header_Recipes_You_Can_Borrow\">Real\u2011World Header Recipes You Can Borrow<\/span><\/h2>\n<p>Let\u2019s put the ideas into concrete examples. These are the kinds of headers I deploy in Nginx\/Apache or set via my CDN rules, adapted to the stack at hand.<\/p>\n<h3><span id=\"Static_assets\">Static assets<\/span><\/h3>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: public, max-age=31536000, immutable\n<\/code><\/pre>\n<p>Use when filenames are versioned. If you\u2019re not fingerprinting, you can still do a long max\u2011age, but make sure you purge on deploy.<\/p>\n<h3><span id=\"Public_WordPress_HTML_anonymous_users\">Public WordPress HTML (anonymous users)<\/span><\/h3>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: public, max-age=0, s-maxage=1800, stale-while-revalidate=60, stale-if-error=86400\n<\/code><\/pre>\n<p>Works beautifully for blogs and for WooCommerce category\/product pages when no cart or login cookie is present.<\/p>\n<h3><span id=\"WooCommerce_cartcheckoutaccount\">WooCommerce cart\/checkout\/account<\/span><\/h3>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: no-store\n<\/code><\/pre>\n<p>No ambiguity. Don\u2019t cache any of it. That includes admin-ajax calls related to cart fragments or blocks, and any wc-ajax endpoints.<\/p>\n<h3><span id=\"API_and_admin_endpoints\">API and admin endpoints<\/span><\/h3>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: no-store\n<\/code><\/pre>\n<p>\/wp-json\/ endpoints may be public or private depending on your use; if you\u2019re not certain, keep them dynamic first and tighten later.<\/p>\n<h3><span id=\"Optional_SurrogateControl_for_CDNs_that_support_it\">Optional: Surrogate\u2011Control for CDNs that support it<\/span><\/h3>\n<p>Some CDNs obey Surrogate\u2011Control in addition to Cache\u2011Control, which lets you differentiate browser and edge behavior more cleanly. For example, you can keep the browser revalidating while the CDN holds a longer copy:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">Cache-Control: public, max-age=0\nSurrogate-Control: max-age=1800, stale-while-revalidate=60\n<\/code><\/pre>\n<p>If your provider supports it, the <a href=\"https:\/\/developer.fastly.com\/reference\/http\/http-headers\/Surrogate-Control\/\" target=\"_blank\" rel=\"noopener nofollow\">Surrogate\u2011Control reference<\/a> is a helpful read.<\/p>\n<h2 id=\"section-6\"><span id=\"Cookies_Variants_and_the_Subtle_Ways_Caches_Go_Sideways\">Cookies, Variants, and the Subtle Ways Caches Go Sideways<\/span><\/h2>\n<p>I wish I could say all plugins play by the same rules, but they don\u2019t. Some drop cookies on every visitor. Others add a toolbar for certain roles or produce tiny bits of page personalization. That\u2019s fine, but it means you have to be deliberate about when to cache and when to bypass.<\/p>\n<h3><span id=\"Bypass_on_cookie_not_Vary_Cookie\">Bypass on cookie, not Vary: Cookie<\/span><\/h3>\n<p>This is a big one. Vary: Cookie can explode your cache into a thousand tiny variants because every unique cookie value creates a new version of the page. Instead, prefer rules like \u201cIf Cookie contains wordpress_logged_in_, bypass cache.\u201d That keeps the anonymous cache clean and warm, and your dynamic users happy.<\/p>\n<h3><span id=\"Device_variants\">Device variants<\/span><\/h3>\n<p>If you\u2019re using a responsive design (most themes are), avoid device\u2011specific cache keys. Don\u2019t vary on user\u2011agent unless you truly need separate mobile\/desktop content. Every variant is a smaller, colder cache. Keep it simple and let CSS handle small differences.<\/p>\n<h3><span id=\"CDN_default_behavior_versus_your_intent\">CDN default behavior versus your intent<\/span><\/h3>\n<p>Many CDNs don\u2019t cache HTML by default; they cache assets only. That\u2019s actually a good safety net until your rules are right. When you\u2019re ready, enable HTML caching intentionally with your bypass rules in place. I\u2019ve seen teams flip \u201ccache everything\u201d on a Friday afternoon and spend the weekend putting out fires. Go slow. Test with private windows. Check response headers with curl. Confirm that cart and checkout are coming from your origin, not an edge node halfway across the world.<\/p>\n<h2 id=\"section-7\"><span id=\"How_I_Test_and_Troubleshoot_Without_Losing_My_Weekend\">How I Test and Troubleshoot (Without Losing My Weekend)<\/span><\/h2>\n<p>When I\u2019m validating a setup, I keep it boring and methodical. One client joked that my testing checklist felt like a pilot\u2019s pre\u2011flight routine. It\u2019s not glamorous, but it works.<\/p>\n<h3><span id=\"Check_headers_with_curl\">Check headers with curl<\/span><\/h3>\n<p>From your terminal:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">curl -I https:\/\/example.com\/\n<\/code><\/pre>\n<p>Look for CF\u2011Cache\u2011Status or your provider\u2019s equivalent, plus the Cache\u2011Control line and any Vary headers. Hit a product page as an anonymous user. Then log in (or add a product to cart), hit it again, and confirm the cache now bypasses. Repeat for \/cart\/ and \/checkout\/\u2014these should always be origin hits.<\/p>\n<h3><span id=\"Simulate_edge_behavior\">Simulate edge behavior<\/span><\/h3>\n<p>Most CDNs show you whether a response was HIT, MISS, BYPASS, or EXPIRED. If your dashboard shows 100% MISS on HTML, you\u2019re either not caching pages at all or your rules are too conservative. If it shows suspicious HITs on cart or checkout, tighten your bypass rules immediately.<\/p>\n<h3><span id=\"Watch_the_query_strings\">Watch the query strings<\/span><\/h3>\n<p>Open your network tab and click a URL that includes utm_source or fbclid. If the CDN treats each as a separate cache key, you\u2019ll see a lot of misses on essentially the same page. Tell your CDN to ignore these parameters for caching and try again. You\u2019ll usually see the hit rate jump.<\/p>\n<h3><span id=\"Fight_double_caching_carefully\">Fight double caching carefully<\/span><\/h3>\n<p>Page cache plugins are great, but if they cache and your CDN caches too, you can end up with confusing behavior. My rule of thumb: let the CDN do the heavy lifting for anonymous HTML; let your plugin focus on smart purges and origin\u2011side speed. If you keep the plugin\u2019s disk cache on, that\u2019s fine\u2014just make sure you understand which layer you\u2019re debugging when something looks stale.<\/p>\n<h2 id=\"section-8\"><span id=\"A_Story_About_Fragments_Midnight_and_a_Lesson_I_Still_Use\">A Story About Fragments, Midnight, and a Lesson I Still Use<\/span><\/h2>\n<p>Several years ago, a fashion store was preparing for a limited launch. We cleaned up their images, pushed their assets to long\u2011lived immutable caching, and set HTML caching for anonymous users with a 20\u2011minute s\u2011maxage. Everything looked perfect in staging. Five minutes into the launch, add\u2011to\u2011cart felt slow. We discovered an old fragments script firing background AJAX calls on every page view for every user\u2014anonymous included. It wasn\u2019t caching; it was hammering the origin for no reason.<\/p>\n<p>The fix wasn\u2019t fancy. We bypassed admin-ajax calls at the edge, disabled the fragments script for anonymous users, and the site calmed down instantly. The lesson was simple: even with great headers and rules, one rogue background call can nibble away at your gains. Don\u2019t chase a bigger CDN until you\u2019ve removed the small leaks.<\/p>\n<h2 id=\"section-9\"><span id=\"Putting_It_All_Together_A_Simple_Reliable_Blueprint\">Putting It All Together: A Simple, Reliable Blueprint<\/span><\/h2>\n<p>If you\u2019re building your checklist, this is what mine usually looks like for WordPress\/WooCommerce:<\/p>\n<p>1) Static assets: year\u2011long caching with immutable. 2) HTML for anonymous users: Cache with s\u2011maxage around 30 minutes; add stale-while-revalidate. 3) Bypass rules: wp-login.php, wp-admin\/, admin-ajax.php, wp-cron.php, preview\/customizer, cart, checkout, my-account, wc-ajax, add-to-cart. 4) Bypass on cookie: wordpress_logged_in_, wp_woocommerce_session_, woocommerce_items_in_cart. 5) Parameter normalization: ignore utm_*, fbclid, gclid in cache key. 6) Purge automation: purge on publish\/update; tag\u2011based if available. 7) Sanity checks: verify headers with curl, confirm edge HITs on anonymous pages, and BYPASS on sensitive paths.<\/p>\n<p>If you want to go deeper into edge patterns tailored for WordPress, I wrote a friendly guide that dives into HTML caching and bypass tricks you can adapt right away: <a href=\"https:\/\/www.dchost.com\/blog\/en\/wordpress-icin-cdn-onbellek-kurallari-nasil-kurulur-woocommercede-html-cache-bypass-ve-edge-ayarlariyla-uctan-uca-hiz\/\">CDN Caching Rules for WordPress: The Friendly Guide to HTML Caching, Bypass Tricks, and Edge Settings That Won\u2019t Break WooCommerce<\/a>. It pairs nicely with this article when you\u2019re ready to fine\u2011tune.<\/p>\n<h2 id=\"section-10\"><span id=\"Advanced_Touches_When_Youre_Ready\">Advanced Touches When You\u2019re Ready<\/span><\/h2>\n<p>Once your baseline is solid, there are a few upgrades that feel like flipping a turbo switch.<\/p>\n<h3><span id=\"Stale_TTLs_for_resilience\">Stale TTLs for resilience<\/span><\/h3>\n<p>stale-while-revalidate and stale-if-error are more than performance tricks\u2014they\u2019re resilience tools. During traffic spikes or brief origin hiccups, your CDN can keep serving a slightly older page while it fetches a fresh copy or waits for your server to come back. It\u2019s the difference between a smooth sale and a lot of angry DMs.<\/p>\n<h3><span id=\"Tagbased_purging\">Tag\u2011based purging<\/span><\/h3>\n<p>If your CDN supports surrogate keys or tags, use them. Posts can carry tags for their IDs, categories, and the homepage. Update a product, purge that product and its category pages; leave everything else warm. You\u2019ll feel the difference on sites with large catalogs.<\/p>\n<h3><span id=\"Smart_cache_keys\">Smart cache keys<\/span><\/h3>\n<p>Custom cache keys let you include exactly what matters\u2014language, currency\u2014and ignore what doesn\u2019t\u2014marketing fluff. This keeps your cache both accurate and compact.<\/p>\n<h3><span id=\"Monitor_whats_actually_happening\">Monitor what\u2019s actually happening<\/span><\/h3>\n<p>Don\u2019t set and forget. Watch cache hit ratios, origin error rates, and your slowest endpoints after a change. If you notice login or cart getting slower, check whether a plugin update introduced new AJAX calls or cookies that shift pages into bypass unexpectedly. The fix is usually one small rule.<\/p>\n<h2 id=\"section-11\"><span id=\"Common_Gotchas_I_See_And_How_I_Dodge_Them\">Common Gotchas I See (And How I Dodge Them)<\/span><\/h2>\n<p>1) Plugin adds Vary: * or Vary: Cookie without you noticing. Result: your cache explodes into useless variants. Solution: remove the Vary or change to bypass rules on specific cookies.<\/p>\n<p>2) Caching search results and preview pages. It seems harmless until someone shares a preview link and suddenly strangers see draft content. Solution: bypass preview=true, and bypass search (?s=) unless you have a good reason not to.<\/p>\n<p>3) Caching admin-ajax. You\u2019d think nobody does this. It happens all the time with blanket \u201ccache everything\u201d settings. Solution: explicitly bypass \/wp-admin\/admin-ajax.php and all wc-ajax endpoints.<\/p>\n<p>4) Forgetting to ignore tracking parameters. Hit rates tank; origin gets hammered; nobody knows why. Solution: normalize or ignore utm_*, fbclid, gclid for the cache key.<\/p>\n<p>5) Over\u2011purging on deploy. Purging the entire CDN cache after a single post edit is like emptying your fridge because you ate an apple. Solution: purge what changed; keep the rest warm.<\/p>\n<h2 id=\"section-12\"><span id=\"If_Youre_New_to_CDNs_A_Gentle_Ramp\">If You\u2019re New to CDNs, A Gentle Ramp<\/span><\/h2>\n<p>I\u2019ve walked plenty of folks through their first CDN setup. Here\u2019s the path that keeps stress low:<\/p>\n<p>Start by caching only static assets. Confirm big performance gains right away. Then add HTML caching for anonymous users on a non\u2011critical section of your site (like your blog) and monitor. Once that\u2019s steady, bring your catalog pages into the fold. Keep WooCommerce interactions dynamic. Only after a week or two of smooth sailing should you consider advanced touches like surrogate keys and fine\u2011grained cache keys. You\u2019ll build confidence step by step.<\/p>\n<p>If you want a broad overview of how CDNs actually help and where they sit in your stack, this <a href=\"https:\/\/www.dchost.com\/blog\/en\/content-delivery-network-cdn-nedir-web-siteniz-icin-avantajlari\/\">friendly explanation of what a CDN is and why it matters<\/a> makes a good side read once you\u2019re done here.<\/p>\n<h2 id=\"section-13\"><span id=\"Edge_Rules_in_Human_Language\">Edge Rules in Human Language<\/span><\/h2>\n<p>Clients often ask me to \u201cjust write down the rules in English.\u201d Here\u2019s how I phrase them before I translate into a vendor\u2019s UI:<\/p>\n<p>1) If the request method is not GET or HEAD, bypass. 2) If the path starts with \/wp-admin\/ or equals \/wp-login.php, bypass. 3) If the path equals \/wp-cron.php or \/wp-admin\/admin-ajax.php, bypass. 4) If the URL contains preview=true or customize_changeset_uuid, bypass. 5) If the path starts with \/cart\/, \/checkout\/, or \/my-account\/, bypass. 6) If the query contains wc-ajax or add-to-cart, bypass. 7) If the Cookie contains wordpress_logged_in_, wp_woocommerce_session_, or woocommerce_items_in_cart, bypass. 8) Otherwise, cache and respect s\u2011maxage; ignore utm_*, gclid, and fbclid in the cache key.<\/p>\n<p>Is it perfect for every site? No. But it\u2019s the clean starting point that gets you 90% of the way there with minimal drama.<\/p>\n<h2 id=\"section-14\"><span id=\"What_About_Security_Headers_and_HTTPS\">What About Security Headers and HTTPS?<\/span><\/h2>\n<p>Security headers like HSTS and CSP don\u2019t change cache policy directly, but they do matter for the bigger picture of trust and performance. If you\u2019re rolling out caching, take a minute to ensure HTTPS is everywhere and your security headers are sensible. Many CDNs make this easy with one\u2011click TLS and header injections at the edge. It\u2019s a quick win that pairs naturally with your caching work. If you need a friendly primer on the security side, I\u2019ve written about HTTP security headers elsewhere, and it aligns nicely with a clean CDN setup.<\/p>\n<h2 id=\"section-15\"><span id=\"WrapUp_Your_Site_Only_Fasterand_Still_Correct\">Wrap\u2011Up: Your Site, Only Faster\u2014and Still Correct<\/span><\/h2>\n<p>Let me circle back to that morning with the flash sale. We didn\u2019t add more servers. We didn\u2019t change hosting plans. We didn\u2019t install a magic plugin. We simply cleaned up cache headers, set thoughtful edge rules, and stopped a few background calls from hammering the origin. The store started to breathe. Pages loaded fast for new visitors, and carts and checkout stayed precise and up\u2011to\u2011date for real buyers. That\u2019s the whole game: speed for everyone, correctness for the people who matter most.<\/p>\n<p>If you\u2019re just getting started, begin with assets. Then enable HTML caching for anonymous users, protect WooCommerce\u2019s sensitive paths with firm bypasses, normalize marketing parameters, and automate purges. Keep your rules human\u2011readable so you can debug them under pressure. And when in doubt, choose correctness over aggression; you can always dial the cache up once you see stable behavior.<\/p>\n<p>Hope this gave you a calm, practical path forward. If it saved you even one late\u2011night panic, I\u2019ll call that a win. See you in the next post\u2014where we\u2019ll keep peeling back the layers and make your stack not just faster, but friendlier to run.<\/p>\n<hr>\n<p>Further reading that fits right into this topic:<\/p>\n<p>\u2022 The official docs are handy when you want to double\u2011check directive behavior: <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Cache-Control\" target=\"_blank\" rel=\"noopener nofollow\">how Cache\u2011Control works<\/a> and a vendor view like <a href=\"https:\/\/developers.cloudflare.com\/rules\/cache\/\" target=\"_blank\" rel=\"noopener nofollow\">Cloudflare\u2019s cache rules overview<\/a>. Read them once, apply forever.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>So there I was, staring at a WooCommerce site at 6:07 AM with coffee in one hand and a spinning loader in the other. The store had a flash sale. Traffic was pouring in. The homepage felt snappy thanks to the CDN, but the cart and checkout were acting like they were stuck in molasses. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1344,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-1343","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-teknoloji"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/1343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=1343"}],"version-history":[{"count":0,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/1343\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/1344"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=1343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=1343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=1343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}