<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Peakhour.IO - Learning</title><link href="https://www.peakhour.io/" rel="alternate"></link><link href="https://www.peakhour.io/feeds/learning.atom.xml" rel="self"></link><id>https://www.peakhour.io/</id><updated>2026-07-29T00:00:00+10:00</updated><entry><title>Interaction to Next Paint (INP)</title><link href="https://www.peakhour.io/blog/interaction-to-next-paint/" rel="alternate"></link><published>2023-09-11T13:00:00+10:00</published><updated>2023-09-11T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-09-11:/blog/interaction-to-next-paint/</id><summary type="html">&lt;p&gt;Google is introducing a new Core Web Vital to replace First Input Delay, read on to learn all about it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Google has announced that &lt;a href="https://web.dev/inp/"&gt;Interaction to Next Paint (INP)&lt;/a&gt; will replace First Input Delay (FID) as a
&lt;a href="/blog/web-vitals/"&gt;Core Web Vital&lt;/a&gt;
as of March 2024. Introduced as a metric in 2022, INP covers gaps in FID by measuring more of what happens after a user
interacts with a page.&lt;/p&gt;
&lt;p&gt;To help site owners prepare for its introduction as a Core Web Vital, INP is already included in the
&lt;a href="/blog/what-is-the-chrome-ux-report-crux/"&gt;Chrome User Experience Report (CrUX)&lt;/a&gt;.
By analysing the CrUX data, website owners can see their current INP performance and make targeted optimisations ahead
of the March 2024 change.&lt;/p&gt;
&lt;h2&gt;A better metric than First Input Delay&lt;/h2&gt;
&lt;p&gt;First Input Delay, as its name suggests, only measures the delay between an input, such as a keypress or mouse click, and
the point where the browser begins to handle that event. It does not include the time spent processing the input. It only
measures how long the browser was blocked before it could start handling it.&lt;/p&gt;
&lt;p&gt;That leaves two issues: it only considers the FIRST event, and it does not measure how long it takes for the user to see
the result of their input.&lt;/p&gt;
&lt;p&gt;INP is designed to cover both issues. It measures the latency of ALL 'interactions' through to the visual response for
that interaction. As explained by Google, an interaction like a tap on a touch screen device can consist of several input
events.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;"An interaction's latency consists of the single longest duration of a group of event handlers that drives the
interaction, from the time the user begins the interaction to the moment the next frame is presented with visual feedback."&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;After measuring all interactions, the final INP score is the longest interaction observed, ignoring any outliers.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;What Constitutes a Good Score&lt;/h2&gt;
&lt;p&gt;INP is measured in milliseconds (ms), with lower scores indicating better performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Good: &amp;lt; 200 ms&lt;/li&gt;
&lt;li&gt;Needs Improvement: 200-500 ms&lt;/li&gt;
&lt;li&gt;Poor: &amp;gt; 500 ms&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="text-center"&gt;
    &lt;img src="/static/images/blog/inp.jpg" alt="Interaction To Next Paint" style="max-width: 700px"/&gt;
&lt;/div&gt;

