{"id":421956,"date":"2026-03-04T09:50:13","date_gmt":"2026-03-04T08:50:13","guid":{"rendered":"https:\/\/remkusdevries.com\/?p=421956"},"modified":"2026-03-05T09:10:21","modified_gmt":"2026-03-05T08:10:21","slug":"response-headers","status":"publish","type":"post","link":"https:\/\/remkusdevries.com\/response-headers\/","title":{"rendered":"Response Headers and Everything You Should Know About Them"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Most WordPress performance advice focuses on assets. Compress images, minify CSS, defer JavaScript, etc. You&#8217;ve seen them. And, those things matter, but they\u2019re not the layer that ultimately decides whether your site scales.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Headers do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Response headers are the instructions that tell browsers, CDNs, and proxies how they\u2019re allowed to treat your content. They determine whether a response can be reused, whether multiple variations exist, and how long something may live before it needs to be refreshed.<\/p>\n\n\n\n<p class=\"has-base-color has-secondary-background-color has-text-color has-background has-link-color wp-elements-334695f8240c049214045723e6f1ea5d wp-block-paragraph\" style=\"border-top-left-radius:var(--wp--preset--border-radius--sm);border-top-right-radius:var(--wp--preset--border-radius--sm);border-bottom-left-radius:var(--wp--preset--border-radius--sm);border-bottom-right-radius:var(--wp--preset--border-radius--sm)\">This is post is an adaption of a lesson in my <a href=\"https:\/\/withinwp.com\/courses\/make-wordpress-fast\/\" target=\"_blank\" rel=\"noreferrer noopener\">Make WordPress Fast course<\/a>, the most complete course on all aspects of WordPress Performance Optimization out there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before we talk about specific headers, it helps to understand what headers actually are. Every time a browser requests something from a server, the request and the response contain two parts. The first part is the visible content, like HTML, JSON, images, or CSS. The second part is a set of small metadata instructions called <strong>headers<\/strong>. These headers travel along with the request and response and tell the systems involved how the data should be handled.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"696\" src=\"https:\/\/remkusdevries.com\/wp-content\/uploads\/2026\/03\/response-headers-1280x696.jpg\" alt=\"Example of Response Headers\" class=\"wp-image-421960\" srcset=\"https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-768x417.jpg?quality=100&amp;width=384 384w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-820x446.jpg?quality=100&amp;width=410 410w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers.jpg?quality=100&amp;width=530 530w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-1280x696.jpg?quality=100&amp;width=640 640w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-768x417.jpg?quality=100&amp;width=768 768w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-820x446.jpg?quality=100&amp;width=820 820w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-2048x1113.jpg?quality=100&amp;width=1024 1024w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers.jpg?quality=100&amp;width=1060 1060w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers-1280x696.jpg?quality=100&amp;width=1280 1280w, https:\/\/remkusdevries.com\/acd-cgi\/img\/v1\/wp-content\/uploads\/2026\/03\/response-headers.jpg?quality=100&amp;width=1590 1590w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Headers don\u2019t contain the content itself. Instead, they describe the rules around that content. They can tell a browser whether it\u2019s allowed to cache a page, tell a CDN how long it may store a response, tell proxies whether multiple versions of a response might exist, or tell the browser how the payload was compressed. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In other words, headers don\u2019t change what your site sends, they change how that response behaves across the entire delivery chain. This is why headers matter so much for performance. They are the control layer that determines whether responses can be reused, when they must be refreshed, and how many variations of a response might exist. Understanding them means understanding how your site actually behaves once it leaves the server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can have a perfectly optimized page, a fast server, and a well-configured CDN. If the headers contradict your caching strategy, the infrastructure cannot help you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The key to understanding headers is realizing they answer only three performance questions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Can this response be reused?<br>How many versions of it exist?<br>How long may it live before it needs to be refreshed?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once you look at headers through those three questions, they stop feeling like protocol trivia and start looking like a control panel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So let&#8217;s talk about them. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-first-decide-whether-the-response-is-allowed-to-be-cached\">First: Decide Whether the Response Is Allowed to Be Cached<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first job headers perform is determining whether reuse is allowed at all. The directive that controls this is Cache-Control.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cache-Control can allow shared caches such as CDNs to store a response, restrict it to browser-only caching, or forbid storage entirely. A typical response might look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cache-Control: public, max-age=300<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The keyword <code>public<\/code> allows shared caches to store the response. If the directive instead says <code>private<\/code>, a CDN must not store it, even if the page itself appears identical for every visitor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then there is the most restrictive directive: <code>no-store<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cache-Control: no-store<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This tells every cache layer not to store the response anywhere. Not in the browser. Not in intermediary proxies. Not at the edge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is effectively the nuclear option for caching.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is where many WordPress sites accidentally undermine their own performance. A page that should be reusable across visitors returns a <code>private<\/code> or <code>no-store<\/code> directive because a plugin sets conservative defaults.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The CDN obeys the instruction, and suddenly the request travels all the way back to the origin for every visitor. At that point, it doesn\u2019t matter how good your infrastructure is. The response has been declared non-reusable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is also an important distinction between browser caching and shared caching.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>max-age<\/code> directive defines how long the browser may reuse the response. The <code>s-maxage<\/code> directive defines how long shared caches such as CDNs may reuse it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you don\u2019t differentiate between those two intentionally, you lose precision. You might cache aggressively in the browser but barely at the edge, or the opposite.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Understanding which cache layer you\u2019re instructing is the first step toward real control.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-next-decide-how-many-versions-of-the-response-may-exist\">Next: Decide How Many Versions of the Response May Exist<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once a response is allowed to be cached, the next question becomes how many variations of that response might exist. That is  controlled by the <code>Vary<\/code> header.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vary tells caches what aspects of the request determine whether the response should be treated as unique. A common example looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Vary: Accept-Encoding<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This tells caches that the response differs depending on whether the client supports gzip or brotli compression. That\u2019s normal and expected. But variation can quickly spiral out of control.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a response includes something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Vary: User-Agent<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The cache now treats every browser type as potentially unique. Since user agents vary widely, the number of possible versions multiplies quickly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Even more dangerous is the directive we discussed in the previous module section:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Vary: Cookie<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This tells caches that every cookie combination might produce a different response.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now imagine what that means in a WordPress environment where multiple plugins set cookies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of one cached page, you may end up with dozens or hundreds of variants. The cache technically still works, but reuse becomes rare. This phenomenon is called cache fragmentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Caching may be enabled, but the cache hit ratio collapses because the system is storing too many variations. The hygiene rule here is straightforward: only vary on dimensions that genuinely change the output.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every additional variation reduces reuse, and reuse is the foundation of scalable performance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-then-decide-how-long-the-response-may-live\">Then: Decide How Long the Response May Live<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Even if a response is eligible for caching and does not create excessive variations, the system still needs to know how long the response may remain valid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the lifetime question.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Cache-Control<\/code> directives such as <code>max-age<\/code> and <code>s-maxage<\/code> define that lifetime.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cache-Control: public, max-age=300, s-maxage=600<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this example, the browser may reuse the response for five minutes, while shared caches such as CDNs may reuse it for ten minutes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By the way, it&#8217;s possible you will also encounter the <code>Expires<\/code> header in this context:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Expires: Wed, 21 Oct 2026 07:28:00 GMT<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Expires<\/code> is the older mechanism for defining expiration. Modern systems primarily rely on <code>Cache-Control<\/code> instead. If both are present, they must agree. Conflicting directives create unpredictable behavior across browsers and proxies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Short lifetimes increase freshness but reduce reuse. Long lifetimes increase reuse but may delay updates.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal is to align the lifetime of the cache with the volatility of the content.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-happens-when-cached-content-becomes-stale\">What Happens When Cached Content Becomes Stale<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eventually, cached responses expire. At that point, the system must decide whether to regenerate the content or simply confirm that nothing has changed. This is where revalidation mechanisms come in.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two headers enable this behavior: <code>ETag<\/code> and <code>Last-Modified<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An <code>ETag<\/code> might look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ETag: \"abc123\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When a browser or CDN has a cached response, it can ask the origin server a conditional question.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u201cHas this resource changed since I last saw it?\u201d If nothing has changed, the server responds with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>304 Not Modified<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">No payload is transferred. The cache simply continues using its existing copy. That\u2019s efficient, but it only works when the validation signals are stable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If <code>ETag<\/code>s are generated differently across multiple origin servers, caches cannot reliably compare them. If <code>Last-Modified<\/code> timestamps change unnecessarily, caches revalidate even when the content hasn\u2019t actually changed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Revalidation is cheaper than regenerating the entire response, but it still involves a round trip. At scale, those round trips accumulate.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-payload-handling-making-the-response-efficient-to-deliver\">Payload Handling: Making the Response Efficient to Deliver<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beyond caching behavior, headers also influence how the payload itself is delivered. The <code>Content-Encoding<\/code> header tells the browser how the response body was compressed.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Content-Encoding: br<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This might indicate brotli compression. If textual responses such as HTML, CSS, or JavaScript are served without compression, unnecessary bandwidth is consumed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But compression must be consistent across the stack. If both the origin and the CDN compress responses independently without coordination, the result can be inconsistent caching behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>Content-Type<\/code> header determines how the browser interprets the payload.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Content-Type: text\/html<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Incorrect types can break preloading behavior, prevent caching optimizations, or interfere with early rendering strategies. These headers may seem minor, but they affect how efficiently browsers process and render the response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-security-headers-and-the-performance-tradeoff\">Security Headers and the Performance Tradeoff<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Modern sites often include additional headers related to security policy. <code>Content-Security-Policy<\/code> defines which resources a page may load. <code>Permissions-Policy<\/code> controls access to certain browser capabilities. <code>Strict-Transport-Security<\/code> enforces HTTPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These headers improve security posture, but they also require careful tuning. A very strict <code>Content-Security-Policy<\/code> might block third-party scripts that would otherwise load asynchronously. In some cases, that improves performance. In others, it breaks functionality or forces complicated workarounds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The key principle is alignment. Headers should reflect deliberate system design, not the accidental accumulation of plugin defaults.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-diagnosing-header-behavior-in-practice\">Diagnosing Header Behavior in Practice<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Understanding headers conceptually is useful, but real control comes from observing them. Open your browser\u2019s developer tools and inspect the main document request, and then ask three questions.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Is this response eligible for shared caching?<\/li>\n\n\n\n<li>How many variations does this response create?<\/li>\n\n\n\n<li>How long is the response allowed to live?<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Then compare different contexts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Look at the site while logged out and while logged in. Compare the homepage with a product page. Compare an anonymous browser session with one carrying cookies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You want to see intentional differences.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The checkout page might legitimately bypass caching. The homepage probably should not. Once you start reading headers this way, performance debugging becomes faster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of staring at a waterfall and guessing, you immediately classify the problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Is caching disallowed?<br>Is variation excessive?<br>Is lifetime too short?<br>Is revalidation constant?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Those questions lead directly to the root cause.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-header-hygiene-matters\">Why Header Hygiene Matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Headers are not decorative metadata. They are the instructions that define how every layer of the web stack behaves. If those instructions contradict your caching strategy, your caching strategy does not exist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once you understand that, performance optimization stops being a series of tweaks. It becomes the deliberate design of how your site is allowed to behave across browsers, CDNs, and origin servers.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most WordPress performance advice focuses on assets. Compress images, minify CSS, defer JavaScript, etc. You&#8217;ve seen them. And, those things matter, but they\u2019re not the layer that ultimately decides whether your site scales. Headers do. Response headers are the instructions that tell browsers, CDNs, and proxies how they\u2019re allowed to treat your content. They determine [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":421960,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":"","_links_to":"","_links_to_target":"","_wordproof_timestamp":false},"categories":[1484],"tags":[],"class_list":["post-421956","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-wordpress-performance"],"acf":[],"_links":{"self":[{"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/posts\/421956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/comments?post=421956"}],"version-history":[{"count":2,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/posts\/421956\/revisions"}],"predecessor-version":[{"id":421963,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/posts\/421956\/revisions\/421963"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/media\/421960"}],"wp:attachment":[{"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/media?parent=421956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/categories?post=421956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/remkusdevries.com\/wp-json\/wp\/v2\/tags?post=421956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}