&lt;h2&gt;How to ensure you have a good INP score&lt;/h2&gt;
&lt;h3&gt;Minimise Main-Thread Work&lt;/h3&gt;
&lt;p&gt;Long-running JavaScript can block the main thread and increase INP times. Break these tasks into smaller parts and run
them asynchronously to reduce delays.&lt;/p&gt;
&lt;h3&gt;Efficiently Use Browser APIs&lt;/h3&gt;
&lt;p&gt;APIs that trigger layout recalculations can be expensive. Use them sparingly and look for alternatives that put less
pressure on the browser.&lt;/p&gt;
&lt;h3&gt;Defer Non-Essential CSS and Scripts&lt;/h3&gt;
&lt;p&gt;Postpone the loading of non-critical CSS and JavaScript. Use techniques like asynchronous loading to improve INP scores.&lt;/p&gt;
&lt;h3&gt;Monitor Third-Party Scripts&lt;/h3&gt;
&lt;p&gt;Heavy third-party scripts can degrade INP performance. Use asynchronous or deferred loading for these scripts to limit
their impact.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Google estimates that 90% of a user's time on a page is after it has finished loading. FID focused on first impressions,
with the assumption that a fast start meant the page would stay responsive. Interaction to Next Paint addresses that gap
and gives a more accurate view of user experience. If you want to know your current INP score, you can use our free
&lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt; to view it alongside your other Web Vitals, and see
how your website compares to your competitors.&lt;/p&gt;</content><category term="Learning"></category><category term="Core Web Vitals"></category><category term="Web Performance"></category><category term="Analytics"></category><category term="Caching"></category><category term="Browser Fingerprinting"></category><category term="Features"></category></entry><entry><title>Understanding HTTP Link Headers</title><link href="https://www.peakhour.io/blog/http-link-headers/" rel="alternate"></link><published>2023-05-24T13:00:00+10:00</published><updated>2026-07-29T00:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-05-24:/blog/http-link-headers/</id><summary type="html">&lt;p&gt;How HTTP Link headers move preload, preconnect, and related resource hints into the response headers, plus the cases where that is operationally useful.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Sometimes the browser needs to discover a critical asset before it reaches the corresponding tag in the HTML. An HTTP
Link header lets the server put that resource hint in the response headers, where the browser can see it earlier. It
also works when an edge layer can change headers but should not rewrite the response body.&lt;/p&gt;
&lt;p&gt;Here are some sample request headers sent by my browser when requesting a page on the Peakhour website:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="sr"&gt;/html,application/xhtml+xml,application/xml;q=0.9,*/&lt;/span&gt;&lt;span class="o"&gt;*;&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;
&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deflate&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt;
&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Language&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AU&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;
&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alive&lt;/span&gt;
&lt;span class="n"&gt;Cookie&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_ga_NRWSVE0PSC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GS1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;1.1685943893&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;13.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;1685943893.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;peakhour&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;
&lt;span class="n"&gt;Sec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Fetch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Dest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;
&lt;span class="n"&gt;Sec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Fetch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Mode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;navigate&lt;/span&gt;
&lt;span class="n"&gt;Sec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Fetch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Site&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mozilla&lt;/span&gt;&lt;span class="sr"&gt;/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4.1 Safari/&lt;/span&gt;&lt;span class="mf"&gt;605.1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;The HTTP Link header&lt;/h2&gt;
&lt;p&gt;An HTTP Link header lets a server send
context about a document back to a client. It can identify related
resources or the direct location of a specific asset. For page-load optimisation, Link headers can be an alternative to
putting preload/preconnect/prefetch hints in the HTML.&lt;/p&gt;
&lt;h2&gt;History&lt;/h2&gt;
&lt;p&gt;HTTP Link headers were proposed as a standard in the late 1990s, around the same time the
HTTP/1.1 protocol was defined. However, it wasn't until 2010 that HTTP Link headers were officially recognised by
the Internet Engineering Task Force (IETF) in RFC 5988, which described their purpose and functionality.&lt;/p&gt;
&lt;h2&gt;Uses&lt;/h2&gt;
&lt;p&gt;HTTP Link headers have several uses in web development. Some common examples are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pagination&lt;/strong&gt;: Say we have a blog site with hundreds of posts, and we display 10 posts per page.
  When a user requests a page, we can use Link headers to provide URLs for the next and previous pages.
  This helps navigation through the large list of posts. Here's an example of how it might look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Link:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;/posts?page=2&amp;gt;;&lt;span class="w"&gt; &lt;/span&gt;rel=&amp;quot;next&amp;quot;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;/posts?page=4&amp;gt;;&lt;span class="w"&gt; &lt;/span&gt;rel=&amp;quot;prev&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Preloading&lt;/strong&gt;: Suppose we have a heavy image or a large CSS file that we know will be required for a webpage.
  We can use a Link header to tell the browser to start downloading it early, improving the perceived page
  load speed. For instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;big&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;picture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Resource Hints&lt;/strong&gt;: Link headers can give the browser hints about resources that might be needed in the
  future, so the browser can decide whether to fetch them ahead of time. For instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;Link&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;myscript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;js&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;prefetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Comparison to Link Tags and Why Headers Can Be Better&lt;/h2&gt;
&lt;p&gt;Now you might be wondering, "Why use Link headers when we can use HTML Link tags?" There are several reasons
HTTP Link headers might be a better choice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Faster Processing&lt;/strong&gt;: Since HTTP Link headers are part of the HTTP response, they arrive before the HTML document.
  This allows browsers to start preloading or prefetching resources sooner, which can improve page load times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greater Flexibility&lt;/strong&gt;: HTTP Link headers can be used in situations where HTML Link tags cannot. For instance, they
  can be used with file types that don't support HTML, like JSON or XML. They can also be added by a third party, e.g.,
  &lt;strong&gt;an edge delivery layer such as Peakhour&lt;/strong&gt;, without the need to parse and rewrite the HTML document.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Less Clutter&lt;/strong&gt;: Link headers can make your HTML less cluttered, as you can avoid filling the HTML document with
  numerous Link tags.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Use Link headers where they solve a measured delay&lt;/h2&gt;
&lt;p&gt;Do not preload every plausible dependency. An unnecessary preload competes with resources the page actually needs.
Start with a waterfall, identify a late-discovered critical resource, add the Link header at the application or edge,
and measure the result again. The header is valuable when it advances a real dependency without creating extra work for
the browser.&lt;/p&gt;</content><category term="Learning"></category><category term="HTTP"></category><category term="Web Performance"></category><category term="Caching"></category><category term="Rate Limiting"></category><category term="Core Web Vitals"></category><category term="CDN"></category></entry><entry><title>Cdn-Cache-Control</title><link href="https://www.peakhour.io/blog/cdn-cache-control-header/" rel="alternate"></link><published>2022-02-28T13:00:00+11:00</published><updated>2022-02-28T13:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2022-02-28:/blog/cdn-cache-control-header/</id><summary type="html">&lt;p&gt;CDN-Cache-Control is a proposed new header to augment the venerable Cache-Control. Its aim is to make controlling caching easier as CDNs become ubiquitous.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Caching headers are easy to get wrong. Public resources may be cached in one layer, while the server
configuration prevents an otherwise cacheable resource from being stored where you expect.&lt;/p&gt;
&lt;p&gt;Controlling caching without a service like Peakhour usually means working directly with Cache-Control headers.
This header has a wide range of fields and directives. Each one tells downstream clients (e.g., proxies, shared caches, and end browsers)
how to handle caching for a particular resource.&lt;/p&gt;
&lt;p&gt;Getting this right can be complicated, and it can become brittle when web server configuration also affects the response.&lt;/p&gt;
&lt;h2&gt;Enter CDN-Cache-Control&lt;/h2&gt;
&lt;p&gt;CDN-Cache-Control is a recently proposed header specified in
&lt;a href="https://datatracker.ietf.org/doc/html/draft-cdn-control-header-01"&gt;https://datatracker.ietf.org/doc/html/draft-cdn-control-header-01&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The draft RFC states:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;specification&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;defines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;convention&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HTTP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;header&lt;/span&gt;
&lt;span class="nv"&gt;fields&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;directives&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;controlling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;caching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;targeted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;
&lt;span class="nv"&gt;specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;caches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;caches&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;also&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;defines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;such&lt;/span&gt;
&lt;span class="nv"&gt;header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;field&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;targeted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nv"&gt;Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Delivery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Network&lt;/span&gt;]&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;learning&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;cdn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CDN&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;caches&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What does this mean in practice? Imagine a Magento application where pages should be cacheable by a shared cache
but not by a browser. A Cache-Control header could look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: max-age=0, s-max-age=600
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tells browser caches not to reuse the response, while shared caches may cache it for 600 seconds.&lt;/p&gt;
&lt;p&gt;What if the application does not want every shared cache to store the resource, because the application already sends purges
to the cache when content changes?&lt;/p&gt;
&lt;p&gt;Cache-Control does not give you that separation. The header above enables any shared cache to store the resource
for the specified time. With CDN-Cache-Control, the application could instead send:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: max-age=0, s-max-age=60
CDN-Cache-Control: max-age=3600
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tells the browser not to cache the page, allows a shared cache to keep the page for 60s,
and allows the CDN to keep it for one hour.&lt;/p&gt;
&lt;h2&gt;Targeted CDN-Cache-Control&lt;/h2&gt;
&lt;p&gt;What if you need to target a specific CDN only? Use the provider's targeted CDN-Cache-Control header.&lt;/p&gt;
&lt;p&gt;For example, Peakhour-CDN-Cache-Control instructs Peakhour only to cache the resource.
Other CDNs in the request path will not honour this header.&lt;/p&gt;
&lt;h2&gt;Header format&lt;/h2&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;p&gt;The following header fields instruct a CDN cache
to consider the response fresh for 600 seconds, other shared caches
for 120 seconds, and any remaining caches for 60 seconds:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: max-age=60, s-maxage=120
CDN-Cache-Control: max-age=600
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These header fields instruct a CDN cache to consider the
response fresh for 600 seconds, while all other caches are
prevented from storing it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: no-store
CDN-Cache-Control: max-age=600
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because CDN-Cache-Control is not present, this header field
prevent all caches from storing the response:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: no-store
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whereas these prevent all caches except CDN caches from
storing the response:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Cache-Control: no-store
CDN-Cache-Control: none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(note that 'none' is not a registered cache directive; it is used here to
avoid sending a header field with an empty value, because such a
header might not be preserved in all cases).&lt;/p&gt;</content><category term="Learning"></category><category term="Caching"></category><category term="CDN"></category><category term="Rate Limiting"></category><category term="Drupal"></category><category term="Web Performance"></category></entry><entry><title>Cache-Status</title><link href="https://www.peakhour.io/blog/cdn-cache-status-header/" rel="alternate"></link><published>2022-02-25T13:00:00+11:00</published><updated>2022-02-25T13:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2022-02-25:/blog/cdn-cache-status-header/</id><summary type="html">&lt;p&gt;Cache-Status is a proposed standard header to provide visibility into how caching providers interact and handle a request.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Diagnosing &lt;a href="/learning/cdn/"&gt;CDN&lt;/a&gt; caching can be complex, with multiple cache layers, Origin Shielding, and local caching.
Understanding how these layers interact can mean spending weeks working through RFCs for the
finer details of ETag, Cache-Control, and Last-Modified headers - before you even account for advanced controls
that override them.&lt;/p&gt;
&lt;p&gt;When you have a problem, or are trying to optimise caching, the first thing you need is visibility into how a request was handled.&lt;/p&gt;
&lt;p&gt;This is where the new Cache-Status header helps. The new draft RFC, RFC draft-ietf-httpbis-cache-header-08, aims to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;aide&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;debugging&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;standardising&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;various&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;standard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;semantics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;these&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;often&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unclear&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vary&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;implementations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The draft RFC proposes a new header, Cache-Status, with a uniform format for showing how multiple
caching providers interact and handle a request. The Cache-Status header forms a list. Each member of the list
represents a cache that has handled the request, and the last member belongs to the cache that most recently
served the user. The header is only applicable to responses directly generated
by an origin server. Each member can add a parameter indicating how it handled the request.&lt;/p&gt;
&lt;p&gt;A Cache-Status header looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OriginCache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;1100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CDN Company Here&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;uri-miss&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It is formatted and labelled, with each cache in the line separated in list format by a ','.&lt;/p&gt;
&lt;h6&gt;The format of the Cache-Status header is a list comprising of the following possible parameters:&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;hit&lt;/strong&gt;          = boolean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fwd&lt;/strong&gt;          = (bypass, method, uri-miss, vary-miss, request, stale, partial)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fwd-status&lt;/strong&gt;   = integer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ttl&lt;/strong&gt;          = integer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stored&lt;/strong&gt;       = boolean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;collapsed&lt;/strong&gt;    = boolean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;key&lt;/strong&gt;          = string&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;detail&lt;/strong&gt;       = token / string&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What the parameters mean&lt;/h2&gt;
&lt;h3&gt;Hit&lt;/h3&gt;
&lt;p&gt;The hit parameter signifies that a request was satisfied by the cache. It is a boolean parameter meaning
that its presence indicates a cache hit.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Peakhour&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Fwd&lt;/h3&gt;
&lt;p&gt;The Fwd parameter indicates when a response was forwarded to an origin server - and why. The Fwd parameter
contains one of the following arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bypass&lt;/strong&gt; - The cache was configured to not handle this request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;method&lt;/strong&gt; - The request method's semantics require the request to be
   forwarded&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;uri-miss&lt;/strong&gt; - The cache did not contain any responses that matched
   the request URI&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vary-miss&lt;/strong&gt; - The cache contained a response that matched the
   request URI, but could not select a response based upon this
   request's headers and stored Vary headers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;miss&lt;/strong&gt; - The cache did not contain any responses that could be used
   to satisfy this request (to be used when an implementation cannot
   distinguish between uri-miss and vary-miss)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;request&lt;/strong&gt; - The cache was able to select a fresh response for the
   request, but the request's semantics (e.g., Cache-Control request
   directives) did not allow its use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stale&lt;/strong&gt; - The cache was able to select a response for the request,
   but it was stale&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;partial&lt;/strong&gt; - The cache was able to select a partial response for the
   request, but it did not contain all of the requested ranges (or
   the request was for the complete response)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Peakhour&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;uri-miss&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Fwd-status&lt;/h3&gt;
&lt;p&gt;The fwd-status parameter indicates the status code the next hop returned in response to the request. It is only
meaningful when "fwd" is present. For example, a complete miss would look like &lt;em&gt;fwd=uri-miss&lt;/em&gt;
and the HTTP status code of the downstream response would be supplied in the fwd-status:&lt;/p&gt;
&lt;p&gt;The following example shows that the cache did not satisfy the request and that the downstream server
indicated an HTTP 304 response - HTTP Not Modified.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Peakhour&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;uri-miss&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fwd-status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;304&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;ttl&lt;/h3&gt;
&lt;p&gt;Each cache item is associated with a lifetime, referred to as the Time To Live (TTL). A TTL is in seconds
and indicates the remaining  &lt;em&gt;freshness&lt;/em&gt; of the resource. This value is calculated by the cache.&lt;/p&gt;
&lt;p&gt;For example, a cache hit showing that the resource has a &lt;em&gt;freshness&lt;/em&gt; of 376 seconds.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ExampleCache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;376&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another example shows a cache hit with &lt;em&gt;negative freshness&lt;/em&gt;  - a stale resource.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ExampleCache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-412&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;stored&lt;/h3&gt;
&lt;p&gt;Indicates whether the received response was stored by the cache. This example shows a cache miss and the
cache storing the response for the next hit.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ExampleCache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;uri-miss&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;stored&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;collapsed&lt;/h3&gt;
&lt;p&gt;Indicates whether the received response was collapsed with another request.&lt;/p&gt;
&lt;h3&gt;key&lt;/h3&gt;
&lt;p&gt;The key is the lookup index into a cache. Cache keys convey a representation of how the cache will
look up the resource used for the response. The cache key is implementation-specific.&lt;/p&gt;
&lt;p&gt;This example shows a cache hit, the cache key and secondary key, and the remaining TTL of the resource.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;cache-status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;peakhour&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;io&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://example.com/calendar.css&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secondary-key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;encoding::gzip&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;30674859&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;detail&lt;/h3&gt;
&lt;p&gt;Allows additional implementation-specific information not captured by other parameters.&lt;/p&gt;
&lt;h3&gt;Multiple layers of caching&lt;/h3&gt;
&lt;p&gt;The header allows multiple layers of caching. For example, a global CDN may sit in front of a local varnish server.
Each cache appends its cache-status to the header, so the last item is the closest cache
to the actual application and the first item is the closest cache to the accessing user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;Cache-Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OriginCache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;1100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CDN Company Here&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;545&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Security&lt;/h2&gt;
&lt;p&gt;There are security implications to consider with this header. Making it public gives an attacker insight
into how the cache is configured, which may help them bypass a cache entirely and access an origin server directly.
Cache header security is provider-specific, but could involve enabling it only when required, restricting access to specific
IP addresses, or requiring a custom request header to trigger the cache-status header.&lt;/p&gt;
&lt;p&gt;Peakhour.IO regularly sees automated scans sending other providers' headers to trigger a cache-status header response.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Cache-Status header gives clear insight into how cache layers interact
on a website. It provides a standardised format for showing how a cache handled a request and
allows multiple caches to report their part of the request path.&lt;/p&gt;
&lt;p&gt;Peakhour fully supports this new header, and it can be enabled/disabled and secured in the dashboard.&lt;/p&gt;</content><category term="Learning"></category><category term="Caching"></category><category term="CDN"></category><category term="Drupal"></category><category term="HTTP"></category><category term="Rate Limiting"></category><category term="Web Performance"></category></entry><entry><title>What is the Chrome UX Report (CrUX), and why should you care?</title><link href="https://www.peakhour.io/blog/what-is-the-chrome-ux-report-crux/" rel="alternate"></link><published>2021-02-26T13:00:00+11:00</published><updated>2026-07-29T00:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2021-02-26:/blog/what-is-the-chrome-ux-report-crux/</id><summary type="html">&lt;p&gt;Learn what the Chrome UX Report is, how CrUX field data feeds Core Web Vitals reporting, and how to use it alongside lab tools.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A slow page can lose a customer before the application gets a chance to do its job. Google also uses Core Web Vitals
within its page-experience systems. That makes performance a ranking input, not a promise that one faster page will outrank another
or that a Lighthouse score will reduce advertising costs.&lt;/p&gt;
&lt;p&gt;We've previously written about &lt;a href="/blog/web-vitals/"&gt;Google's Web Vitals&lt;/a&gt;. The practical question is where the real-world data comes from. It is not gathered by Googlebot. Google uses field data from the Chrome User Experience Report, usually shortened to CrUX, to show how eligible Chrome users actually experienced a page or origin.&lt;/p&gt;
&lt;p&gt;That matters because a fast lab score is not the same thing as a fast customer experience. &lt;a href="/blog/testing-website-speed-webpagetest/"&gt;WebPageTest&lt;/a&gt; and Lighthouse help diagnose a controlled test run. CrUX shows the field data behind PageSpeed Insights, Search Console's Core Web Vitals report, and Peakhour's &lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Introducing the Chrome UX Report (CRuX)&lt;/h2&gt;
&lt;p&gt;The CrUX report is a public data set of real-user measurements (RUM) of &lt;a href="/blog/testing-sitespeed-lighthouse/"&gt;website performance&lt;/a&gt; across millions of sites. The report has been around since 2017 and is updated regularly, but the value is still often missed: it shows what real users experienced, not what a synthetic test predicted.&lt;/p&gt;
&lt;p&gt;The data is collected from real Chrome browser users who have opted in to send browsing information back to Google.
This opt-in requires that the user has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Opted in to syncing browser history&lt;/li&gt;
&lt;li&gt;Not set up a sync passphrase&lt;/li&gt;
&lt;li&gt;Usage statistic reporting enabled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite these conditions, millions of Chrome users still report statistics back to Google. A given website still needs
to be fairly busy before there are useful statistics in the report.&lt;/p&gt;
&lt;h3&gt;Gathered Metrics&lt;/h3&gt;
&lt;p&gt;The current Core Web Vitals are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;: how quickly the main content appears.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interaction to Next Paint (INP)&lt;/strong&gt;: how responsive the page is to real user interactions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cumulative Layout Shift (CLS)&lt;/strong&gt;: how visually stable the page is while it loads.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CrUX also exposes supporting performance metrics and dimensions that help explain those headline scores. Older metrics such as First Input Delay (FID) still appear in older reports and tools, but INP is now the responsiveness metric to watch.&lt;/p&gt;
&lt;h3&gt;Dimensions&lt;/h3&gt;
&lt;p&gt;Because performance can vary widely, the metrics are divided into the following dimensions to help segment and understand the user experience.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Country&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device Type&lt;/strong&gt;: Tablet, Phone, Desktop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Speed&lt;/strong&gt;: slow 2g, 2g, 3g, 4g, or offline&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Viewing data in the report&lt;/h2&gt;
&lt;p&gt;There are several ways to see how &lt;a href="/learning/performance/how-to-pass-core-web-vitals/"&gt;your website&lt;/a&gt; performs in the report. These include:&lt;/p&gt;
&lt;h4&gt;Pagespeed insights&lt;/h4&gt;
&lt;p&gt;Google's website analysis tool provides summary CRuX data for the analysed URL and, if data is available, for the entire site.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/page-speed-insights-field-data.jpg" alt="&lt;a href="/solutions/use-case/improve-web-vitals/"&gt;Page Speed&lt;/a&gt; Insights Field Data" style="max-width: 100%;margin-bottom: 20px"/&gt;&lt;/p&gt;
&lt;h4&gt;Google BigQuery&lt;/h4&gt;
&lt;p&gt;The most flexible option is to access it directly via &lt;a href="https://console.cloud.google.com/bigquery?project=chrome-ux-report"&gt;BigQuery&lt;/a&gt;.
You query it with SQL (database query language).&lt;/p&gt;
&lt;p&gt;The downside is that you need to understand SQL and have a Google account.&lt;/p&gt;
&lt;h4&gt;Google's Search Console (formerly Webmaster Tools)&lt;/h4&gt;
&lt;p&gt;The search console now has a section 'Core Web Vitals' that shows whether URLs pass the Core Web Vitals,
as well as a historical graph of performance for both mobile and desktop.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/search-console.jpg" alt="Google Search Console Web Vitals" style="max-width: 100%;margin-bottom: 20px"/&gt;&lt;/p&gt;
&lt;h4&gt;Looker Studio&lt;/h4&gt;
&lt;p&gt;Looker Studio, formerly Google Data Studio, can be used to build dashboards on top of CrUX data and other sources. It lets you visualise the performance of your website, or a competitor's website, over time.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/data-studio.jpg" alt="Google data studio" style="max-width: 100%;margin-bottom: 20px"/&gt;&lt;/p&gt;
&lt;h4&gt;Third party tools&lt;/h4&gt;
&lt;p&gt;Like our own &lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt;. It uses the
&lt;a href="https://developers.google.com/web/tools/chrome-user-experience-report/api/reference"&gt;Chrome UX API&lt;/a&gt; to retrieve the
information.&lt;/p&gt;
&lt;h2&gt;CrUX vs lab tools vs RUM&lt;/h2&gt;
&lt;p&gt;CrUX is field data. It is useful because it reflects real Chrome users, but it only reports where there is enough eligible traffic. It can also be slower to reveal the cause of a problem because it is aggregated.&lt;/p&gt;
&lt;p&gt;Lab tools such as &lt;a href="/blog/testing-website-speed-webpagetest/"&gt;WebPageTest&lt;/a&gt; are better for diagnosis. They show waterfalls, redirects, blocked resources, caching issues, image weight, and third-party requests. Your own real user monitoring can go further again, because it can include business context that CrUX does not know: customer type, template, campaign, cache state, bot pressure, origin load, and release timing.&lt;/p&gt;
&lt;h2&gt;Put field data beside the release&lt;/h2&gt;
&lt;p&gt;The data in the Chrome UX Report is one of the clearest public views of how Google sees the performance of your website. It is also a free source of real-world user measurements that helps you understand how visitors experience your pages.&lt;/p&gt;
&lt;p&gt;Use CrUX to see whether your site is passing Core Web Vitals, use lab tools to find the technical cause, and use traffic visibility to understand whether bots, crawlers, bursts, cache misses, or expensive requests are putting the experience under pressure. If the first question is "how do we compare?", start with the &lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt;. If the question is "what is slowing us down?", start with &lt;a href="/solutions/use-case/traffic-control/"&gt;Traffic Management&lt;/a&gt;.&lt;/p&gt;</content><category term="Learning"></category><category term="Web Performance"></category><category term="SEO"></category><category term="Core Web Vitals"></category><category term="Analytics"></category><category term="Browser Fingerprinting"></category><category term="Magento"></category></entry><entry><title>The CDN is Dead, Long Live the CDN!</title><link href="https://www.peakhour.io/blog/cache-partitioning-firefox-chrome/" rel="alternate"></link><published>2021-02-17T13:00:00+11:00</published><updated>2021-02-17T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2021-02-17:/blog/cache-partitioning-firefox-chrome/</id><summary type="html">&lt;p&gt;Firefox and Chrome are introducing cache partitioning to improve your privacy, from everyone except Google.&lt;/p&gt;</summary><content type="html">&lt;p&gt;It has long been conventional wisdom that if your website uses a third-party library, such as jQuery or Bootstrap,
you should load it from the library's high-performance &lt;a href="/learning/cdn/"&gt;CDN&lt;/a&gt;. For example, jQuery has&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;code.jquery.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;from which you can include any version of jQuery on your website, e.g.:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://code.jquery.com/jquery-3.3.1.min.js&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;anonymous&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This was meant to give you two performance benefits: the browser could load jQuery from a global CDN, &lt;strong&gt;AND&lt;/strong&gt; if another site
the visitor had already opened included the same jQuery file in the same way, the file might already be in their
browser cache. In that case the browser could reuse the cached copy and the page would load faster.&lt;/p&gt;
&lt;p&gt;In practice, website visitors rarely see much benefit. The browser still has to open a separate connection
to the third-party CDN, which can cancel out any latency advantage the CDN provides. There are also so many versions
of major libraries in use that the chance of two websites using the same version,
AND both loading it from the CDN in the same way, is small.&lt;/p&gt;
&lt;h2&gt;Cache Partitioning in Chrome and Firefox&lt;/h2&gt;
&lt;p&gt;Until recently, Chrome and Firefox used a shared browser cache for all websites that a user visited. This means that if
you visited a website and it loaded this resource:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://www.somesite.com/foo.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and you then visited a second website that included the same resource, the resource would be loaded from the
shared cache rather than being downloaded from the internet a second time. Cookies set by these resources would also be shared.&lt;/p&gt;
&lt;p&gt;As of Firefox v85 and Chrome v86, the browser cache will be partitioned. This means that the same resource included
on two sites will have to be downloaded from the internet twice and stored separately.&lt;/p&gt;
&lt;h2&gt;Why are they doing this?&lt;/h2&gt;
&lt;p&gt;The main reason is privacy. Shared browser caches have been used by unscrupulous operators
to track users across different websites without consent. They do this by utilising cache
side-channel attacks.&lt;/p&gt;
&lt;h2&gt;What Are Side Channel Attacks?&lt;/h2&gt;
&lt;p&gt;In computer systems, a program or algorithm can be correct and secure in itself, while its interaction with the computer still leaves
information that an attacker can exploit. A useful analogy from the spy world is using a laser beam to measure the vibrations
on a pane of glass, so you can hear a conversation inside a room that you otherwise could not hear.&lt;/p&gt;
&lt;p&gt;Side-channel attacks can be ingenious, utilising techniques such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;observing power consumption/electromagnetic radiation;&lt;/li&gt;
&lt;li&gt;timing data moving in and out of memory;&lt;/li&gt;
&lt;li&gt;timing how long a CPU takes to execute an instruction;&lt;/li&gt;
&lt;li&gt;measuring sounds emitted by a hard drive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the browser cache, a simple attack starts with the user opening a malicious website. That website
then requests resources (e.g., an image) from another site. By timing how long the browser takes to load that image,
it can determine whether it was fetched from the browser cache or had to be downloaded
over the internet. According to Google, this technique can be used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detect if a user has visited a specific site.&lt;/li&gt;
&lt;li&gt;Detect if an arbitrary string is in the user's search results by checking for 'no search result' images used by particular
  sites.&lt;/li&gt;
&lt;li&gt;Track users across sites using the cache.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GitHub user terjanq has published a &lt;a href="https://terjanq.github.io/Bug-Bounty/Google/cache-attack-06jd2d2mz2r0/index.html_"&gt;cache side channel attack&lt;/a&gt;
that can gather all sorts of information from Google services. He states that a regular Google user could have the following
information leaked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;search history&lt;/li&gt;
&lt;li&gt;videos watched&lt;/li&gt;
&lt;li&gt;the exact URLs visited&lt;/li&gt;
&lt;li&gt;time frames of the activities&lt;/li&gt;
&lt;li&gt;private book collection&lt;/li&gt;
&lt;li&gt;books read / purchased / bookmarked / favourite / etc.&lt;/li&gt;
&lt;li&gt;private emails&lt;/li&gt;
&lt;li&gt;tokens / credit card numbers / phone numbers / etc.&lt;/li&gt;
&lt;li&gt;contacts (including email addresses, names, phone numbers)&lt;/li&gt;
&lt;li&gt;bookmarked websites&lt;/li&gt;
&lt;li&gt;and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perhaps it was simpler to implement cache partitioning in Chrome than to keep patching these vulnerabilities...&lt;/p&gt;
&lt;h2&gt;What you should do&lt;/h2&gt;
&lt;p&gt;Google estimates that the changes in browser caching will have minimal impact, as little as a 0.3% difference in
FCP (First Contentful Paint). However, since there will no longer be any shared-cache benefit from hosting on a third-party CDN, you should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Host all third-party libraries on your own domain&lt;/strong&gt; to eliminate the need for the browser to establish a second connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a delivery layer such as Peakhour Edge,&lt;/strong&gt; which transparently caches and serves your website assets close to users.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Some third-party resources really are shared across millions of websites. Google Fonts is the obvious example.
Under the new cache partitioning implementations, these fonts will have to be downloaded for every site
that uses them. That has a cost for site speed and data usage. It also has the side effect of
&lt;strong&gt;improving&lt;/strong&gt; Google's ability to track users, because each font request tells Google another site the user has visited.&lt;/p&gt;
&lt;p&gt;Safari has been partitioning the HTTP cache since 2013, leaving Microsoft's Edge as the last major browser with global HTTP caches.
However, future versions will be based on Chromium (the open-source version of Chrome) so should get cache partitioning by default.&lt;/p&gt;</content><category term="Learning"></category><category term="CDN"></category><category term="Caching"></category><category term="Drupal"></category><category term="Web Performance"></category></entry><entry><title>Is your website obese?</title><link href="https://www.peakhour.io/blog/website-obesity-reducing-page-weight/" rel="alternate"></link><published>2020-03-19T13:00:00+11:00</published><updated>2020-03-19T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2020-03-19:/blog/website-obesity-reducing-page-weight/</id><summary type="html">&lt;p&gt;An introduction to page weight, what it is and how you can reduce it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/learning/page-weight/"&gt;Page weight&lt;/a&gt; has increased steadily since the early web, as connection speeds improved and sites became more interactive
and visually rich. The HTTP Archive shows over a &lt;strong&gt;300% increase&lt;/strong&gt; over the last 10 years.&lt;/p&gt;
&lt;div style="width: 100%;text-align: center"&gt;
    &lt;div style="display: inline-block; padding: 20px"&gt;
        &lt;strong&gt;MEDIAN DESKTOP&lt;/strong&gt;&lt;br/&gt;
        1957.8 KB&lt;br/&gt;
        ▲318.6%
    &lt;/div&gt;
    &lt;div style="display: inline-block; padding: 20px"&gt;
        &lt;strong&gt;MEDIAN MOBILE&lt;/strong&gt;&lt;br/&gt;
        1791.9 KB&lt;br/&gt;
        ▲1137.5%
    &lt;/div&gt;
    &lt;div markdown="1"&gt;
        &lt;img src="/static/images/blog/page-weight.jpg" alt="Page weight historical"/&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Why should I care?&lt;/h2&gt;
&lt;h3&gt;Smaller page weight = Faster page load&lt;/h3&gt;
&lt;p&gt;Page weight directly affects page load times. The smaller the page, the faster your site can load. The slower the connection, the more
obvious the effect. Faster page loads are associated with higher conversion rates, fewer bounces, and higher engagement with your
site.&lt;/p&gt;
&lt;h3&gt;Smaller page weight = Less bandwidth = Savings&lt;/h3&gt;
&lt;p&gt;Your hosting provider or CDN may charge for data transfer. Reducing page size can reduce those costs.&lt;/p&gt;
&lt;h2&gt;How big is too big?&lt;/h2&gt;
&lt;p&gt;Google says that, to meet its best practice guidelines, you should aim for a page weight of 500 KB on mobile devices and a request count of
fewer than 50.&lt;/p&gt;
&lt;h2&gt;What can I do to shrink my page weight?&lt;/h2&gt;
&lt;p&gt;Start with the bytes that browsers have to download, parse, and render.&lt;/p&gt;
&lt;h3&gt;1. Optimise Images&lt;/h3&gt;
&lt;p&gt;Images are often the biggest contributors to page weight. Careful optimisation can reduce their size without a noticeable loss in quality.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compression:&lt;/strong&gt; Use image compression tools to reduce file sizes. For JPEGs, a quality setting of 75-85% is often a practical balance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Next-Gen Formats:&lt;/strong&gt; Formats like WebP and AVIF compress better than older formats like JPEG and PNG. Modern browsers have wide support for them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive Images:&lt;/strong&gt; Use the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element or &lt;code&gt;srcset&lt;/code&gt; attribute to serve different image sizes for different screen resolutions. There is no reason to send a large desktop image to a mobile phone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lazy Loading:&lt;/strong&gt; Load images only when they are about to enter the viewport. This does not reduce total page weight, but it improves the initial load time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Minify CSS, JavaScript, and HTML&lt;/h3&gt;
&lt;p&gt;Minification removes unnecessary characters from source code without changing its functionality. That includes white space, comments, and line breaks. Many build tools, including Webpack, and CMS plugins can automate this process.&lt;/p&gt;
&lt;h3&gt;3. Enable Server-Side Compression&lt;/h3&gt;
&lt;p&gt;Enabling compression like Gzip or Brotli on your web server can reduce the size of the HTML, CSS, and JavaScript files sent to the browser. Brotli offers better compression than Gzip and is supported by all modern browsers.&lt;/p&gt;
&lt;h3&gt;4. Leverage Browser Caching&lt;/h3&gt;
&lt;p&gt;Set appropriate &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;Expires&lt;/code&gt; headers so the browser can store files locally. On later visits, the user's browser can load those files from its local cache instead of downloading them again, making return visits much faster.&lt;/p&gt;
&lt;h3&gt;5. Remove Unused Code&lt;/h3&gt;
&lt;p&gt;Many websites, especially those built on complex themes or frameworks, load large CSS and JavaScript files with code that is never used. Use browser developer tools, such as the Coverage tab in Chrome DevTools, to identify and remove unused code. For JavaScript builds, this process is often called "tree-shaking," and it can significantly shrink your file sizes.&lt;/p&gt;
&lt;h3&gt;6. Use a Content Delivery Network (CDN)&lt;/h3&gt;
&lt;p&gt;A CDN stores copies of your assets on servers around the world. When a user visits your site, those assets are served from the server closest to them, reducing latency. Many CDNs also provide automatic optimisation features such as image compression and minification.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Reducing page weight is ongoing work. A lighter, faster website gives users less to wait for, improves search engine rankings and conversion rates, and reduces the amount of data you have to serve. Start with images, compression, caching, and unused code; those changes usually give you the clearest wins.&lt;/p&gt;</content><category term="Learning"></category><category term="Web Performance"></category><category term="Core Web Vitals"></category><category term="Caching"></category></entry></feed>