<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Peakhour.IO - Drupal</title><link href="https://www.peakhour.io/" rel="alternate"></link><link href="https://www.peakhour.io/feeds/tag/drupal.atom.xml" rel="self"></link><id>https://www.peakhour.io/</id><updated>2026-07-06T13:00:00+10:00</updated><entry><title>HTTP Security Headers</title><link href="https://www.peakhour.io/blog/http-security-headers-web-application-protection/" rel="alternate"></link><published>2023-11-28T14:00:00+11:00</published><updated>2023-11-28T14:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2023-11-28:/blog/http-security-headers-web-application-protection/</id><summary type="html">&lt;p&gt;Comprehensive guide to HTTP security headers for protecting web applications from client-side attacks. Learn essential browser security configurations for modern application security platforms and DevSecOps workflows.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Traditionally, web security has focused on the server side: protecting the application itself from attack. That work is
necessary, but it often leaves the client side under-specified. Client-side attacks move the exposure point into the
user's browser, where the business impact can be serious.&lt;/p&gt;
&lt;p&gt;Magecart attacks are a clear example. Attackers inject skimming scripts into websites to steal sensitive customer
information, such as credit card details, directly from the user's browser. Session hijacking and Cross-Site Scripting
(XSS) attacks also exploit browser vulnerabilities, leading to unauthorised access and data breaches. These attacks
don't just risk user data; they can erode trust, damage reputations, and result in significant financial and legal
repercussions for businesses.&lt;/p&gt;
&lt;p&gt;HTTP security headers are practical controls for these types of attacks. Properly implemented, they instruct browsers
on how to handle website content and interactions safely.&lt;/p&gt;
&lt;h2&gt;Key HTTP Security Headers&lt;/h2&gt;
&lt;h3&gt;Content-Security-Policy (CSP)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: CSP prevents Cross-Site Scripting (XSS) attacks by specifying which sources browsers should allow when
loading scripts, images, and other resources. It can also prevent MageCart-style attacks by restricting the host names
that an injected script can communicate with.&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;Content-Security-Policy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script-src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;self&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="nt"&gt;apis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;com&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This example allows scripts to load only from the site's own domain ('self') and https://apis.google.com.&lt;/p&gt;
&lt;h3&gt;X-Frame-Options&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: This header protects against clickjacking attacks by controlling whether a browser allows a page to
be rendered in a &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;X-Frame-Options: DENY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This setting prevents any domain from framing the content. Another option is &lt;code&gt;SAMEORIGIN&lt;/code&gt;, which only allows framing by
the same site.&lt;/p&gt;
&lt;h3&gt;X-Content-Type-Options&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: This header prevents MIME-sniffing, where a browser might incorrectly interpret the content type of a
resource, leading to security vulnerabilities.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;X-Content-Type-Options: nosniff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This instructs the browser to follow the content type declared in the HTTP headers.&lt;/p&gt;
&lt;h3&gt;X-XSS-Protection&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: This enables the browser's inbuilt XSS protection features. However, this header is largely deprecated in
favour of CSP.&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;X-XSS-Protection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;block&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This configuration enables the protection and tells the browser to block the page if an XSS attack is detected.&lt;/p&gt;
&lt;h3&gt;Strict-Transport-Security (HSTS)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: HSTS forces the browser to use HTTPS over HTTP, ensuring encrypted communication and protecting against
man-in-the-middle attacks. Alternatively, you can automatically redirect all requests to HTTPS on your web server or at
your EDGE provider. For example, Peakhour allows you to set up EDGE redirects to force all traffic to HTTPS.&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;Strict-Transport-Security&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max-age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;31536000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;includeSubDomains&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This example tells the browser to use HTTPS for all subdomains for one year.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Implementing the correct HTTP security headers is a straightforward way to improve web application security. These
headers form part of the first line of defence against many common security vulnerabilities. As threats evolve, keeping
security headers current and properly configured helps safeguard your users and your brand.&lt;/p&gt;</content><category term="Security"></category><category term="Application Security"></category><category term="Account Protection"></category><category term="API Security"></category><category term="Credential Stuffing"></category><category term="Drupal"></category><category term="DDoS"></category></entry><entry><title>Enterprise-Level Caching for All</title><link href="https://www.peakhour.io/blog/magento-2-plugin/" rel="alternate"></link><published>2023-11-02T13:00:00+11:00</published><updated>2023-11-02T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-11-02:/blog/magento-2-plugin/</id><summary type="html">&lt;p&gt;Elevate your e-commerce with our newly released Magento 2 plugin. Experience enterprise-level caching features accessible to all Peakhour customers.&lt;/p&gt;</summary><content type="html">&lt;p&gt;We've released our Magento 2 plugin for e-commerce stores using Magento. It brings Peakhour's caching features into
Magento, including capabilities that other providers often reserve for enterprise plans. With Peakhour,
'Enterprise for Everyone' means making those features available to all customers, regardless of plan.&lt;/p&gt;
&lt;h2&gt;Why Cache Tags Matter&lt;/h2&gt;
&lt;p&gt;&lt;a href="/learning/cache-tags/"&gt;Cache tags&lt;/a&gt; solve a practical website management problem: keeping your cache current when content changes.
In Magento 2, a single change, such as updating a product's price, can affect multiple pages. Cache tags ensure that only
the relevant cached content is updated, maintaining cache efficiency and reducing server load. That matters for
website speed and user experience, which directly affect sales and SEO rankings.&lt;/p&gt;
&lt;h2&gt;Enterprise for Everyone&lt;/h2&gt;
&lt;p&gt;While other providers offer cache tags only in expensive enterprise plans, Peakhour makes this feature available
to everyone. Our infrastructure and caching algorithms make that possible. We also offer other
enterprise-level features, including DDoS protection, real-time analytics, and custom caching rules, so
'Enterprise for Everyone' is reflected in the product rather than just the plan names.&lt;/p&gt;
&lt;h2&gt;Peakhour vs. Magento and Varnish Caching&lt;/h2&gt;
&lt;p&gt;Our plugin goes beyond Magento's built-in caching and Varnish cache in several ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom Cache Tags&lt;/strong&gt;: Unlike Magento's built-in cache, we offer custom cache tags for more granular cache control.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advanced Algorithms&lt;/strong&gt;: Our caching algorithms go beyond Varnish, helping improve cache hit rates and lower server load.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Additional Features&lt;/strong&gt;: With Peakhour, caching sits alongside real-time analytics, DDoS protection, and custom caching rules, features often missing in standard Magento or Varnish setups.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Expected Performance Improvements&lt;/h2&gt;
&lt;p&gt;By using Peakhour's Magento 2 plugin, you can expect performance improvements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faster Page Loads&lt;/strong&gt;: Our caching can reduce page load times by up to 50%, giving users a smoother experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced Server Load&lt;/strong&gt;: Efficient caching means fewer requests to your origin server, reducing server load by as much as 70%.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved SEO&lt;/strong&gt;: Faster websites are favoured by search engines, which can improve SEO rankings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Higher Conversion Rates&lt;/strong&gt;: A faster website gives users a better experience, which can lead to higher conversion rates and increased sales.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Installation Options&lt;/h2&gt;
&lt;p&gt;You can install the Magento 2 plugin through Magento Connect, Composer, or a ZIP file. For
more detail, see our &lt;a href="/docs/how-to-guides/integrations/magento-2/"&gt;plugin page&lt;/a&gt;.&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="Magento"></category><category term="CDN"></category><category term="Drupal"></category><category term="WordPress"></category><category term="Web Performance"></category></entry><entry><title>Useful tips to accelerate your Magento store</title><link href="https://www.peakhour.io/blog/accelerate-magento/" rel="alternate"></link><published>2023-11-01T13:00:00+11:00</published><updated>2023-11-01T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-11-01:/blog/accelerate-magento/</id><summary type="html">&lt;p&gt;There are many things you can do to speed up your Magento store, here are just a few.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Out of the box, Magento is not the fastest ecommerce platform. Magento 2 is built with Full Page Cache in mind, so repeat page requests do not always have to hit the application. A slow Magento store can frustrate customers, increase bounce rates, and cost sales. There are several practical ways to accelerate &lt;a href="/learning/ecommerce-security/securing-magento-shopify/"&gt;your Magento&lt;/a&gt; store and improve the user experience. These are good places to start.&lt;/p&gt;
&lt;h2&gt;Caching is King&lt;/h2&gt;
&lt;p&gt;Caching is usually the biggest performance lever for a Magento store. By storing pre-generated versions of pages, you reduce server response times and avoid asking Magento to rebuild the same page for every request.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Full Page Caching (FPC)&lt;/strong&gt;: Magento includes built-in FPC, but it can be extended. Varnish is a common choice for this role. Magento 2 has native support for Varnish, which acts as a web application accelerator.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt;: Use Redis for session and cache storage. It is an in-memory data structure store that can speed up backend operations by reducing database load.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edge Caching&lt;/strong&gt;: Use Peakhour Edge to cache your dynamic pages close to users. This serves content from a nearby delivery path and reduces latency. Peakhour's &lt;a href="/docs/how-to-guides/integrations/magento-1/"&gt;Magento 1&lt;/a&gt; and &lt;a href="/docs/how-to-guides/integrations/magento-2/"&gt;Magento 2&lt;/a&gt; plugins make this straightforward to set up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Optimise Your Images&lt;/h2&gt;
&lt;p&gt;Images often make up the bulk of a page's weight. Optimising them is one of the simplest ways to improve load times.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compression&lt;/strong&gt;: Use image compression tools to reduce file sizes without a noticeable loss in quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next-Gen Formats&lt;/strong&gt;: Serve images in modern formats like WebP or AVIF, which offer better compression. A CDN can often handle this conversion automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lazy Loading&lt;/strong&gt;: Implement lazy loading for images that are "below the fold" (not immediately visible). This means they only load when they are about to enter the user's viewport.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Minify and Merge CSS/JavaScript&lt;/h2&gt;
&lt;p&gt;Magento has built-in features for merging and minifying CSS and JavaScript files.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minification&lt;/strong&gt;: Removes unnecessary characters (like whitespace and comments) from code to reduce file size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merging&lt;/strong&gt;: Combines multiple CSS or JavaScript files into a single file to reduce the number of HTTP requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Always test thoroughly after enabling merging, as it can sometimes cause issues with certain themes or extensions.&lt;/p&gt;
&lt;h2&gt;Keep Your Environment Updated&lt;/h2&gt;
&lt;p&gt;The environment your Magento store runs in has a direct effect on performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Latest PHP Version&lt;/strong&gt;: Use the latest stable version of PHP supported by your Magento version. Each new release brings performance and security improvements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Resources&lt;/strong&gt;: Ensure your server has adequate RAM and CPU power to handle your traffic, especially during peak times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Server&lt;/strong&gt;: Use a high-performance web server like Nginx, which is known for its speed and efficiency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Use Edge Caching and Delivery&lt;/h2&gt;
&lt;p&gt;An edge delivery layer is a practical requirement for many ecommerce stores. It caches your static assets (images, CSS, JavaScript) close to users.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduced Latency&lt;/strong&gt;: Users receive content from the server geographically closest to them, which speeds up load times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced Origin Load&lt;/strong&gt;: By serving cached content, an edge cache reduces the number of requests that hit your origin server, improving its performance and stability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Peakhour also offers security features like a Web Application Firewall (WAF) and DDoS protection.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Database Optimisation&lt;/h2&gt;
&lt;p&gt;A slow database can slow the whole store.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clean Logs&lt;/strong&gt;: Regularly clean out Magento's log tables (e.g., &lt;code&gt;log_customer&lt;/code&gt;, &lt;code&gt;log_visitor&lt;/code&gt;). These can grow very large and slow down database queries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable Flat Catalog&lt;/strong&gt;: For Magento 1 and older versions of Magento 2, enabling the Flat Catalog for products and categories can improve performance by reducing the complexity of database queries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-index Regularly&lt;/strong&gt;: Keep your Magento indexes up to date. A cron job should be set up to handle this automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Audit Third-Party Extensions&lt;/h2&gt;
&lt;p&gt;Poorly coded or unnecessary third-party extensions are a common cause of Magento performance issues.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Review Extensions&lt;/strong&gt;: Audit your installed extensions regularly. If you're not using one, disable or uninstall it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a Profiler&lt;/strong&gt;: Use Magento's built-in profiler or a tool like New Relic to identify slow-running code, which can often be traced back to a specific extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These changes will not fix every Magento performance problem, but they cover the areas that usually matter first: cache behaviour, asset weight, the hosting environment, database maintenance, and extension overhead.&lt;/p&gt;</content><category term="CMS"></category><category term="Magento"></category><category term="Web Performance"></category><category term="Drupal"></category><category term="WordPress"></category><category term="Caching"></category><category term="CDN"></category></entry><entry><title>Vary Cache on Cookie Value</title><link href="https://www.peakhour.io/blog/vary-cache-on-cookie-value/" rel="alternate"></link><published>2023-08-03T13:00:00+10:00</published><updated>2023-08-03T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-08-03:/blog/vary-cache-on-cookie-value/</id><summary type="html">&lt;p&gt;Varying the cache on a specific cookie value is a powerful way to cache personalised content. Many CDNs consider this an enterprise feature, but it's essential for modern dynamic websites.&lt;/p&gt;</summary><content type="html">&lt;p&gt;On most websites, a user requests a page and receives the same response as everyone else. Some websites, however, change
the content depending on who is visiting. For example, someone visiting from Australia might get the page in English,
while someone visiting from Spain gets it in Spanish.&lt;/p&gt;
&lt;h2&gt;The Vary Header&lt;/h2&gt;
&lt;p&gt;The HTTP &lt;code&gt;Vary&lt;/code&gt; header is the standard way to tell a cache that the content of a page can change depending on a request
header. For example, if a website returns this header:&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;Vary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It tells any cache that the content can change depending on the language preference of the user's browser. The cache then
stores a separate copy of the page for each language.&lt;/p&gt;
&lt;p&gt;That works for standard request headers, but content often changes based on something else. For example, a user might be
able to select their currency on an ecommerce store. This preference is usually stored in a cookie.&lt;/p&gt;
&lt;p&gt;A common, but problematic, way of handling this is to return:&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;Vary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cookie&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tells the cache to store a separate version of the page for every unique &lt;code&gt;Cookie&lt;/code&gt; header it sees. The problem is that
the &lt;code&gt;Cookie&lt;/code&gt; header can contain many different cookies (e.g., for analytics, session tracking, etc.), creating many cached
versions and a very low cache hit rate. This effectively makes caching useless.&lt;/p&gt;
&lt;h2&gt;Varying on a Specific Cookie Value&lt;/h2&gt;
&lt;p&gt;A better solution is to vary the cache based on the value of a &lt;em&gt;specific&lt;/em&gt; cookie. For example, an ecommerce store might use
a cookie named &lt;code&gt;currency&lt;/code&gt; to store the user's preference. By instructing the CDN to look only at the value of the &lt;code&gt;currency&lt;/code&gt;
cookie, it can store separate cached versions for AUD, USD, EUR, etc., while ignoring all other cookies.&lt;/p&gt;
&lt;p&gt;This means you can serve personalised, dynamic content while still benefiting from a high cache hit rate.&lt;/p&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;Varying the cache on a cookie value is useful for dynamic websites:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multi-Currency/Multi-Lingual Stores&lt;/strong&gt;: Serve cached pages with the correct currency and language for each user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Groups&lt;/strong&gt;: Show different content or pricing to different user groups, like wholesale vs. retail customers, without them needing to log in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A/B Testing&lt;/strong&gt;: Serve different versions of a page to different users as part of an A/B test and cache both versions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Personalisation&lt;/strong&gt;: Cache pages with personalised content, like recently viewed items or location-based offers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Enterprise or Essential?&lt;/h2&gt;
&lt;p&gt;Many major CDN providers restrict this feature to their expensive enterprise plans, putting it out of reach for many businesses.
At Peakhour, we see it as an essential feature for modern dynamic websites. That's why we make it available on all our plans.
It's a core part of our 'Enterprise for Everyone' philosophy.&lt;/p&gt;
&lt;p&gt;The ability to vary the cache on a specific cookie value addresses the limitations of the &lt;code&gt;Vary: Cookie&lt;/code&gt; header and allows
for efficient caching of personalised content. It is not a luxury feature; it is an essential tool for improving performance
and user experience on modern websites.&lt;/p&gt;</content><category term="Features"></category><category term="Caching"></category><category term="CDN"></category><category term="Drupal"></category></entry><entry><title>Cache Tags/Surrogate Keys</title><link href="https://www.peakhour.io/blog/surrogate-keys-cache-tags/" rel="alternate"></link><published>2023-06-28T13:00:00+10:00</published><updated>2023-06-28T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-06-28:/blog/surrogate-keys-cache-tags/</id><summary type="html">&lt;p&gt;Surrogate Keys, or cache tags, are a powerful mechanism for targeted flushing of content from a cache, not all CDNs support them though.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Caches are a way of storing information so future requests for the same data can be served more quickly. CDNs, including
Peakhour, run caches on each of their POPs (Points of Presence). At that level these caches are key/value stores, where
the key can be a combination of several request details, as outlined in our previous blog post on
&lt;a href="/blog/cdn-cache-keys/"&gt;cache keys&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An established way to improve website performance is &lt;a href="/blog/caching-dynamic-content-with-a-cdn/"&gt;full page caching&lt;/a&gt;,
where a copy of a full page generated by a CMS is stored in a CDN. For a cache hit compared with a cache miss, this can
typically cut 1-4s off a page load.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/savvy-before.jpg" width="100%" alt="Savvysupporter before"/&gt;
&lt;em&gt;Main document load &lt;strong&gt;before&lt;/strong&gt; caching: &lt;strong&gt;2.07s&lt;/strong&gt;&lt;/em&gt;
&lt;/div&gt;

&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/savvy-after.jpg" width="100%" alt="Savvysupporter after"/&gt;
&lt;em&gt;Main document load &lt;strong&gt;after&lt;/strong&gt; caching: &lt;strong&gt;82ms!!&lt;/strong&gt;&lt;/em&gt;
&lt;/div&gt;

&lt;h2&gt;A Simple Cache Example&lt;/h2&gt;
&lt;p&gt;When a website changes a resource, such as a page or image, it can instruct the CDN to flush the cache entry for that
resource's key. For example, say we have a page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/about-us/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we're doing full page caching, it will be stored in the CDN with the key &lt;strong&gt;"/about-us/"&lt;/strong&gt;. If it changes, we can
issue a flush using the key &lt;strong&gt;"/about-us/"&lt;/strong&gt; and the CDN will fetch a fresh version.&lt;/p&gt;
&lt;h2&gt;A blog example&lt;/h2&gt;
&lt;p&gt;But consider a blog article with the URL &lt;strong&gt;"/caching/caching-explained/"&lt;/strong&gt;. A typical blog has categories, tags, and
authors. A link and summary for the article can exist on many pages: the home page if it's recent, the category pages
that the article belongs to, the author page, and so on.&lt;/p&gt;
&lt;p&gt;When we update the article, flushing only the key &lt;strong&gt;"/caching/caching-explained/"&lt;/strong&gt; is not enough. We also have to find
the other pages it appears on and flush them too, because they may have changed. That means issuing database queries to
find all the pages that our article appears on, gathering them into a list, and issuing a flush for each of them.&lt;/p&gt;
&lt;h2&gt;An eCommerce example&lt;/h2&gt;
&lt;p&gt;Another example is an ecommerce store with lots of products and product categories. A particular product might appear on
100s of pages with its price displayed. When that price changes, the cached site needs to reflect it. You have two
choices: do a lot of work on the server to discover the pages the product is on and flush them, or flush everything.
Neither option is good. The first can slow your website to a crawl with database queries; the second forces the cache to
repopulate.&lt;/p&gt;
&lt;h2&gt;Enter Cache Tags&lt;/h2&gt;
&lt;p&gt;Cache tags, also known as surrogate keys, are a mechanism for adding another way to find content in a cache. Unlike the
primary cache key, these tags are not unique.&lt;/p&gt;
&lt;p&gt;A website utilises cache tags by returning them in an HTTP header with the response. For example, Magento 2 uses the
header &lt;strong&gt;X-Magento-Tags&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;x-magento-tags: cms_b_site_home_main_banner,store,cms_b,cms_b_site_homepage_bar,cms_p_47,cms_b_header_custom_notice,cms_b_porto_custom_block_for_header_home5,cms_b_site_header_social_links,cms_b_site_home_shopby_category,cms_b_site_home_shopby_brand,cat_c_p_2,cat_p_2508,cat_p,cat_p_2483,cat_p_2387,cat_p_2372,cat_p_1412,cat_p_1388,cat_p_2575,cat_p_2560,cat_p_2557,cat_p_2543,cat_p_2520,cat_p_1262,cat_p_2434,cat_p_2423,cat_p_1660,cat_p_1579,cat_p_1276,cat_p_1217,cms_b_site_footer_social_links,cms_b_site_footer_contact_us,cms_b_site_footer_popular_items,cms_b_site_footer_quick_links,cms_b_site_footer_information
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Magento returns tags for page elements like the navigation, sidebar, and notice, as well as for product categories and
products. Products have tags in the format &lt;strong&gt;cat_p_1234&lt;/strong&gt;, where &lt;strong&gt;1234&lt;/strong&gt; is the product id in the database.&lt;/p&gt;
&lt;p&gt;When someone updates product 1234, a flush is issued for the tag &lt;strong&gt;cat_p_1234&lt;/strong&gt; and all pages that have that tag are
flushed. Magento doesn't have to do any work trying to determine which page the product might be on. The cache can
efficiently find those cached pages and invalidate them.&lt;/p&gt;
&lt;h2&gt;Cache tags in CMSs&lt;/h2&gt;
&lt;p&gt;As mentioned, Magento 2 uses a sophisticated cache tag strategy to maximise the performance of its full page cache. Other
CMSs, including Drupal 8/9/10 and Typo3, also utilise cache tags. Peakhour adds cache tags to &lt;a href="/docs/how-to-guides/integrations/wordpress"&gt;WordPress&lt;/a&gt;,
&lt;a href="/docs/how-to-guides/integrations/prestashop/"&gt;Prestashop&lt;/a&gt;, &lt;a href="/docs/how-to-guides/integrations/magento-1/"&gt;Magento 1&lt;/a&gt;, and &lt;a href="/docs/how-to-guides/integrations/opencart-3/"&gt;Opencart&lt;/a&gt;
via our plugins to enable full page caching.&lt;/p&gt;
&lt;h2&gt;Cache Tag support amongst CDNs&lt;/h2&gt;
&lt;p&gt;If you're looking for maximum full page cache effectiveness for your website, especially if you're using a CMS with
built-in cache tag support, cache tag support matters. The table below outlines support amongst major CDN providers.&lt;/p&gt;
&lt;table class="table table-striped"&gt;
&lt;tr&gt;&lt;th&gt;CDN/Cache&lt;/th&gt;&lt;th&gt;Cache Tag Support&lt;/th&gt;&lt;th&gt;Custom Header&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Peakhour&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Cloudflare&lt;/td&gt;
    &lt;td&gt;
        &lt;i class="fas fa-exclamation-triangle text-yellow-200 text-xl"&gt;&lt;/i&gt;
        Enterprise Plan Only
    &lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-times-circle text-red-700 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Fastly&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Self Hosted Varnish&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-check-circle text-green-500 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Cloudfront&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-times-circle text-red-700 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
    &lt;td&gt;&lt;i class="fas fa-times-circle text-red-700 text-xl"&gt;&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;h2&gt;Enterprise or essential?&lt;/h2&gt;
&lt;p&gt;In our view, cache tags are an essential feature for any &lt;a href="/learning/cdn/"&gt;CDN&lt;/a&gt; trying to maximise cache performance.
They make cache invalidation targeted and efficient, which protects hit rates and reduces work on the origin server. They
shouldn't be walled off in an Enterprise-level package.&lt;/p&gt;</content><category term="Performance"></category><category term="Caching"></category><category term="CDN"></category><category term="Drupal"></category><category term="Web Performance"></category></entry><entry><title>Headless Commerce Security</title><link href="https://www.peakhour.io/blog/headless-commerce-security-api-protection/" rel="alternate"></link><published>2023-06-28T00:00:00+10:00</published><updated>2023-06-28T00:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-06-28:/blog/headless-commerce-security-api-protection/</id><summary type="html">&lt;p&gt;Comprehensive analysis of security challenges in headless commerce and Single Page Applications. Learn how to protect modern e-commerce APIs and microservices architectures from scraping, fraud, and automated attacks.&lt;/p&gt;</summary><content type="html">&lt;p&gt;At Peakhour, we spend a lot of time looking at e-commerce architecture trends. Single Page Applications (SPAs) and
headless commerce keep coming up, with tools such as Nuxt.js, Strapi, Hydrogen, and Gatsby leading many builds. These
tools can make frontend work faster and more flexible, but they also put more e-commerce data behind APIs that scrapers
can target.&lt;/p&gt;
&lt;p&gt;Single Page Applications (SPAs) and headless e-commerce have changed how many retailers build their storefronts.
Frontend development tools like Nuxt.js and headless CMSs like Strapi are now common parts of that stack.&lt;/p&gt;
&lt;p&gt;The trade-off is exposure. Product information is often available as JSON data, which makes it easier for scrapers to
collect at scale. That raises a practical question: how do you secure data while still making it available through APIs?&lt;/p&gt;
&lt;h2&gt;Strategies for Data Protection&lt;/h2&gt;
&lt;p&gt;Data protection matters, but it is not a single control. These are the usual layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Controls the number of client requests to your API within a set time frame.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bot Detection&lt;/strong&gt;: Distinguishes between humans and bots based on behavioural patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Page Load Authentication&lt;/strong&gt;: Secures the page load through bot detection and authenticates subsequent API calls.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IP Threat Intelligence&lt;/strong&gt;: Blocks suspicious IP addresses from accessing your API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GeoIP Filtering&lt;/strong&gt;: Regulates requests based on geographical origin.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As bots change, those controls need to change as well.&lt;/p&gt;
&lt;h2&gt;Facing the Challenge of Headless Scraping&lt;/h2&gt;
&lt;p&gt;Headless scraping uses browsers without a user interface to imitate normal browsing. It is difficult to detect, but
&lt;strong&gt;network fingerprinting&lt;/strong&gt; can help.&lt;/p&gt;
&lt;p&gt;Network fingerprinting examines network features like Transport Layer Security (TLS) settings and HTTP/2 (H2)
parameters. By analysing these, companies can detect and block bots, adding another security layer.&lt;/p&gt;
&lt;h2&gt;Client-side Security in SPAs&lt;/h2&gt;
&lt;p&gt;In SPAs, where much of the processing happens in the user's browser, the security concerns shift:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data Exposure&lt;/strong&gt;: Protecting sensitive data from leakage or manipulation is critical.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Injection Attacks&lt;/strong&gt;: SPAs must guard against attacks like Cross-Site Scripting (XSS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication and Session Management&lt;/strong&gt;: Properly handled, these prevent unauthorised access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Insecure Direct Object References (IDORs)&lt;/strong&gt;: Proper authorisation stops attackers from accessing others' data.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Risks in JavaScript Packages&lt;/h2&gt;
&lt;p&gt;SPAs usually depend on JavaScript libraries and packages. They are useful, but they also add supply chain risk. Using
only essential packages, keeping them updated, and sourcing them from trusted providers reduces that risk. Supply chain
audit tools can help automate the work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://owasp.org/www-project-dependency-check/"&gt;OWASP Dependency-Check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://securestack.com/"&gt;SecureStack&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Security audits need to be frequent because vulnerabilities can appear quickly. Tools like npm's npm audit or GitHub's
Dependabot, along with regular penetration testing, can help uncover potential weaknesses.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The move toward SPAs and headless commerce is a trade-off between development flexibility and security exposure. These
architectures can improve user experience and speed up delivery, but they also introduce new security issues.&lt;/p&gt;
&lt;p&gt;Client-side security in SPAs needs deliberate attention. Data exposure, injection attacks, and insecure direct object
references all need to be managed, and the convenience of JavaScript libraries brings its own vulnerabilities.&lt;/p&gt;
&lt;p&gt;Peakhour addresses these problems with rate limiting that manages request traffic and helps prevent attacks without
harming customer experience. Our Web &lt;a href="/learning/cloud-security/cloud-waf-vs-native-waf/"&gt;Application Firewall&lt;/a&gt; (WAF)
examines all payload data, adding another layer of protection.&lt;/p&gt;
&lt;p&gt;Frequent security audits still matter. They help e-commerce managers keep SPAs and headless commerce operations secure
without giving up the efficiency these architectures can provide.&lt;/p&gt;</content><category term="Security"></category><category term="API Security"></category><category term="Magento"></category><category term="Account Protection"></category><category term="Drupal"></category><category term="Application Security"></category><category term="Bot Management"></category></entry><entry><title>Consistent Hash with Bounded Loads</title><link href="https://www.peakhour.io/blog/bounded-load-hashing/" rel="alternate"></link><published>2023-05-15T13:00:00+10:00</published><updated>2023-05-15T13:00:00+10:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2023-05-15:/blog/bounded-load-hashing/</id><summary type="html">&lt;p&gt;An overview of consistent hashing with bounded loads, an advanced load balancing technique that ensures a more even distribution of keys across servers, preventing overload and improving system stability.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A consistent hash with load bounds extends the basic consistent hashing technique. Standard consistent hashing
distributes keys across servers in a way that minimises rehashing when servers are added or removed, but it does not
guarantee a perfectly even load distribution. Random hash collisions can leave some servers responsible for
significantly more keys than others, creating an imbalance in the load distribution.&lt;/p&gt;
&lt;p&gt;Consistent hashing with bounded loads, or bounded-load hashing, addresses this issue. It keeps the load (i.e., the
number of keys assigned) on any given server within a factor of the average load across all servers.&lt;/p&gt;
&lt;p&gt;The approach still uses a hash ring, but with an additional constraint that no server can exceed a certain load limit.
When a key is hashed and the corresponding server in the hash ring is already at its load limit, the key is assigned to
the next server in the hash ring that isn't at its load limit. This process is repeated until all keys have been
assigned.&lt;/p&gt;
&lt;p&gt;Here's a rough pseudocode example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Function to add a key with bounded loads&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_key_bounded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Find the server for this key&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;  &lt;span class="c1"&gt;# To loop back to the beginning of the ring&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hash_ring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;server_loads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hash_ring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;LOAD_LIMIT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server_loads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hash_ring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# Increment the server&amp;#39;s load&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash_ring&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Return the server that should handle this key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code&gt;server_loads&lt;/code&gt; is a dictionary that tracks how many keys each server is responsible for,
and &lt;code&gt;LOAD_LIMIT&lt;/code&gt; is the maximum number of keys any server is allowed to handle.&lt;/p&gt;
&lt;p&gt;By bounding the load on each server, this approach can produce a more balanced distribution of keys across servers.
However, it's worth noting that this algorithm can result in keys being assigned to servers that are not their nearest
neighbours in the hash ring, which can increase latency in some cases. As with any algorithm, there's a
trade-off between &lt;a href="/learning/load-balancing/"&gt;load balancing&lt;/a&gt; and other factors such as latency and computational overhead.&lt;/p&gt;
&lt;p&gt;The Google Cloud team, in collaboration with visiting researcher Mikkel Thorup from the University of Copenhagen,
devised an efficient allocation algorithm to improve load balancing in large-scale web services such as content hosting.
The results of their research were detailed in a paper titled “Consistent Hashing with Bounded Loads” that was published
in August 2016.&lt;/p&gt;
&lt;p&gt;Load balancing in large-scale web services means distributing client requests evenly across multiple servers to prevent
any individual server from becoming overloaded. An ideal load-balancing system distributes the load uniformly and
minimises the changes to the system when servers or clients are added or removed. This consistency matters in dynamic
environments where servers and clients can change over time.&lt;/p&gt;
&lt;p&gt;Traditional consistent hashing techniques, although effective for load balancing in dynamic environments, can still lead
to sub-optimal load distribution across servers in certain scenarios. As both servers and clients can be added or
removed frequently, the load balancing algorithm needs to stay dynamic and adaptive, maintaining an evenly distributed
load while minimising the number of client reassignments whenever a change occurs.&lt;/p&gt;
&lt;p&gt;Google's algorithm addresses these challenges. To explain their method, they use an analogy of servers as bins and
clients as balls. The goal is for all bins (servers) to have a load (clients) roughly equal to the average density, with
a small tolerance factor, ε. The algorithm is designed to ensure that every bin has a capacity within the range of the
average load times (1+ε), which helps achieve both uniformity and consistency in client-server allocations.&lt;/p&gt;
&lt;p&gt;The algorithm employs two separate hash functions to assign positions to balls (clients) and bins (servers) on a
circular continuum. The balls are then allocated in a specific order (say, based on their ID), with each ball being
assigned to the first bin it encounters with spare capacity as it moves clockwise on the circle.&lt;/p&gt;
&lt;p&gt;The algorithm recomputes the allocation whenever an update occurs (i.e., the addition or removal of a client or server)
to maintain the uniform distribution of load. The paper proves that every ball removal or insertion in the system
results in O(1/ε²) movements of other balls. Importantly, this upper bound is independent of the total number of balls
or bins in the system, which means the algorithm scales well with increasing size.&lt;/p&gt;
&lt;p&gt;The algorithm creates a trade-off between uniformity and consistency. A smaller ε value provides better uniformity but
compromises consistency, while a larger ε value improves consistency but reduces uniformity. The algorithm performs well
even in worst-case scenarios, making it ideal for dynamic, large-scale environments like content hosting services.&lt;/p&gt;
&lt;p&gt;Google's algorithm has been used in production. Andrew Rodland from Vimeo implemented it in HAProxy, a popular
open-source software package, for their load balancing project, resulting in a substantial decrease in cache bandwidth
by almost a factor of 8. This eliminated a significant scaling bottleneck, showing the algorithm's practical
effectiveness.&lt;/p&gt;
&lt;p&gt;Overall, the work of the Google Cloud team and Mikkel Thorup is a useful contribution to load balancing. By addressing
the challenges of uniformity and consistency in dynamic environments, their algorithm provides a robust solution for
managing large-scale web services efficiently. The team's research and its open-source availability give the broader
community a practical implementation path.&lt;/p&gt;</content><category term="Features"></category><category term="Rate Limiting"></category><category term="Drupal"></category></entry><entry><title>Multi-Origin Load Balancing</title><link href="https://www.peakhour.io/blog/multi-origin-load-balancing/" rel="alternate"></link><published>2023-04-11T13:00:00+10:00</published><updated>2023-04-11T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-04-11:/blog/multi-origin-load-balancing/</id><summary type="html">&lt;p&gt;Websites with a global audience need more than just a traditional CDN. They need geographic multi origin load balancing.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Websites and applications with global audiences often run into a problem that a standard CDN cannot always hide: not
every request can be served from cache. Traditional Content Delivery Networks (CDNs) reduce latency by distributing
content across multiple points of presence (POPs) around the world. But when content has to be fetched from a distant
origin server, for example dynamic content that cannot be cached, users still wait on that round trip. This post explains
the problem multi-origin &lt;a href="/learning/load-balancing/"&gt;load balancing&lt;/a&gt; solves and how Peakhour handles it.&lt;/p&gt;
&lt;h2&gt;Understanding the Problem&lt;/h2&gt;
&lt;p&gt;Web content usually starts from a single server, called the origin server. As a website's audience grows, that server
takes more traffic, which can slow response times. CDNs reduce the pressure by caching and delivering content from
servers distributed across multiple geographic locations. When the content is already cached, this reduces origin load
and lowers latency for users accessing the content.&lt;/p&gt;
&lt;p&gt;However, traditional CDNs still have limits when serving global audiences. If a user requests content that is not cached
in the CDN, the request has to go back to the origin server. If that origin is far away from the user, latency increases
and pages take longer to load.&lt;/p&gt;
&lt;p&gt;Multi-origin load balancing addresses that remaining gap in &lt;a href="/learning/cdn/"&gt;CDN&lt;/a&gt; performance and further reduces latency.&lt;/p&gt;
&lt;h2&gt;Introducing Multi-Origin Load Balancing&lt;/h2&gt;
&lt;p&gt;Traditional load balancing distributes traffic evenly across two or more servers that
are physically hosted in the same location.&lt;/p&gt;
&lt;p&gt;Multi-origin load balancing extends that approach across origin servers in different geographical locations. The
Peakhour EDGE can select the closest origin server to a user, or choose a different origin based on criteria such as
device type, user preferences, or URL. For requests not stored in the CDN, this reduces the time spent crossing long
network paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-Time Performance Monitoring:&lt;/strong&gt; Peakhour continuously monitors its global network of servers in real time. This
allows the system to detect potential issues or bottlenecks and adjust routing. If one origin server experiences high
traffic or goes offline, Peakhour can reroute user requests to the next best server to keep the site responsive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adaptive Content Caching:&lt;/strong&gt; Peakhour Edge uses adaptive content caching strategies, which dynamically cache both
static and dynamic content based on user behaviour and request patterns. Frequently requested dynamic elements, such as
personalised user data or search results, are cached on edge servers, reducing the need to fetch content from the
origin servers and further minimising latency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Load Balancing and Failover:&lt;/strong&gt; Peakhour's multi-origin load balancing is complemented by load balancing and failover
mechanisms. These features keep the system resilient and responsive during periods of high traffic or server outages.
By distributing user requests evenly across origin servers and automatically redirecting traffic when a server fails,
Peakhour maintains a stable and reliable content delivery experience for users worldwide.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Multi-origin load balancing addresses the limitations of traditional CDNs by optimising content delivery for a global
audience. Peakhour Edge reduces latency and improves the experience for users who would otherwise wait on a distant
origin server.&lt;/p&gt;
&lt;p&gt;As web content and applications grow in complexity and reach, origin placement and routing become part of performance
planning. Peakhour implements multi-origin load balancing with clients' origin servers so requests can be sent to the
most suitable origin instead of treating a single distant server as the only fallback.&lt;/p&gt;</content><category term="Features"></category><category term="CDN"></category><category term="Web Performance"></category><category term="Drupal"></category><category term="Caching"></category><category term="Rate Limiting"></category></entry><entry><title>Introducing Automatic Cache Tags in the Updated Peakhour WordPress Plugin</title><link href="https://www.peakhour.io/blog/wordpress-plugin-cache-tags/" rel="alternate"></link><published>2023-03-20T13:00:00+11:00</published><updated>2023-03-20T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2023-03-20:/blog/wordpress-plugin-cache-tags/</id><summary type="html">&lt;p&gt;Read about automatic cache tags in the updated Peakhour WordPress plugin.&lt;/p&gt;</summary><content type="html">&lt;p&gt;We have updated the Peakhour WordPress plugin with automatic cache tag generation. The update is designed to help keep
cached WordPress content accurate without forcing full cache clears for routine content changes. We have also checked
compatibility with the latest versions of WordPress and PHP.&lt;/p&gt;
&lt;h2&gt;What are Cache Tags?&lt;/h2&gt;
&lt;p&gt;Cache tags are labels that CDNs such as Peakhour use to manage cached content more precisely. Instead of clearing the
whole cache when one page changes, you can purge only the content associated with the relevant tag. Visitors get the
updated content, while the rest of the site can continue to be served from cache.&lt;/p&gt;
&lt;h3&gt;A Cache Tag Example&lt;/h3&gt;
&lt;p&gt;Suppose you run a site with a home page, product pages, and blog articles. Without cache tags, updating one product page
or article may require clearing the entire cache. With cache tags, each part of the site can use its own tag, such as
"homepage," "product," and "blog." When you update content, you only need to purge the cache for the affected tag. That
means less unnecessary cache invalidation and a faster site for visitors.&lt;/p&gt;
&lt;h2&gt;Automatic Cache Tags in the Peakhour WordPress Plugin&lt;/h2&gt;
&lt;p&gt;The updated Peakhour WordPress plugin generates cache tags automatically for your site's content. When you update
content in the WordPress admin, the plugin sends the relevant purge requests to Peakhour. The result is more targeted
cache clearing without extra manual work.&lt;/p&gt;
&lt;h2&gt;How to Get Started with Automatic Cache Tags&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Update to the latest version of the Peakhour WordPress plugin.&lt;/li&gt;
&lt;li&gt;Follow the instructions to configure cache tags in the Peakhour admin.&lt;/li&gt;
&lt;li&gt;Flush your cache.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After that, the plugin generates cache tags and sends purge requests to Peakhour whenever you update content in the
WordPress admin.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Automatic &lt;a href="/learning/cache-tags/"&gt;cache tags&lt;/a&gt; in the Peakhour WordPress plugin make cache purging more precise. They
help improve performance, give you more flexibility, and reduce unnecessary cache clears. Try the updated plugin and let
us know how it works for you.&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="CDN"></category><category term="Drupal"></category><category term="WordPress"></category><category term="Rate Limiting"></category></entry><entry><title>Layer 7 DDoS Protection</title><link href="https://www.peakhour.io/blog/layer-7-dos-and-full-page-caching/" rel="alternate"></link><published>2023-02-07T13:00:00+11:00</published><updated>2024-12-01T13:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2023-02-07:/blog/layer-7-dos-and-full-page-caching/</id><summary type="html">&lt;p&gt;Comprehensive guide to Layer 7 DDoS protection using strategic caching within application security platforms. Learn how intelligent caching strategies provide robust defence against sophisticated application-layer attacks.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In previous blog posts we've covered the benefits that &lt;a href="/blog/caching-dynamic-content-with-a-cdn/"&gt;full page caching has on page load performance&lt;/a&gt;.
We also covered how caching pages lowers origin server utilisation, so the site can handle more customers.
A lesser-known side benefit is protection against Layer 7 &lt;a href="/products/ddos-protection/"&gt;denial of service&lt;/a&gt; (DoS) attacks.&lt;/p&gt;
&lt;h2&gt;What is a Denial of Service attack?&lt;/h2&gt;
&lt;p&gt;The goal of a DoS attack is to make a network resource, such as a website or networked service, unavailable to users
by overwhelming it with excessive traffic or requests. DoS attacks can be launched from a single source or from multiple
sources (known as a distributed denial of service, or DDoS attack).&lt;/p&gt;
&lt;p&gt;Typically, an attacker floods the web server with HTTP or API requests to try to overwhelm it. An
attacker might also launch a 'slow attack', especially if they find a weak point in the application that consumes
a lot of server resources for a single request. One example is repeatedly using a site search function.
By slowing down the rate at which requests are sent, the attacker can bypass common rate-limiting and
traffic-shaping mechanisms that are designed to block high-volume traffic spikes.&lt;/p&gt;
&lt;p&gt;DoS conditions can also be inadvertent. A CMS like Magento or WordPress on an underpowered server can be overwhelmed,
or slowed to a crawl, by so-called 'grey' bots. Examples include Semrush, Ahrefs, dotbot, and MJ12Bot. These spiders
can crawl a site aggressively enough to bring it to its knees. Even Bing or Google can negatively impact a site.&lt;/p&gt;
&lt;h2&gt;What is Layer 7?&lt;/h2&gt;
&lt;p&gt;In networking parlance, Layer 7 refers to the actual application running on a web server. It is part of the OSI (Open Systems
Interconnection) model used to describe the various functions in transmitting data over a network. The model is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Layer 1 -&amp;gt; Physical Layer&lt;/strong&gt;, responsible for transmitting raw bits of data, e.g., a wire or wireless link&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 2 -&amp;gt; Data Link&lt;/strong&gt;, responsible for transmitting data frames between network devices and detecting transmission errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 3 -&amp;gt; Network Layer&lt;/strong&gt;, responsible for routing data packets between networks and determining the best path for transmission.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 4 -&amp;gt; Transport Layer&lt;/strong&gt;, responsible for end-to-end communication between applications, providing reliable data transmission and flow control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 5 -&amp;gt; Session Layer&lt;/strong&gt;, responsible for establishing, maintaining, and terminating communication sessions between applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 6 -&amp;gt; Presentation Layer&lt;/strong&gt;, responsible for formatting data to be presented to the application layer and for converting data from the application layer into a standardised format for transmission.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer 7 -&amp;gt; Application Layer&lt;/strong&gt;, responsible for providing services to the user, such as file transfer, email, and web access.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How Full Page Caching helps&lt;/h2&gt;
&lt;p&gt;Full page caching helps by reducing the number of dynamic requests to the server. Caching the entire page
allows the server to serve pre-generated content to visitors, rather than generating it dynamically each time a page is
requested. This reduces the server's processing load and helps prevent excessive requests from overloading
the server or making it unavailable.&lt;/p&gt;
&lt;h2&gt;A real world example&lt;/h2&gt;
&lt;p&gt;As part of a series of Peakhour recommendations to 'harden' its website against layer 7 attacks, a Peakhour client
(a government site) applied limited full page caching with a &lt;strong&gt;time to live of 10 minutes.&lt;/strong&gt;  This means that Peakhour
will serve a page from its Edge cache for 10 minutes before hitting the origin again for a new version.&lt;/p&gt;
&lt;p&gt;Not long after implementation, that change was tested: a DDoS originating from hundreds of IPs across multiple countries
hammered a set of 5 pages. The attack was spread over several days with bursts of activity lasting about 15 minutes. With
the first hit, the page was cached and every subsequent hit was served from our high-performance Edge cache. No
slowdown was observed and the attackers gave up.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/layer-7-dos-attack.jpeg" width="100%" alt="Layer 7 DoS real world"/&gt;
&lt;em&gt;Real attack on a Peakhour client; the spikes formed in 15 minute bursts.&lt;/em&gt;
&lt;/div&gt;

&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/dos-attack-page-load.jpg" width="100%" alt="Layer 7 DoS real world page load"/&gt;
&lt;em&gt;Real page load times measured in the client browser&lt;/em&gt;
&lt;/div&gt;

&lt;h2&gt;But my site is dynamic...&lt;/h2&gt;
&lt;p&gt;Many websites have a small dynamic component on the page. For ecommerce sites this might be the mini cart in the top right
showing the number of items in the cart, or it might be some personalisation for a user. Often these dynamic
parts of the page can be rendered in the browser using Ajax or local storage, rather than rendered on the server.
By moving the dynamic components to the browser, the full page becomes cacheable. Another option is to use Edge Side
Includes (ESI), which enables the majority of the page to be cached in the CDN while the dynamic parts are fetched separately
before serving the full page to the user.&lt;/p&gt;
&lt;p&gt;Peakhour can help move dynamic page components from the server to the browser and cache more at the edge.
We also have a range of CMS plugins that do just that. Contact us if you want some help.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Layer 7 DoS attacks have become more common than traditional DoS attacks, as they typically require far fewer
resources from the attacker. Reducing dynamic requests to the origin using full page caching is a useful but underappreciated
way to mitigate them. If you're concerned about website security, resilience, and performance,
Peakhour helps you cache more so you can protect, accelerate, and scale your website.&lt;/p&gt;</content><category term="DDoS"></category><category term="DDoS"></category><category term="CDN"></category><category term="Rate Limiting"></category><category term="Caching"></category><category term="Drupal"></category><category term="DNS"></category></entry><entry><title>Automatic Image Optimisation</title><link href="https://www.peakhour.io/blog/automatic-image-optimisation/" rel="alternate"></link><published>2022-12-06T13:00:00+11:00</published><updated>2022-12-06T13:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2022-12-06:/blog/automatic-image-optimisation/</id><summary type="html">&lt;p&gt;Image optimisation saves you bandwidth and designer effort while reduces page load time.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Since releasing our &lt;a href="/image-optimisation/"&gt;automatic image optimisation&lt;/a&gt; capability in 2019, Peakhour optimises
more than 35 million images each month. On average, clients reduce page download time by 35% and image sizes by
91%.&lt;/p&gt;
&lt;p&gt;Automatic image optimisation offers a number of benefits over manual optimisation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Image formats are dynamically chosen based on the content of the image.&lt;/li&gt;
&lt;li&gt;Files are optimised based on client capabilities.&lt;/li&gt;
&lt;li&gt;Designer time is saved by offloading image optimisation work.&lt;/li&gt;
&lt;li&gt;Page load times can be reduced across an entire website.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What is Image Optimisation?&lt;/h2&gt;
&lt;p&gt;Image optimisation means delivering high quality images in the right format and resolution for the device viewing them.
The aim is to minimise file size without compromising visual quality. Smaller image files generally load faster in a
visitor's browser, which can improve website speed, reduce traffic and support higher page visits and conversions.&lt;/p&gt;
&lt;h2&gt;Why dynamically choose the image format?&lt;/h2&gt;
&lt;p&gt;Using a one size fits all optimisation process does not account for the different types of images used across a typical
website.&lt;/p&gt;
&lt;p&gt;Website images can include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Photographs, such as product images, demonstration photos and title photos.&lt;/li&gt;
&lt;li&gt;Logos, which are typically text and can include animation.&lt;/li&gt;
&lt;li&gt;Illustrations that incorporate custom graphics to support the quick perception of information.&lt;/li&gt;
&lt;li&gt;3D graphics from computer renders including buildings, interior and exterior designs and products.&lt;/li&gt;
&lt;li&gt;Combinations of the above!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any optimisation process needs to account for the end user's device type, supported file formats, resolution and the
content of the image. For example, a photograph with text needs different optimisation parameters so the text remains
crisp, while images with large areas of colour may need different formats to avoid colours being degraded by lossy
compression.&lt;/p&gt;
&lt;h2&gt;Fine developer control&lt;/h2&gt;
&lt;p&gt;With the &lt;a href="/docs/explanation/image-optimisation/api/"&gt;image optimisation API&lt;/a&gt;, your developers can enable reactive images by
resizing images on the edge. Peakhour serves and caches these optimisations, reducing origin requests and traffic,
cutting costs and making image changes faster to ship. You no longer need to wait for a designer to resize, crop or
re-orient an image for a page.&lt;/p&gt;
&lt;h2&gt;How does image optimisation effect web application performance&lt;/h2&gt;
&lt;p&gt;Large portions of a website are typically built from images. Large images loading over a 4G network in a train can delay
image rendering and create a poor user experience. Layout shifts can occur as the browser attempts to render the page,
and the user's perceived page load time will be affected.&lt;/p&gt;
&lt;p&gt;You can see the benefit of automatic image optimisation with our &lt;a href="/pages/page-weight"&gt;free tool&lt;/a&gt;. It will analyse any
website, calculate its optimisation potential and let you download the optimised images.&lt;/p&gt;
&lt;h2&gt;See how it can help in a real life example below&lt;/h2&gt;
&lt;div class=images&gt;
&lt;div class=image-container style="border: 1px;border-color: lightgrey; border-style: solid;"&gt;&lt;div class="section text-center"&gt;&lt;div class=image-header&gt;&lt;i class="fa-image fas" style="padding-right: 5px;"&gt;&lt;/i&gt; security.png &lt;/div&gt; &lt;div class=image-box&gt;&lt;a href=https://www.peakhour.io/static/images/security.png target=original title="show original"&gt;&lt;img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAChCAMAAABK+nL1AAAAt1BMVEX///8sOU/yyLr99fL+/f0hLUP9+/rvrUD//v7EjzT0z8MUIzvxwbL88Ozsrpr11cpTfrj55d7onYTvu6ofME7qp5H44Nb66uXttaJZUUPBiy6Eo8y6y+Hb5O/lkXb22tCZstXy9fqnvdvPlTLJ1eX3sj/n7fTbnznT3eyyt79pj8HJlDeRl6F1YT9ESlXrqDqPcT2rgTrhgGD34LjQpFnivHtibHzszJQ7bK7utVfbaEOhprCAh5RYZMzlAAAACXBIWXMAAAsSAAALEgHS3X78AAAWIklEQVR4nO1dCUPiOhAOaYwxtiltt4ccLSAiwgME12Pd/f+/680k5ZRzRReUz4PSFmg/ZiYzk2RCyAknnHDCCSeccMIJJ5xwwgknfBTEGP/6Qo4AYunm98YKIgRx7BxB9D3ZWqpSYslu2JGqIEcaEfYJF3dgYIwwtEECNpAgeEosTjjX9gn+MTjMBNJHrJRoswWnKPrtyGLEU0lGLOCHRDE8cwmzSKZInCQx7GOckTjCDYtospgB/55kubajMiVtRbOMJtImMnRVSNwwi+3EVXbmBRnskipDiUsnr0u+JVlUKN9OwjiwbRkGkoROQiWxoiAJbRUQldAgsTLfDjRZxmkQ/HuSlYUJpcoNHJsmKrQlARmTLnGDhCYBCFqgPDguAxWNJcvYrG9IFty64xAWcyYYF44FxgieCKAhBpPlCDT0cCQWHAyaIN4ojh04aMVWGJDv6JrO3LNWsbEDZXbjf5E3k3KUpv4olWrkp+koJOx7+lrbgIGGwj9uB0EMj1FKHGg7T3QtBfjvqR+GYZKAcQvDVBKeBctd2hPQz3LHyFwPtTMOrG9o5/8OIGzBtwx7toFgM8AdYMLAKTuxtR3A5ToZrh3A4sQ6+RBbAux+4p2Ea0sATSfDpTFjzdfBDT7jYg4cWyqXICfDRcBJN04o33wmGq5PuKKDBSMqDUOllL8xagYDb8svb+bX3Z4gPsTMjuPE1PM8a9MbufbX5spyiJYKk4gxW2KSpWHE94jvhz4G0L4vNgjXlzZcENolgUtizh3GHODNciyL4C+3jBhx6sUUdgoiQ+k7G5gQxnB9TbqArFA6ibK9UAahSiRYp8RLEhrK0MWb5r5BmhH/1Q83kQWxI7FdnUH8eoSB7+3ZSeIoGqggCjxFXQqEUUWtwOaYRk6x35CTJCCZijZKlmbL1a/8egDJUoEMQ2rDgydj240yN0ts6VqBwluOX33MV9FXSjhhW5CFZi5OHOJuc+rRgTvEdgTXFkqYptFCI21xfOKF9ijivgLJ4oynDvjzGzmA1ypH9zJ+NeherXkLk/dT6D/PJ1ma2sROVOqnfn7CJhoEof636ygTJPL1WAcSJAScLZCqSEbcHFqHBBP0K8gSIs8gfrGONBzbMGkNjY7yWNo003ytkRzLWjzIOGD5K/DQ5kD98AFsReC5e5Ezs5M7klIX6RB8pXDMHNA8mc16q1VtNyt3gMpdpdlsV1t1c0isJvOoYAY3TJ7iHTEro1Q6nGzSRxwUgeDtu99/np76/cKZxuXZjx9nZ4VCod9/evrz312lapT7uHshxZJ0FggU/OcRpTTGVnR1C4myIqp3f56ApEv4maIwxg8EPO0//XfXwjfbnN44Omh+uAd8edZklOCCUuI51f+ezi4BZwv4YVCYkoYMPv1uHbt0rYCmBgw+taMY+WILYTYj7A6ZmhB0OSNUV33EWLJy1lAzn5qbXZOjBPCD40UclwYZjn5wZwblwh03n8ZMwUPh6qrXu78fDoelUumidHs+GAxub2+7F8P7+95Vv5AzBnw9VckXVEUD1BphZTJw48yfksXIbyNKl8BT735Y6l4jut3uBQDIAtyc3xicD7rD+6uC4euscHdsDu0uhoNpNyJyJfj6+W0y8p+hqtC7LyFJF/PQZM0AKRsMe32k68fZkbE1jXm2A7aIjvLHesjIneaqD0whT6UFrkqLZOWMDYZ9ZKvQPCq2hIXu+i62QxBXTrd5H8nqXVxfLMdSsrRm9oCts6cjylkwEvs4di0gfKeLZhMlrGiurt+I1FrJMnTdo+GqHphorUmom8iZxOnOpsuAk9/gg/YXDdUWkgUYgCYenNUCJWP5zAk9gyKfRIFIgCx3FHopsf9qig5H8355tUoH10rW+c2VJuuA3AcIkYEP4giIYC20UGikONM5QEaUT9IRV69RNPqrFPFGstZJ1iGS5YRuTENXUTfMklC6ylV2kDgmFROqdEToq53mncw7pp7eJVnnB0gW9+xEWV5o0SQJnNC3A9zUA0VJrNJX5bivI18uCNY4dbeQuVswMF9PspLAxpkTllJ2qChVWWC5oR6rzdMgGgWpPQoyk/HkWeTFjmO99SUYckechf1fTLIIzpKAMBju1HJ4AI8OZ6Z7mpFEERdUkI584qU4xAG8c5dCxExtm1IpZRRFcWxZee6PEanmBXALyfo1pmbwa4Grj5EsMY+/ef3k5sab2mJZzEpp6oUqtIkz1TDMLFiWE8dR5kqXUgqiCeRRV3r+PFtbSJYVjbmKfn0CWYuOyP58XkZcn1A/FG6Yf4owdurNidxyHM+LIt+f27+RrK5HyMNAU+NZnyFZotWuVqvt/Lfd2t87A1khzm4lE7IWPhn9sqmNFyRQ8ydsIqv0gKZRs/VAnI+zWeNB6Jw0XoqzeKnsz+kV3EHTjT2wm88FqYt3ag27F6VHhm//PBg8gO38MMmamb9VxtC8jmi16i1S6/zD4HMnsjD596gt5jNI2BvJ2qMaNmtzaGFE1umUi6SxT7LyzO62rcbiaWvJKj0/lrqlZ0w7c53Z2TNZ2glES0EqL+VyuVMeo1Osw4c2m+3Kfsl6J9aRBTTxn6VbzZbu6XgcfIzNYqRt0mIgTvV2vcnqxfZYxorlwwnU15AFCgj+3M/b85IeDQGGq7RXyRKkUexMUOwUy1xwkLHGS7VebI7lbB9k6f4H3Uv4PiFdTRaYdlQQ59fNoOTid/5cWhSsd5ElSOulUcnRhJ9KsYGzsVqsRVpFzJJVq632PtRQzPim7xq1sZKs0k9uwsv4/Pz2IiKEvuHqfWQBGWCZhHEays1ahXQatbG/UEQnAgz8yyayxEppmelwrtehBSmXq7C107Vua+BLP6087iAesNWN3bdcvc9mjcnSqYJOpdMgnVoVZAzRKLaM/9BaTRaWT8HEHtO8YJJPu5T5o/Et9UHSLneq1U612alXOuXWLoK6MApmBVmlnzGeiX2M4JIiW93bt1z9tWQxjoPNWsUmuukTdBr6ztrlOtiszljIaktsFqvnt8xbdd4af35dj+rj+bF2q9kygldutcq1JiGNRplU9NuJN+NCcow7f/QjtDXbBNIlCHKAK+8C5YuhA3+7hKtFsvALNl+y/tbNn6mPM90hzCn4w4sLznobyeo0Oy1oDdsgYPAHUrEkWmw1CKm20KhVG5VqHR7qvM0aFQK316wxfN4sg7dWb3N4da2BIODulivlXQKCTmeerRWSleFIkbirXVI453mJCr5Hsmo1ptWwxesG4KvXwWbV2wCQtopWw1oNRGGJGmqyqrVaBSio1KB5gBMbVfiDrQbITrtcbdRgE09A/lmjxhlwVyMtsIwo041isVEtFst1kN96uVhsV2Z2tMByNmFHrVV86cxJ31Ky0Lij11DKQ0Pr13KyFmwWXFu9CpddbzSaDG6ENRsN+OobbdhRYXCUj3c0X8rwtlObVX+pFWtAVvllYuDXk1UjICONZhuoamNbCm8K3LRrKFywp90oY/uK14P9V+VavQmMVtqtWrmNCgi2sVWHlzI4i8Eb1FsVEFB4I9hR4bCjhTtY56WyWbKQLMMV1RnGn9fLuVqQLPiKQCaKlfwr6tTzHY3JDvjO2k3Y0XoBniZkIc+tZhUMPKkzMEHadUAVRDFbooZw1UA43FGj3KhWGyiLrTZQ12pUG1VSbzSrqJwooc0atICtDtiscgOpr/FmZzIjZfZxidGHrxAM3WabhWSB794t/cT2ha3iamc1nHg7NdPaFcu1PL5BX71crJCpUzqxZO1lRgYNI9DSmGnb2FsKzCdCk9o0ni2DtrCKG2LWsorcspLxzsmTxUh6JVmPJXxEbXhcxdUKA8/e2vPxk/wP2jBUhnpxGg0iXnS+oVmvQLiD6rMmT6rf2FRZY3mhNRzNrUeMMe0eGscnv+NKw8Qg4Grt1L26cPIKshhyhY/A1WKQs9Jm7QA9Tn1GDZtGDSszgbSYnLnmXba5dYZNrAk0y/V3xU4ryNJcaQl7ExC+Qw2nEMYpbekhv5yhgS+DgR+ThWb4HTf1Fq22xk4e6Vusckpz0ohdur35ALJIThYxSRrSQs3sLPM/9wKxhZhugfX5rJ8PpQ/rNwRF7Mw7pWCo6txg36Ttp7zthrRyd20n63vzWfXmHN6pJB+PgxoYcnBcHVT3PZv9+eiJLiYo3eUFfMF5eEvW7FCt7rENOVqDnUaTGg9N0fm9b8gqDad0de+H4MWviKL3YLM+E5zQRCprg5E3g2nyV1hRGq4LpLvD/tXVsKtdh9LF9dX9denR9EefL6HsICVrkjqen5pJ4hRL+q1xTsQMTV4kqbQjOH9NIH09vLrt9S56V73r+6uri6th6f7ReXge/Hp4eD5/Hjz+On94eFiUrIPpfZlAR1d64JDQfcQ4FCmlQEcql3yzYjqvxnI8V+KQmsizOPH8+bO5HtldmGjhsD+8uu9dDQv3F7gBP4/ZT+/Xw+Nt9PwweHg8j2bSgIPCj8KPPXav7wPcI7GZAxfPTf4ejTLP85LX+amoU+crp0kiTSw/EMeL03aaOAp+2J2oYe++i/NRhr0ekFXoXT8+3D48Zr9uHwxZD4OJPt4MgavCQXlHOMSfJJ4bERpJ6TnSyjLPxQle3H9FuPnVTnVO8NiTktpSZrE19vSXN5tmHPxED0H5brvXIFnDUt9IVg9s1sOv54fH6Bf8gGQtmKzDGgePM3oT37PDgEZ2GiswU5HSZTSDUQo2aJTOlCziVgymiQbSjZxJPZrZIUhvWgNuZqMMr3PJ6qFxB5t1cX/VG94PL3o/0VTdPj/8vH18eP51/jzRQRSsQzNZOACXJEEQKukFaQacKZaFEu5S+amM/TDVmgUWPHNxyFoWWblZEts4YRDE4pzVsdXqatKur69h6xqjne717fnt4PwaH66vB+cTi3VzCzp41j+0Mho8Ip5HpWPLOM48GkciozjCKLWDV3/kpLq8SoQW3JnQtMNcHi1al1fL5g1gAjDPOtzkf3NN4aEJFplzi6YdsKCFIYl9qoiXWjPu6WZpEotP89k7K0KeFR78zaHO3GGmZAMzCVPMMuKwFt8jrop9QnxTnWDJ4MgVbzcvDIy0UREvl7O1ItzR83Z+9A+qKVwNRnzq+GE8cuN0p4koVrbolxlf6+zyfilbS8kyXB3N/DlBsrwIlK92uGJGeODLN4n435erZGupZBmufhycwdon0JzxOEpGi7KVz2a97C2x8m/IGpiZhoWz3wcWFa7DfJ3pTRCYqsUSIrazZGkZTv5otq4uuqUNkjW4GVwdHVc7QcsUMuWBN7us2l/um172h4uquEjWTbef6+CX5ArFCJiSpmoIZ8uqMQiwW6ZYwYKZX5Ssm2FB2/YvyZWRKaplKu9jXe5iQJtY0FPLe93uSskam6t+89i5WjLoS8dBWClkWlllzcvbfWO4ZlVxTrKMCoIv2jrudhAN+Jz3rpVthqnNERAn9SfNFqhid4lk3dzcaxU8+8OPXK4Eseg0lBGmHJSkNELt29K3Z+PiITPCNZWsm9urSZmQQ/fbMd6ZmePC5swPI460X6U07ZweVYNMZZqpHT6Ek2b/Mrfz3TnJysWqcPl0aLUJlkCYXDEXAhe50rsYNmw4llXnvEKVKD8vMSMcl5oSdmu1T/DFYhDQKNb/mAo+V8OZWjRgrbRY/Sj8dwwqyCkVnuWZJ4I4VGL+T5fEwqd+GigntNPQQqZsmXGxWfuW3TUIsGkVL/MqIkjWzSC3VtAKHrwKYk0Qatu+DO3YorFHiQyoHQeWb3vUgptOfH80kqPUTylxgSlGtim7GaglxeNBbFt5hag+6CJI1s2NrjsDjuifel6Y5KDLJDKiYkcpT8kgUYGyiVRJELhBkiWJg2SNQk9yGfkjqm9niypgAlf/GC2pu4mae2eciLOr0vXtzW3vx0Ss2MzLDxUgWbZNAzdxEpW4SURoIBWQZcsoxEjPSvxYStdL7S1KIRJNpuCSusvLCsNRsFy5LnaNYT9DazXuECFRdMiGS4DNIp4EqwVqGDtgs6jlBZYjMcMM37J8xVUGwtetVrjCNGHsJh7L1PLTcQgFNIuaroIRq6e2ESttImOaJOPk+yHaMLGwMXuJjLhpnOAUfC/dvDoFDld1bIoZQ2vligS4tuRvE/4AVZf9u0m7iv6L9KhyiemLPMhcqR5zLPQEgfHqyPpq9TBc5UZpolTqBPYG0cKktAeNAyNLJ6/MfV4LdVFrYH0sQCxGqQSxthPNFBfekVX9BrICkKvE9YNwPVlo/DO41XF7tu42schp8wkk6091XPGVYDFmj7BEkkjXh8hs6ieHbL7eAvwtPwxx8YowjNcQgA2gh+UktxQFlNo7XWFzJoyCj3IiSbIMmwibetSlxyVaCDE7zGHZcdRXSSXOL9n6TXVJm2ldG4GlXpjrKykpyF0m3SBA5sQWPXCHg+lqhquuGfZD+BhZuy78y2ZHDvMIWJIQTYQJlpMQklo0ySYf8DWgfQVpe/ydviTFNRwoCBq3ffTobFwr3cMuIywX/iXowkYrDqSnW9J3vRHFeFRKPa0ugMjKUq6NPbwsTNWXWDwEvY2M2hZ7t/vISBYJy6Uq1p4LRA2uQ2P8LpxQUv9418KYGi/BI3s/K6AI4iUyCaSJvrlFYhVmyCE4XYH0j3XdRDH9zzM7s/YT9oJkBVKadcgY4xCKEi8MI2ghcdkkuunlhwpuuaFnhmlxbKz2liIwJfjxbWWmQtBBEvng2rEsUdlhmCwxSYzMVL4g+qKZmY8452HpxT9Go1E6qT+217sw0bRFfXdEsVq6oYvvS3z3gby+g2ZHF3gYe9U4etmkr4SZR0r0sjJO4FipY+b17RGTL8uiqa+CGCXXIZmf6JIG/54s8JACN98aB60Rj3CwHyeubenMskVdOb0TICv2MXdqf9T1C+LSxMWkRURlIB3pBxYJNqyq+CkIswBcP5lx6keZdGwap67r2XFiRb6bpdSzvMRPbc92pOuiywhkYbk61//A1ZosJ9PzkyCUTlPKuZ3mWZt/CZCakBCpbKUSaSdhAu2OreIkU8p2iAw85SVOQoPAhUOhF+LXi2QFYI3TNP24y9fDchhxAxXa4PASHrz6/5osuCoF4aqUIYT4SaCoTSOaREmSqMCKKXg4WUCVpNQL4bhw0fMxZIH/4/ofZkfyhoXIDKyXxKg6NEV4d52ott+rIg7Ev7FwIyYljzInthwviz3pxRwiWe5kDo0cJ+axdDySuRPJgmuOP46sycVhqhttu+Pkew4gpjYXsS4FM93wRq6PK/fh0nQff+ETt8WUPnOTYOuE2UdczXiE8thv0JUftLSzabUovdOUlCIywQU9PkGyTDf5JOnKHSsYJeEocraoBHooYATiWulmwaeaXGiLRmnqhyH4xOrf6+K2EIJjZ08Yfu5K5IwkeS1F/6+WPfhWYMROCMcOJ/9fmq3dsT7J/EGfiRXUdZidHoIzf9gAf94E71b6ry/l8GFWhiWf0gofPTRLmrPwRNYmgO+A89XZqur0J8xBpJ5lOZjzOJG1CYLYqZ8CPte/O1qcSNoeS0aPnXDCCSeccMIJJ5xwwgnfFP8D+4Qq+JHEZOkAAAAASUVORK5CYII=" style="max-width: 100%; max-height: 300px"&gt;&lt;/a&gt;&lt;/div&gt; &lt;div class=info-box&gt;&lt;div&gt;&lt;div style="width: 40%;display: inline-block"&gt;&lt;h5&gt;Original&lt;/h5&gt;&lt;/div&gt; &lt;div style="width: 50%;display: inline-block"&gt;&lt;strong&gt;2362 x 1266px&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;div style=" width: 35%; display: inline-block"&gt;&lt;strong&gt;PNG&lt;/strong&gt;&lt;/div&gt; &lt;div style=" width: 5%;display:inline-block"&gt;&lt;/div&gt; &lt;div style="display: inline-block; width: 25%"&gt;&lt;strong&gt;190.2 kb&lt;/strong&gt;&lt;/div&gt; &lt;div style="width: 30%; display: inline-block; font-size: 17px"&gt;&lt;span class="fa-info-circle fas text-primary" data-content="                    &lt;strong&gt;format:&lt;/strong&gt; PNG&lt;br&gt;                    &lt;strong&gt;is_animated:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;colour_space:&lt;/strong&gt; RGBA&lt;br&gt;                    &lt;strong&gt;has_transparency:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;is_photo:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;width:&lt;/strong&gt; 2362&lt;br&gt;                    &lt;strong&gt;height:&lt;/strong&gt; 1266&lt;br&gt;            " data-title="Image Info" data-toggle=popover&gt;&lt;/span&gt;&amp;nbsp; &lt;a href=https://www.peakhour.io/static/images/security.png target=original title="open original in new window"&gt;&lt;span class="fa-external-link-alt fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class=info-box&gt;&lt;div&gt;&lt;div style="width: 40%;display: inline-block"&gt;&lt;h5&gt;Optimised&lt;/h5&gt;&lt;/div&gt; &lt;div style="width: 50%;display: inline-block"&gt;&lt;strong&gt;605 x 324px&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;div style="width: 35%; display: inline-block; text-transform: uppercase"&gt; png &lt;/div&gt; &lt;div style="width: 5%; display: inline-block"&gt;&lt;br&gt;&lt;/div&gt; &lt;div style="width: 25%; display: inline-block"&gt; 10.8 kb &lt;/div&gt; &lt;div style="width: 30%; display: inline-block;font-size: 17px"&gt;&lt;span class="fa-info-circle fas text-primary" data-content="                    &lt;strong&gt;format:&lt;/strong&gt; PNG&lt;br&gt;                    &lt;strong&gt;optimize:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;quality:&lt;/strong&gt; 75&lt;br&gt;                    &lt;strong&gt;colour_space:&lt;/strong&gt; RGBA&lt;br&gt;                    &lt;strong&gt;lossless:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;subsampling:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;delay_format:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;strip:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;compression:&lt;/strong&gt; 6&lt;br&gt;            " data-title="Optimised Info" data-toggle=popover&gt;&lt;/span&gt;&amp;nbsp; &lt;a download=security.png.optimised.png href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/6641ec77-2367-4c17-ab27-2447cdc8aceb/img.png title=download&gt;&lt;span class="fa-download fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;a href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/6641ec77-2367-4c17-ab27-2447cdc8aceb/img.png target=6641ec77-2367-4c17-ab27-2447cdc8aceb title="Open in new window"&gt;&lt;span class="fa-external-link-alt fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;div style="width: 35%; display: inline-block; text-transform: uppercase"&gt; jpeg &lt;/div&gt; &lt;div style="width: 5%; display: inline-block"&gt;&lt;br&gt;&lt;/div&gt; &lt;div style="width: 25%; display: inline-block"&gt; 15.4 kb &lt;/div&gt; &lt;div style="width: 30%; display: inline-block;font-size: 17px"&gt;&lt;span class="fa-info-circle fas text-primary" data-content="                    &lt;strong&gt;format:&lt;/strong&gt; JPEG&lt;br&gt;                    &lt;strong&gt;optimize:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;quality:&lt;/strong&gt; 75&lt;br&gt;                    &lt;strong&gt;colour_space:&lt;/strong&gt; RGB&lt;br&gt;                    &lt;strong&gt;lossless:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;subsampling:&lt;/strong&gt; 4:2:0&lt;br&gt;                    &lt;strong&gt;delay_format:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;strip:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;progressive:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;progressive_min_bytes:&lt;/strong&gt; 10240&lt;br&gt;                    &lt;strong&gt;quant_table:&lt;/strong&gt; 3&lt;br&gt;            " data-title="Optimised Info" data-toggle=popover&gt;&lt;/span&gt;&amp;nbsp; &lt;a download=security.png.optimised.jpeg href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/796c932f-a4a1-4a4b-890d-d88fba459c29/img.jpeg title=download&gt;&lt;span class="fa-download fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;a href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/796c932f-a4a1-4a4b-890d-d88fba459c29/img.jpeg target=796c932f-a4a1-4a4b-890d-d88fba459c29 title="Open in new window"&gt;&lt;span class="fa-external-link-alt fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;div style="width: 35%; display: inline-block; text-transform: uppercase"&gt; [webp](/products/image-optimisation-and-transformation/) &lt;/div&gt; &lt;div style="width: 5%; display: inline-block"&gt;&lt;br&gt;&lt;/div&gt; &lt;div style="width: 25%; display: inline-block"&gt; 10.7 kb &lt;/div&gt; &lt;div style="width: 30%; display: inline-block;font-size: 17px"&gt;&lt;span class="fa-info-circle fas text-primary" data-content="                    &lt;strong&gt;save_all:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;duration:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;loop:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;format:&lt;/strong&gt; WEBP&lt;br&gt;                    &lt;strong&gt;optimize:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;quality:&lt;/strong&gt; 75&lt;br&gt;                    &lt;strong&gt;colour_space:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;lossless:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;subsampling:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;delay_format:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;strip:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;method:&lt;/strong&gt; 3&lt;br&gt;                    &lt;strong&gt;kmin:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;kmax:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;allow_mixed:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;minimize_size:&lt;/strong&gt; True&lt;br&gt;            " data-title="Optimised Info" data-toggle=popover&gt;&lt;/span&gt;&amp;nbsp; &lt;a download=security.png.optimised.webp href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/72346c2d-1302-40f0-bb38-027890c5c920/img.webp title=download&gt;&lt;span class="fa-download fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;a href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/72346c2d-1302-40f0-bb38-027890c5c920/img.webp target=72346c2d-1302-40f0-bb38-027890c5c920 title="Open in new window"&gt;&lt;span class="fa-external-link-alt fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;div style="width: 35%;display: inline-block; text-transform: uppercase"&gt;&lt;strong&gt;avif &lt;span class="fa-trophy fas text-primary"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt; &lt;div style="width: 5%; display: inline-block"&gt;&lt;br&gt;&lt;/div&gt; &lt;div style="width: 25%; display: inline-block"&gt;&lt;strong&gt;8.2 kb&lt;/strong&gt;&lt;/div&gt; &lt;div style="width: 30%; display: inline-block;font-size: 17px"&gt;&lt;span class="fa-info-circle fas text-primary" data-content="                    &lt;strong&gt;format:&lt;/strong&gt; AVIF&lt;br&gt;                    &lt;strong&gt;optimize:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;quality:&lt;/strong&gt; 50&lt;br&gt;                    &lt;strong&gt;colour_space:&lt;/strong&gt; RGBA&lt;br&gt;                    &lt;strong&gt;lossless:&lt;/strong&gt; False&lt;br&gt;                    &lt;strong&gt;subsampling:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;delay_format:&lt;/strong&gt; None&lt;br&gt;                    &lt;strong&gt;strip:&lt;/strong&gt; True&lt;br&gt;                    &lt;strong&gt;compression:&lt;/strong&gt; 6&lt;br&gt;            " data-title="Optimised Info" data-toggle=popover&gt;&lt;/span&gt;&amp;nbsp; &lt;a download=security.png.optimised.avif href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/11a20ace-cd35-4f59-91f7-2794c8650815/img.avif title=download&gt;&lt;span class="fa-download fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;a href=/page-weight/result/41377868-5104-4eaf-9776-a9a682a5e8f5/images/11a20ace-cd35-4f59-91f7-2794c8650815/img.avif target=11a20ace-cd35-4f59-91f7-2794c8650815 title="Open in new window"&gt;&lt;span class="fa-external-link-alt fas text-primary"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Benefits of Peakhour Automatic Image Optimisation&lt;/h2&gt;
&lt;p&gt;Peakhour image optimisation is integrated into your website so you can reduce traffic, speed up page loads and remove
manual image preparation from the design workflow.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Transforming images on the Peakhour edge enables agility and offloads origin work.&lt;/li&gt;
&lt;li&gt;User experience is improved through faster-loading pages, without a separate workflow for image optimisation.&lt;/li&gt;
&lt;li&gt;Take advantage of new image formats, as supported by end users' browsers, to improve the page load experience.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Peakhour image optimisation runs on Peakhour Edge. Images are optimised as they pass through the Peakhour network, and
optimised versions are cached for reuse. Peakhour image optimisation also works with &lt;a href="/blog/cdn-origin-shield"&gt;Origin Shield&lt;/a&gt;,
with the original and transformed images cached appropriately. This reduces origin hits and bandwidth while speeding up
image delivery.&lt;/p&gt;</content><category term="Features"></category><category term="Web Performance"></category><category term="Caching"></category><category term="Core Web Vitals"></category><category term="CDN"></category><category term="Drupal"></category><category term="Rate Limiting"></category></entry><entry><title>Drupal full page caching module</title><link href="https://www.peakhour.io/blog/drupal-purge-module/" rel="alternate"></link><published>2022-10-05T13:00:00+11:00</published><updated>2022-10-05T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2022-10-05:/blog/drupal-purge-module/</id><summary type="html">&lt;p&gt;We're excited to announce our Drupal 8/9 caching module, read on for more details.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Drupal is an open source content management system (CMS) widely used by Australian government websites. For example,
GovCMS is a customised version of Drupal. Peakhour now provides a purge module for Drupal 8/9, extending our
existing Drupal support.&lt;/p&gt;
&lt;p&gt;The Peakhour Purge module extends the Drupal Purge module. The Purge module provides a standardised way of
integrating third party CDNs, like Peakhour, with Drupal's native full page caching support.
Drupal's native support is suitable for small websites, but has a number of drawbacks. These drawbacks include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Doesn't support the Vary header.&lt;/li&gt;
&lt;li&gt;Is served by PHP, so it doesn't scale well.&lt;/li&gt;
&lt;li&gt;Runs on the same server as Drupal, so geographic latency is still an issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Peakhour's edge cache and delivery layer &lt;strong&gt;removes these issues before requests reach origin&lt;/strong&gt;, and also adds
&lt;strong&gt;image/content optimisation and website security.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Default Caching&lt;/h2&gt;
&lt;p&gt;Since version 8, Drupal has supported cache tags to provide efficient,
targeted invalidation of content for anonymous browser sessions. In this context, anonymous means a user that does
not have a Drupal Session cookie. Peakhour supports cache tags for all sites, a feature some competitors
reserve for 'Enterprise' plans.&lt;/p&gt;
&lt;p&gt;Cache tags are great, but another default setting is not. That setting is to add a:&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;Vary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cookie&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Header to the page response to make sure that logged-in users don't get cached pages. The Vary
header informs a cache that different content may be served from origin based on the value in the Cookie
header sent by the browser.&lt;/p&gt;
&lt;p&gt;This is fine if you are using Drupal with no third party javascript libraries. If you do
use third party libraries, it can render page caching close to useless. For example, drupal.org itself uses
perimeterx for bot detection, which results in a unique session cookie being set on the very first request.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/drupal-org-first-request.jpg" width="100%" alt="The cookie set by perimiterx"/&gt;
&lt;em&gt;The first request to drupal.org results in a unique cookie being set by perimiterx&lt;/em&gt;
&lt;/div&gt;

&lt;p&gt;If you use Google analytics, Facebook, or any of a wide array of popular third party libraries, the same
thing can happen.&lt;/p&gt;
&lt;p&gt;This means only the &lt;strong&gt;very first&lt;/strong&gt; request from the user could be served from a general
page cache. Hit rates would be virtually zero.&lt;/p&gt;
&lt;h2&gt;Fix the Vary issue with Skip Cache on Cookie&lt;/h2&gt;
&lt;p&gt;Peakhour gives you a way to work around this. The first step is to disable the &lt;strong&gt;Vary: Cookie&lt;/strong&gt; header. Then, to
make sure any user with a Drupal Session bypasses the cache, use Peakhour's &lt;strong&gt;Skip Cache on Cookie&lt;/strong&gt; feature.
As soon as Drupal sets a session cookie, eg when someone logs in, Peakhour will pass requests through to the origin.
Full instructions on how to do this are provided on our &lt;a href="/docs/how-to-guides/integrations/drupal/"&gt;drupal module documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;eCommerce Drupal sites&lt;/h2&gt;
&lt;p&gt;If you are running an ecommerce store, or some other type of site that
might serve customised information on a page, for example a mini cart with cart count/information,
then you will have to do custom development to continue caching pages.
A workaround for a mini cart would be to make it load via Ajax or to use local browser storage,
and &lt;strong&gt;Peakhour assists in making that happen&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Future features&lt;/h2&gt;
&lt;p&gt;Hopefully in the future Drupal will support setting its own cookie for varying the cache, in a similar manner to
Magento 2. eg X-Drupal-Vary, this would allow Peakhour to store multiple versions of a page to serve to different users.
For example, a user in Germany might get a version of a page in German with German currency.&lt;/p&gt;</content><category term="CMS"></category><category term="Drupal"></category><category term="Caching"></category><category term="CDN"></category><category term="Core Web Vitals"></category><category term="WordPress"></category><category term="Rate Limiting"></category></entry><entry><title>Prestashop 1.6/1.7 full page caching plugin released!</title><link href="https://www.peakhour.io/blog/prestashop-plugin/" rel="alternate"></link><published>2022-08-04T13:00:00+10:00</published><updated>2022-08-04T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2022-08-04:/blog/prestashop-plugin/</id><summary type="html">&lt;p&gt;Prestashop is the latest shopping cart we've made a plugin for, read on for more details.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Prestashop is a popular open source eCommerce platform written in PHP. We're releasing a Prestashop module that enables
full page caching through Peakhour. Pages can stay cached at the Peakhour edge for longer, and the plugin refreshes them
when the underlying content changes in Prestashop.&lt;/p&gt;
&lt;p&gt;&lt;a href="/blog/caching-dynamic-content-with-a-cdn/"&gt;Full Page Caching&lt;/a&gt; reduces load times by removing the time it takes for a
CMS to generate a dynamic page. That is typically around half a second for a quick page, and 3-5 seconds for a slow one.
Here is a cache miss and hit from our client fatburnersonly.com.au:&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/prestashop-cache-miss.jpg" width="100%" alt="Prestashop cache miss"/&gt;
&lt;em&gt;A typical cache miss on a category page. The origin has to generate the page, taking &lt;strong&gt;2.49 seconds&lt;/strong&gt;&lt;/em&gt;
&lt;/div&gt;

&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/prestashop-cache-hit.jpg" width="100%" alt="Prestashop cache miss"/&gt;
&lt;em&gt;A cache hit: &lt;strong&gt;only 35.7 milliseconds&lt;/strong&gt; to deliver to the client from our edge&lt;/em&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Nearly 2.5 seconds removed from Time to First Byte and Largest Contentful Paint.&lt;/strong&gt; That's without changing
the site itself.&lt;/p&gt;
&lt;h3&gt;Tag Based Flushing&lt;/h3&gt;
&lt;p&gt;Like all our recent plugins, the Prestashop plugin adds a header with tag metadata for each cacheable page. This header is
called X-Prestashop-Tag and contains the IDs of the relevant entities on the page, eg products, categories, brands, pages,
etc. This metadata is stored alongside the cached page. When an entity changes in the Prestashop admin, eg a product price
is updated, the plugin issues a flush-by-tag request to Peakhour. Peakhour finds the pages with the associated tag and
invalidates them in the cache. The next request for the page passes through to origin and is then re-cached.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/prestashop-tags.jpg" width="100%" alt="Prestashop full page caching headers"/&gt;
&lt;em&gt;Headers returned by the [caching plugin](/blog/opencart/opencart-3-caching-plugin/).&lt;/em&gt;
&lt;/div&gt;

&lt;h3&gt;Custom TTL&lt;/h3&gt;
&lt;p&gt;TTL (Time to live) is the time a resource stays in cache before the cache checks for a new version. You can control this
within the plugin. The plugin then sets the peakhour-cdn-cache-control header, part of the new
&lt;a href="/blog/cdn-cache-control-header/"&gt;cdn-cache-control specification&lt;/a&gt;, so only Peakhour responds to the cache directive.&lt;/p&gt;
&lt;h3&gt;Ajax Mini Cart&lt;/h3&gt;
&lt;p&gt;Mini carts are dynamic sections of eCommerce websites, and they often stop pages from being cacheable. The Peakhour plugin
loads them via Ajax so as many pages as possible remain cacheable.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/prestashop-mini-cart.jpg" width="100%" alt="Prestashop mini cart"/&gt;
&lt;em&gt;The mini cart is in the top right; it is usually returned as part of the generated page.&lt;/em&gt;
&lt;/div&gt;

&lt;h3&gt;Cache varying&lt;/h3&gt;
&lt;p&gt;The same page can show different information depending on factors such as whether a user is logged in, or whether a
multicurrency store changes currency. The Peakhour Prestashop plugin handles this by changing a cookie value when those
factors change, creating separate cache regions for the different possibilities.&lt;/p&gt;
&lt;h2&gt;The Results&lt;/h2&gt;
&lt;p&gt;The Peakhour Prestashop plugin can improve store performance measurably. Our client fatburnersonly.com.au improved their
'good' web vitals scores by 20% in the two months they've been using Peakhour.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/prestashop-lcp-improvements.jpg" width="100%" alt="Prestashop web vitals improvement"/&gt;
&lt;em&gt;Full page caching was enabled halfway through May. Note that a significant number of pages, eg checkout and admin, cannot be cached and are included in these stats&lt;/em&gt;
&lt;/div&gt;

&lt;h2&gt;Grabbing the plugin&lt;/h2&gt;
&lt;p&gt;If you have a slow Prestashop store, see our &lt;a href="/docs/how-to-guides/integrations/prestashop/"&gt;plugin page&lt;/a&gt; or
&lt;a href="/contact-us/"&gt;contact us&lt;/a&gt; for more information.&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="Drupal"></category><category term="WordPress"></category><category term="Web Performance"></category><category term="CDN"></category><category term="Core Web Vitals"></category></entry><entry><title>CDN Cache Keys</title><link href="https://www.peakhour.io/blog/cdn-cache-keys/" rel="alternate"></link><published>2022-05-16T13:00:00+10:00</published><updated>2022-05-16T13:00:00+10:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2022-05-16:/blog/cdn-cache-keys/</id><summary type="html">&lt;p&gt;Cache keys allows the segmenting of the Peakhour.IO cache by elements of both the request and response, enabling effective and flexible caching of content.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Content Delivery Networks (CDNs) are common on business websites. CDNs speed up websites by storing website content
and assets in caches on servers spread around the world. These caches are typically key/value stores. For example,
let's say we are requesting an image found at:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://www.example.com/someimage.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Depending on how the &lt;a href="/learning/cdn/"&gt;CDN&lt;/a&gt; works internally, it may use &lt;strong&gt;/someimage.jpg&lt;/strong&gt; (the key) to retrieve the image (the value)
from its cache. Most CDNs are fairly rigid in what they use as keys, complicated to configure (eg requiring programming
skills), or charge more for flexibility.&lt;/p&gt;
&lt;p&gt;The Peakhour.io CDN cache keeps configuration flexible while still supporting fine-grained control of caching
behaviour for your application.&lt;/p&gt;
&lt;p&gt;The Peakhour.IO cache key consists of a primary key, sub key, and secondary key. Both the primary and secondary key must
match to constitute a cache hit.&lt;/p&gt;
&lt;h2&gt;Primary key&lt;/h2&gt;
&lt;p&gt;The primary key is the key from the client request that uniquely identifies a resource. It consists of the
scheme, host, path and query string from the request. Within our cache, the primary key may be augmented from other
elements of the request, such as the presence of a particular header, or a header value such as a cookie value. At
Peakhour.IO, we call these augmented keys &lt;a href="/docs/reference/rules/vconf-set/#cache-subkey-vars"&gt;cache subkey vars&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/url-cache-key-composition.png" class="img-responsive"&gt;&lt;/p&gt;
&lt;p&gt;The following options can further manipulate the primary key by specifying how the query string is handled. These
options include &lt;a href="/docs/reference/rules/vconf-set/#cdn-query-mode"&gt;ignore query string&lt;/a&gt; (useful for defeating cache-busting techniques)
or &lt;a href="/docs/reference/rules/vconf-set/#cdn-remove-query-args"&gt;stripping certain tags&lt;/a&gt; (commonly UTM tags).&lt;/p&gt;
&lt;h2&gt;Secondary key&lt;/h2&gt;
&lt;p&gt;The secondary key describes parts of the request message that influenced the content of the response from the origin.
This information is stored in the secondary key and the following headers are used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vary&lt;/li&gt;
&lt;li&gt;Content-Encoding&lt;/li&gt;
&lt;li&gt;User-Agent: (browser/mobile)&lt;/li&gt;
&lt;li&gt;Accept-Language&lt;/li&gt;
&lt;li&gt;Variant-06&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Servers are commonly misconfigured and can send back headers that don't match the behaviour of the content.
For example, it is very common for a server to send back a Vary header with &lt;em&gt;Vary: user-agent&lt;/em&gt;. When you check the behaviour
by sending different user agents, though, the content doesn't actually change. This can cause unnecessary cache fragmentation and lower hit rates. Peakhour allows
you to override/ignore origin behaviour to correct this.&lt;/p&gt;
&lt;p&gt;If you prefer a visual view, the diagram below shows all the elements of a request/response that could make up a cache key.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/cache-keys.png" class="img-responsive"&gt;&lt;/p&gt;
&lt;p&gt;For more flexibility, Peakhour.io can use specific Cookie values in the secondary key. This is useful for Content Management
Systems like Magento, which set a special cookie, &lt;em&gt;X-Magento-Vary&lt;/em&gt;, to enable special caching behaviour that wouldn't normally
be achievable; for example, whether the user is logged in, is part of a special group, or has chosen a different currency.&lt;/p&gt;
&lt;h2&gt;How can I see my keys?&lt;/h2&gt;
&lt;p&gt;Cache keys are generated on the fly based on the client's request and the origin's response. To see a cache key,
enable Debug headers in the Peakhour.IO dashboard. As an example, for the given request/response pair:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/&lt;/span&gt; &lt;span class="kr"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;gzip, deflate, br&lt;/span&gt;

HTTP/1.1 200
Vary: accept-encoding
content-encoding: br
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Peakhour.IO would generate the following cache-status header:&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;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;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://example.com/what-is-new.html&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::br&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;stored&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;31536000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the key is the scheme://host/path and the secondary-key is the encoding served by the origin.&lt;/p&gt;
&lt;h2&gt;What's in it for me?&lt;/h2&gt;
&lt;p&gt;By understanding cache keys and how they are constructed, a web application can tailor its responses to better utilise
a cache. The configuration options give you direct control for fine-tuning cache-key handling, improving user experience
and cache hit rates.&lt;/p&gt;</content><category term="Caching"></category><category term="CDN"></category><category term="Caching"></category><category term="Drupal"></category><category term="Web Performance"></category></entry><entry><title>Request collapsing</title><link href="https://www.peakhour.io/blog/request-collapsing/" rel="alternate"></link><published>2022-05-16T11:00:00+10:00</published><updated>2022-05-16T11:00:00+10:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2022-05-16:/blog/request-collapsing/</id><summary type="html">&lt;p&gt;Request collapsing - saving your origin by reducing concurrent requests and re-using responses for resources.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Request collapsing protects busy origin servers that serve changing content. On a high traffic site, a cache miss
after content expires can amplify one expired resource into many simultaneous origin requests. This behaviour is commonly
called a cache stampede or dog-piling.&lt;/p&gt;
&lt;p&gt;When request collapsing is enabled, only a single request is sent to an origin server for a given resource, then the
resulting body is used to satisfy pending requests.&lt;/p&gt;
&lt;p&gt;This can stop a popular cached resource from causing a flood of requests to an origin server when it expires.
On high traffic sites, enabling the feature for the right resources can smooth request volume and traffic to origin servers.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/request-collapsing.png" class="img-responsive"&gt;&lt;/p&gt;
&lt;h1&gt;How it works&lt;/h1&gt;
&lt;p&gt;Request collapsing is implemented internally using &lt;a href="/blog/cache-keys/"&gt;cache keys&lt;/a&gt; and queues.
Cache keys are used as keys to a map, with client requests tracked using a queue. Secondary response keys
are then used to match waiting client requests to origin responses. Matching requests can then be fulfilled using the
same response.&lt;/p&gt;
&lt;p&gt;Failed requests are retried in the same manner.&lt;/p&gt;
&lt;p&gt;If a response to an active request is marked as private with &lt;code&gt;Cache-Control: private&lt;/code&gt; or &lt;code&gt;Set-Cookie&lt;/code&gt;, then all queued
and future matching requests will go directly to the origin without trying to match them.&lt;/p&gt;
&lt;p&gt;This implementation allows Peakhour to serialise requests to an origin, so it's important to enable the feature only on
resources that you know will be cacheable, either through cache-control headers or Peakhour configuration.&lt;/p&gt;
&lt;h1&gt;How do you know its working?&lt;/h1&gt;
&lt;p&gt;Request collapsing can be verified with Debug enabled and the cache-status header in the response. A collapsed request
looks like the example below, with 'collapsed' at the end.&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;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;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://website.com/home-eco.jpg&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;86400&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;collapsed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;How its used in practice?&lt;/h1&gt;
&lt;h2&gt;Image optimisation&lt;/h2&gt;
&lt;p&gt;Peakhour.IO uses request collapsing internally for image transforms. The feature ensures we only transform a single image
when a resource expires on the edge, reducing latency sensitive image transformation work, client transform costs and
avoidable delays for end users.&lt;/p&gt;
&lt;h2&gt;Dynamic page caching&lt;/h2&gt;
&lt;p&gt;Request collapsing is a good fit when enabling caching of expensive dynamically written pages. Peakhour.IO
can cache server heavy WordPress, Magento, PrestaShop, Drupal and other platforms via our caching plugin. The plugin
keeps the CDN in sync by notifying us when content changes.&lt;/p&gt;
&lt;p&gt;Content still needs to change during busy periods such as sales or item purchases. During these periods
servers can be overwhelmed when pages incur a cache miss. Request collapsing can help smooth out this traffic and maintain
response times for users.&lt;/p&gt;</content><category term="Caching"></category><category term="Caching"></category><category term="CDN"></category><category term="Rate Limiting"></category><category term="Drupal"></category></entry><entry><title>Opencart 3 Full Page Caching Plugin Released</title><link href="https://www.peakhour.io/blog/opencart-3-plugin/" rel="alternate"></link><published>2022-03-25T13:00:00+11:00</published><updated>2022-03-25T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2022-03-25:/blog/opencart-3-plugin/</id><summary type="html">&lt;p&gt;Elevate your Opencart 3 store's performance with Peakhour's full page caching plugin. Learn how our features outperform LiteSpeed and Varnish Cache.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Peakhour's Opencart plugin includes features that are usually reserved for enterprise plans with other providers. It follows our 'Enterprise for Everyone' approach in a practical way:&lt;/p&gt;
&lt;h3&gt;Tag-Based Flushing&lt;/h3&gt;
&lt;p&gt;Peakhour's plugin records metadata for each Opencart page in the cache. When you update a product or category, only the relevant pages are refreshed. That keeps cache flushing targeted instead of clearing more content than necessary.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/opencart-3-full-page-caching-headers.jpg" width="100%" alt="Opencart 3 [full page](/blog/drupal/drupal-purge-module/) caching headers"/&gt;
&lt;em&gt;Headers returned by caching plugin.&lt;/em&gt;
&lt;/div&gt;

&lt;h3&gt;Custom TTL&lt;/h3&gt;
&lt;p&gt;Control how long a resource stays in the cache before it checks for a new version. This gives you a direct way to manage cache freshness.&lt;/p&gt;
&lt;h3&gt;Ajax Mini Cart and Wishlist&lt;/h3&gt;
&lt;p&gt;Dynamic sections like mini carts and wishlists usually prevent caching. The plugin loads these sections via Ajax, which makes more pages cacheable.&lt;/p&gt;
&lt;h3&gt;Cache Vary&lt;/h3&gt;
&lt;p&gt;The plugin adapts to different user states, currencies, and languages. It changes a cookie value to create separate cache regions for these variables.&lt;/p&gt;
&lt;h2&gt;Peakhour vs. LiteSpeed and Varnish Cache&lt;/h2&gt;
&lt;p&gt;LiteSpeed and Varnish Cache are good options, but Peakhour offers a more flexible and efficient caching solution for Opencart. The plugin makes Opencart as cache-friendly as Magento 2 or Drupal 8, if not more so.&lt;/p&gt;
&lt;h2&gt;The Results Speak for Themselves&lt;/h2&gt;
&lt;p&gt;Our client saw a clear improvement in their web vitals scores and website scalability. Full &lt;a href="/blog/prestashop-plugin/"&gt;page caching&lt;/a&gt; was enabled in October, with the gains shown below.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/blog/opencart-3-web-vitals-lcp-improvement.jpg" width="100%" alt="Opencart 3 web vitals improvement"/&gt;
&lt;em&gt;Full page caching was enabled in October. Note a significant amount of pages, eg checkout, admin etc cannot be cached which affects these stats&lt;/em&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;For more information, visit our &lt;a href="/docs/how-to-guides/integrations/opencart-3/"&gt;plugin page&lt;/a&gt; or &lt;a href="/contact-us/"&gt;contact us&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="CDN"></category><category term="Drupal"></category><category term="Web Performance"></category><category term="WordPress"></category><category term="Magento"></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>Website Optimisation</title><link href="https://www.peakhour.io/blog/eliminating-blocking-resources/" rel="alternate"></link><published>2021-05-17T13:00:00+10:00</published><updated>2021-05-17T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2021-05-17:/blog/eliminating-blocking-resources/</id><summary type="html">&lt;p&gt;Even if you have the fastest server in the world, your website might still seem very slow to end users if you have lots of render blocking resources, learn how to deal with them.&lt;/p&gt;</summary><content type="html">&lt;p&gt;We've previously covered ways to find &lt;a href="https://www.peakhour.io/blog/common-issues-that-impact-site-speed/#blocking"&gt;resources that block rendering in a browser&lt;/a&gt;.
Now we'll cover techniques for loading those resources without letting them block rendering. Before making changes,
always run some &lt;a href="/blog/introduction-to-website-performance-testing/"&gt;website performance tests&lt;/a&gt;
using your favourite testing tool to get a baseline so you can measure any improvements. We recommend
&lt;a href="/blog/testing-website-speed-webpagetest/"&gt;testing with webpagetest.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before we get into the techniques, it is worth defining the problem.&lt;/p&gt;
&lt;h2&gt;What is a Blocking Resource?&lt;/h2&gt;
&lt;p&gt;When a browser loads a page from a website it reads the HTML markup from top to bottom. If it
encounters a CSS or JavaScript file while it&amp;apos;s reading, the browser stops while it downloads and parses
that file. While the browser is waiting it can’t process the rest of the HTML or display
content to the user. Rendering is blocked.&lt;/p&gt;
&lt;h2&gt;Why is this a problem?&lt;/h2&gt;
&lt;p&gt;It would not be a problem if every page included only the information required to display it. Most websites do not work that way.
They are typically built using prebuilt themes and third-party libraries that can contain lots of
CSS and JavaScript. Only a tiny fraction of this code might be used on the current page, or it might only be needed for ‘below the fold’
content, or on another page of the site. &lt;strong&gt;Note: Below the fold means the content of a page that
doesn't initially fit on the screen&lt;/strong&gt;. Any unnecessary information keeps the browser blocked for longer than
needed, slowing the load experience for the user.&lt;/p&gt;
&lt;p&gt;CSS and JavaScript files are usually at the very top of the HTML document before any of the content. This means they
have to be downloaded and parsed before the browser can display anything. Even a website that has extremely fast server response
times can still seem very slow if it has blocking resources.&lt;/p&gt;
&lt;p&gt;Take a simple example: a website that includes a live chat widget. The JavaScript for the widget is
included at the very top of the page in the &lt;head&gt; section. The browser has to stop, download, and parse the CSS
and JavaScript for the widget (usually downloaded from a third-party domain) before any content is displayed to the user.
The chat widget could appear after the rest of the content without affecting the user experience.&lt;/p&gt;
&lt;h2&gt;Things you should always be doing&lt;/h2&gt;
&lt;p&gt;Before targeting CSS- or JavaScript-specific techniques, cover the basics for both.&lt;/p&gt;
&lt;h3&gt;1. Minify all files&lt;/h3&gt;
&lt;p&gt;Minification is the removal of all non-essential characters in a file, e.g. irrelevant whitespace and code comments. It
can make a substantial difference to the file size.&lt;/p&gt;
&lt;h3&gt;2. Ensure compression is enabled on your server&lt;/h3&gt;
&lt;p&gt;Make sure your server is configured to use either gzip or Brotli compression when serving CSS and JavaScript.&lt;/p&gt;
&lt;h3&gt;3. Self host files&lt;/h3&gt;
&lt;p&gt;If the CSS or JavaScript is on a third-party domain, i.e. not the domain name your website is on, you should strongly
consider uploading the file to your website and serving it from your domain. This might not always be possible, but if it
is it eliminates the overhead of the browser
opening a connection to another domain, and it removes the possibility that the third party is not compressing, minifying,
or using HTTP/2.&lt;/p&gt;
&lt;p&gt;After the implementation of &lt;a href="https://www.peakhour.io/blog/cache-partitioning-firefox-chrome/"&gt;cache partitioning in the major browsers&lt;/a&gt;,
there is no longer any &lt;em&gt;potential&lt;/em&gt; advantage to using third-party CDNs to serve assets like JavaScript, fonts, or CSS. Make a copy on your server and host
them on your domain.&lt;/p&gt;
&lt;h3&gt;4. If you can't self host then preconnect to third party domains&lt;/h3&gt;
&lt;p&gt;If external assets are on third-party domains, and you absolutely can't self host them, then you can improve
loading by telling the browser to &lt;code&gt;preconnect&lt;/code&gt; to the third-party domain.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;preconnect&lt;/code&gt; tells the browser to establish an early connection to the domain before it has discovered the asset
while reading the HTML. Use &lt;code&gt;preconnect&lt;/code&gt; in the head of the HTML, e.g.:&lt;/p&gt;
&lt;p&gt;&lt;link rel="preconnect" href="https://example.com"&gt;&lt;/p&gt;
&lt;p&gt;Establishing early connections can shave 100-500ms off third-party load times, which is worth having. You should only use
&lt;code&gt;preconnect&lt;/code&gt; on critical third-party resources. For all the others you can use &lt;code&gt;dns-prefetch&lt;/code&gt;, e.g.:&lt;/p&gt;
&lt;p&gt;&lt;link rel="dns-prefetch" href="http://example.com"&gt;&lt;/p&gt;
&lt;p&gt;This tells the browser to perform DNS resolution of the domain early, which is similar to looking up a phone number
in the phone book. Pre-resolving DNS can save 20-120ms. That sounds like small numbers, but remember, &lt;a href="/website-performance/"&gt;differences as small
as 100ms can have large measurable impacts on conversion rates&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;5. Select only what you need from frameworks&lt;/h3&gt;
&lt;p&gt;It is common for users of frameworks like Bootstrap or jQuery UI to only use a fraction of the functionality provided.
If this is you, then the first step is to cut out what you're not using. Bootstrap and jQuery UI are modular, you can
either download everything in one bundle, or you can break it up to take just the bits you are using.&lt;/p&gt;
&lt;p&gt;These points will ensure that blocking resources are downloaded as quickly as possible. Now we can discuss some
specific techniques that will help minimise blocking and get your page displaying quickly. Please be aware that
this is not an exhaustive overview, but these techniques will probably get you 90% of the benefit with 10% of the work.
Getting that last 10% requires deeper technical work. If you want to go there, Google’s web.dev
website is a good resource.&lt;/p&gt;
&lt;h2&gt;Techniques for Optimising Blocking Resources&lt;/h2&gt;
&lt;h3&gt;Javascript&lt;/h3&gt;
&lt;p&gt;There are a few different techniques for optimising the loading of JavaScript. First, let's look at the default
loading behaviour. &lt;em&gt;Credit for the images goes to Daniel Imms and his website Growing with the web&lt;/em&gt;. As stated earlier,
the default behaviour is to stop parsing the HTML document when a script is encountered, download the script and execute it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/javascript-loading-legend.png" alt="Javascript loading legend" width="100%""/&gt;
&lt;img src="/static/images/blog/default-javascript-blocking-behaviour.png" alt="Default Javascript loading behaviour" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;Now let's look at how we can change this behaviour.&lt;/p&gt;
&lt;h4&gt;1. Moving scripts to the very bottom of the page, right before the closing &lt;/body&gt; tag.&lt;/h4&gt;
&lt;p&gt;This is the original optimisation technique, before defer and async were introduced. It works by moving scripts to
the very end of the HTML document, where they are downloaded and parsed after everything else.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/javascript-loading-end-of-html.png" alt="Javascript end of page load" width="100%""/&gt;&lt;/p&gt;
&lt;p&gt;In our example in the previous section, the chat widget would now load and pop up after the rest of the content has been displayed to the user.&lt;/p&gt;
&lt;h4&gt;2. Defer and Async&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags that have a &lt;code&gt;src&lt;/code&gt; attribute can be marked as either deferred &lt;code&gt;defer&lt;/code&gt;, or asynchronous &lt;code&gt;async&lt;/code&gt;, or both.
For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;lt;script src=”https://www.somedomain.com/somescript.js” defer async&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tells the browser to change the script loading behaviour.&lt;/p&gt;
&lt;h4&gt;Defer&lt;/h4&gt;
&lt;p&gt;Defer was the original browser-based support for improved JavaScript loading. It tells the
browser to download the script asynchronously while it keeps reading the HTML, and then execute it once it has finished reading the whole HTML document. Here's the loading behaviour
with defer enabled.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/javascript-loading-defer.png" alt="Javascript loading defer" width="100%""/&gt;&lt;/p&gt;
&lt;p&gt;As you can see the script no longer blocks the browser from doing anything else while downloading. Defer preserves
script execution order. This can be very important if a script depends on one declared earlier in the page.&lt;/p&gt;
&lt;p&gt;As noted earlier, you can only mark &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags that have a &lt;code&gt;src&lt;/code&gt; attribute as deferred. Inline scripts, 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;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;console.log(&amp;quot;Hi&lt;span class="w"&gt; &lt;/span&gt;I&amp;#39;m&lt;span class="w"&gt; &lt;/span&gt;some&lt;span class="w"&gt; &lt;/span&gt;inline&lt;span class="w"&gt; &lt;/span&gt;script!&amp;quot;);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Any attempt to defer that inline block will simply be ignored. This can cause problems if you have inline script scattered through your code
and rely on a deferred third-party library, e.g. jQuery. There are two potential solutions if you can't simply move the
code to the bottom of the page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move each inline code block into its own file and include it using the &lt;code&gt;src&lt;/code&gt; attribute so you can defer it.&lt;/li&gt;
&lt;li&gt;Or you can try declaring the block as a &lt;code&gt;module&lt;/code&gt;. Test this carefully, as browser support may be limited and it also
   places the script into &lt;code&gt;strict&lt;/code&gt; mode, which may break it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Async&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; attribute tells the browser to download the script now and execute it as soon as it has finished. Like &lt;code&gt;defer&lt;/code&gt;, the
downloading is done asynchronously. Unlike &lt;code&gt;defer&lt;/code&gt;, the script is executed as soon as it is downloaded. Here is the behaviour:&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/javascript-loading-async.png" alt="Javascript loading async" width="100%""/&gt;&lt;/p&gt;
&lt;p&gt;As you can see the browser doesn't stop while downloading, but does pause once downloading is finished so it can execute
the script. &lt;strong&gt;&lt;code&gt;async&lt;/code&gt; doesn't preserve script order&lt;/strong&gt;, which can potentially cause issues when there are script dependencies.&lt;/p&gt;
&lt;p&gt;All three methods have pros and cons. &lt;code&gt;Defer&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; result in faster page loads overall as the scripts are downloaded
while the browser reads the HTML. Moving the script to the bottom of the page shifts the sequence around.&lt;/p&gt;
&lt;p&gt;Your first option should be to &lt;code&gt;defer&lt;/code&gt; all scripts. Then, if some above the fold content is taking too long because it has a
JavaScript dependency, e.g. a carousel in the hero section, you can try making the necessary scripts &lt;code&gt;async&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3&gt;Optimising CSS&lt;/h3&gt;
&lt;p&gt;The key to fast CSS loading is to prioritise the CSS needed for the immediate above the fold content and then defer
the rest. Many articles advocate extracting the precise CSS and then including it inline
in the HTML document in a style block, i.e.:&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;style&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;.accordion-btn&lt;span class="w"&gt; &lt;/span&gt;{background-color:&lt;span class="w"&gt; &lt;/span&gt;#ADD8E6;color:&lt;span class="w"&gt; &lt;/span&gt;#444;cursor:&lt;span class="w"&gt; &lt;/span&gt;pointer;padding:&lt;span class="w"&gt; &lt;/span&gt;18px;width:&lt;span class="w"&gt; &lt;/span&gt;100%;border:&lt;span class="w"&gt; &lt;/span&gt;none;text-align:&lt;span class="w"&gt; &lt;/span&gt;left;outline:&lt;span class="w"&gt; &lt;/span&gt;none;font-size:&lt;span class="w"&gt; &lt;/span&gt;15px;transition:&lt;span class="w"&gt; &lt;/span&gt;0.4s;}.container&lt;span class="w"&gt; &lt;/span&gt;{padding:&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;18px;display:&lt;span class="w"&gt; &lt;/span&gt;none;background-color:&lt;span class="w"&gt; &lt;/span&gt;white;overflow:&lt;span class="w"&gt; &lt;/span&gt;hidden;}h1&lt;span class="w"&gt; &lt;/span&gt;{word-spacing:&lt;span class="w"&gt; &lt;/span&gt;5px;color:&lt;span class="w"&gt; &lt;/span&gt;blue;font-weight:&lt;span class="w"&gt; &lt;/span&gt;bold;text-align:&lt;span class="w"&gt; &lt;/span&gt;center;}
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, you have to do this on every possible landing page where the critical CSS might be different per page. Inlining
CSS also adds to the download size for each page, and forces the browser to reparse the CSS rules for every load rather
than being able to use a cached file for repeat views.&lt;/p&gt;
&lt;p&gt;We advocate for putting the critical CSS into its own file and loading that normally, and then deferring any other non
critical CSS. Identifying the critical CSS is the main task. The first thing to do is to look at the included
CSS on your website and see if you can identify any non-core files. You can defer those files using this pattern:&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;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;style&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/styles.css&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;this.onload=null;this.rel=&amp;#39;stylesheet&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/styles.css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;as="style"&lt;/code&gt; lets the browser download the file asynchronously. The onload changes the type to stylesheet, telling the
browser to parse the file. Finally, we include a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; to enable the CSS to load normally if JavaScript is turned off.&lt;/p&gt;
&lt;p&gt;Once you've deferred non-core CSS files you can still check whether your core ones contain lots of unused rules.
Google Chrome's coverage tool can show unused rules, but it
doesn’t let you easily export used and unused rules. You have to go through it yourself, programmatically, or use
a third-party tool to make two files: the critical CSS, and the non-critical CSS which can be deferred.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Eliminating, or at least minimising, blocking resources can be one of the biggest improvements to user experience you can make to your
site. With &lt;a href="/blog/web-vitals/"&gt;Google Web Vitals&lt;/a&gt; being introduced soon as a search signal, it is important
to make sure your website loads as fast as possible so your users stay longer and buy more
from your site.&lt;/p&gt;</content><category term="Performance"></category><category term="Web Performance"></category><category term="Caching"></category><category term="Drupal"></category><category term="Rate Limiting"></category><category term="WordPress"></category><category term="Core Web Vitals"></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>Secure Dynamic Content Caching</title><link href="https://www.peakhour.io/blog/caching-dynamic-content-with-a-cdn/" rel="alternate"></link><published>2021-02-09T13:00:00+11:00</published><updated>2021-02-09T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2021-02-09:/blog/caching-dynamic-content-with-a-cdn/</id><summary type="html">&lt;p&gt;Comprehensive guide to secure dynamic content caching that improves server performance whilst maintaining security controls. Learn how modern application security platforms integrate caching with threat protection for optimal performance-security balance.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Last time we covered how &lt;a href="/blog/common-issues-that-impact-site-speed/#slow" target="issues"&gt;slow server performance&lt;/a&gt; can
have a negative, sometimes &lt;strong&gt;very&lt;/strong&gt; negative, effect on your website load times. The causes of slow server responses
are many and varied, and not necessarily tied to the server specification. Diagnosing and dealing with them can be difficult,
time-consuming, and costly. One practical way to reduce the impact is to look for opportunities to cache
&lt;a href="/learning/dynamic-content-caching/"&gt;dynamic content&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What is Dynamic Content?&lt;/h2&gt;
&lt;p&gt;Content delivered by a web server is categorised as either static or dynamic. Static content is the same for every user
and is delivered without being generated or processed by the server. Static content is fast to deliver
and does not tax a server's resources.&lt;/p&gt;
&lt;p&gt;Dynamic content is generated by the server for every request. That can involve querying a database several times and executing
a large amount of code. Depending on the work required to generate the result, dynamic content can be resource-heavy.&lt;/p&gt;
&lt;p&gt;Traditionally CDNs have only cached and served the static content of websites, usually the images, CSS files,
Javascript files, etc. They required the webmaster to upload these resources to the CDN's servers and to modify their website
source HTML to access the resources from the CDN. For example,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://www.yourdomain.com/image1.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;would be changed to be something like&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://cdn.yourdomain.com/image1.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Modern CDNs, like Peakhour, act as a reverse proxy{:target="learning"}, which means they sit between the
end user and the website's origin server. This enables them to &lt;em&gt;transparently&lt;/em&gt; cache a copy of &lt;em&gt;anything cacheable&lt;/em&gt; being returned by the
origin server. By transparently caching, we mean the caching happens without changes to the original website.&lt;/p&gt;
&lt;h3&gt;Full Page Caching&lt;/h3&gt;
&lt;p&gt;Full Page Caching (FPC) is where the actual HTML document of a web page is cached.&lt;/p&gt;
&lt;p&gt;Most websites are built using a Content Management System (CMS). Widely used CMS platforms are Wordpress,
Drupal, Magento, etc. By default, every time a page is viewed the CMS has to generate the content
from its database. Most of the time this generation is unnecessary. The content is either the
same for every user, changes very rarely, or only differs by a small amount of personalisation. In each case it
is possible to perform Full Page Caching to improve page load times and to cut server load. Let's have a look at the
difference full page caching makes to one of our clients, Magento 2 store &lt;a href="/case-studies/savvysupporter/"&gt;savvysupporter.com.au&lt;/a&gt;.&lt;/p&gt;
&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/savvy-before.jpg" width="100%" alt="Savvysupporter before"/&gt;
&lt;em&gt;Main document load &lt;strong&gt;before&lt;/strong&gt; caching: &lt;strong&gt;2.07s&lt;/strong&gt;&lt;/em&gt;
&lt;/div&gt;

&lt;div class="text-center" style="padding: 20px 0px"&gt;
&lt;img src="/static/images/savvy-after.jpg" width="100%" alt="Savvysupporter after"/&gt;
&lt;em&gt;Main document load &lt;strong&gt;after&lt;/strong&gt; caching: &lt;strong&gt;82ms!!&lt;/strong&gt;&lt;/em&gt;
&lt;/div&gt;

&lt;p&gt;Caching the page has cut nearly &lt;strong&gt;2 whole seconds&lt;/strong&gt; from the download time. That matters: load differences
as small as 100 milliseconds have measurable impacts on website conversion rates. With a Magento 2
website, it's possible to cache all full pages outside the checkout process and the customer/admin area. This can reduce load
on the origin in the order of &lt;strong&gt;60-70%&lt;/strong&gt; and make the customer experience much better.&lt;/p&gt;
&lt;h3&gt;API (Application Programming Interface) Caching&lt;/h3&gt;
&lt;p&gt;Many API calls used by web applications are for the retrieval of information to be displayed to the end user. Examples
 include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pricing and product information&lt;/li&gt;
&lt;li&gt;Form auto completion&lt;/li&gt;
&lt;li&gt;Product catalogue searches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are all strong candidates for caching, reducing load on your server and making websites faster.&lt;/p&gt;
&lt;h2&gt;Handling Stale Information&lt;/h2&gt;
&lt;p&gt;The one potential drawback of caching dynamic content is the possibility of returning out of date information to the
end user. There are two strategies to deal with this risk.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Setting a short Time To Live (TTL) with the caching provider&lt;/strong&gt;. By only keeping content cached for a short time, e.g. 5
   minutes, the cache will never be without up to date information for very long. Cached content will expire and the new
   version fetched from the origin server. The drawback with this method is that, unless your site is very busy, cache hit
   rates can be low, users can frequently get slow loading pages, and stale content is still possible. However, for very
   busy sites this can be an effective, low-overhead strategy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flushing content when it changes&lt;/strong&gt;. This strategy sets very long time to live in the cache, months or even years. When
   content is updated the cache is informed and the new version fetched. This notification of new content could happen
   manually or, in the case of some CMSs, automatically. For example, Magento 2 and Drupal 8 have a built in framework
   for integrating caching providers to handle flushing when content/stock changes. This strategy ensures very high hit
   rates, but unless the flushing is accurate and fast it can result in stale content.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Security-Performance Integration&lt;/h2&gt;
&lt;p&gt;Modern &lt;a href="/learning/application-security/what-is-application-security-platform/"&gt;Application Security&lt;/a&gt; platforms like Peakhour combine caching with comprehensive security controls so performance optimisation does not weaken application protection:&lt;/p&gt;
&lt;h3&gt;Edge Security Processing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WAF/WAAP Integration&lt;/strong&gt;: Security rules are processed at the edge before content is cached, ensuring malicious requests never reach your origin&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bot Management&lt;/strong&gt;: Caching adapts based on traffic classification - legitimate users benefit from cached content whilst malicious bots are filtered out&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Protection&lt;/strong&gt;: Secure caching of API responses with appropriate security headers and access controls&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cache Security Controls&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Secure Purging&lt;/strong&gt;: Authorised cache invalidation through secure API endpoints with proper authentication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Classification&lt;/strong&gt;: Different caching policies for public, authenticated, and sensitive content&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Header Security&lt;/strong&gt;: Automatic injection of security headers (CSP, HSTS, etc.) into cached responses&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Secure dynamic &lt;a href="/products/advanced-caching/"&gt;content caching&lt;/a&gt; works best when performance optimisation and application security are handled together. By implementing caching within an Application Security Platform, organisations can achieve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Superior Performance&lt;/strong&gt;: Dramatic improvements in load times and server capacity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Protection against threats at the edge before they impact cached content&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational Efficiency&lt;/strong&gt;: Reduced origin server load whilst maintaining security posture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Optimisation&lt;/strong&gt;: Lower infrastructure costs through intelligent caching and edge processing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For modern applications and APIs, secure dynamic caching should form part of the performance and security strategy, improving the user experience whilst maintaining threat protection.&lt;/p&gt;</content><category term="Performance"></category><category term="Drupal"></category><category term="Caching"></category><category term="WordPress"></category><category term="Web Performance"></category><category term="CDN"></category><category term="Rate Limiting"></category></entry><entry><title>Are Australian Magento Stores Ready For Web Vitals?</title><link href="https://www.peakhour.io/blog/web-vitals-magento-australia/" rel="alternate"></link><published>2020-11-19T13:00:00+11:00</published><updated>2020-11-19T13:00:00+11:00</updated><author><name>AC</name></author><id>tag:www.peakhour.io,2020-11-19:/blog/web-vitals-magento-australia/</id><summary type="html">&lt;p&gt;Are Australian Magento stores prepared for the introduction of Core Web Vitals as a search signal? Read on to find out.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Google recently confirmed that the &lt;a href="/blog/web-vitals/"&gt;Core Web Vitals&lt;/a&gt; will be included as search signals from May 2021. This
means that, all else being equal, sites that score well on the Core Web Vitals are likely to rank ahead of those that don&amp;apos;t.&lt;/p&gt;
&lt;h2&gt;A quick refresher of Web Vitals&lt;/h2&gt;
&lt;p&gt;The Core Web Vitals consist of three metrics, chosen to measure the experience of browsing a website. Here they are, along
with the current thresholds for a 'Good', 'Needs Improvement', or 'Poor' rating:&lt;/p&gt;
&lt;div class="row" style="margin-bottom: 30px"&gt;
    &lt;div class="col-sm-4 text-center"&gt;
        &lt;img src="/static/images/blog/lcp.svg" alt="Largest Contenful Paint" style="max-width: 300px"/&gt;
    &lt;/div&gt;
    &lt;div class="col-sm-4 text-center"&gt;
        &lt;img src="/static/images/blog/fid.svg" alt="[First Input Delay](/solutions/use-case/improve-web-vitals/)" style="max-width: 300px"/&gt;
    &lt;/div&gt;
    &lt;div class="col-sm-4 text-center"&gt;
        &lt;img src="/static/images/blog/cls.svg" alt="Cumulative Layout Shift" style="max-width: 300px"/&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Web Vitals also defines several other metrics, including Time to First Byte (TTFB), and First Contentful Paint (FCP). While these
aren't 'core' metrics, they are useful for diagnosing where performance problems come from. The target TTFB
in the current version of &lt;a href="/blog/testing-sitespeed-lighthouse/"&gt;Google Lighthouse&lt;/a&gt; is listed as 100ms, while poor is 600ms.
FCP is good under 2s and poor over 4s.&lt;/p&gt;
&lt;h2&gt;How will Australian sites fare?&lt;/h2&gt;
&lt;p&gt;We asked a simple question: what percentage of Australian websites are ready for
Web Vitals as a search signal, and what percentage could lose ground? To answer it, we ran them through
our recently released &lt;a href="/pages/website-competitor-speed-test/"&gt;Website Speed Comparison&lt;/a&gt; tool, which gathers Web Vitals metrics
as part of its report.&lt;/p&gt;
&lt;h2&gt;Methodology&lt;/h2&gt;
&lt;p&gt;There are a lot of Australian websites, so we broke the analysis down by technology platform. We started with
online stores running Magento.&lt;/p&gt;
&lt;p&gt;We started with an initial list from BuiltWith of around 4000 domains. We then trimmed it down by removing development
and demo sites, and sites returning an error, leaving a total of 2998. The list includes some of the largest retailers in Australia,
including Harvey Norman, Sportsgirl, Philips and Dyson.&lt;/p&gt;
&lt;p&gt;We then ran our competitor report for &lt;strong&gt;&lt;em&gt;every one of them&lt;/em&gt;&lt;/strong&gt;. The report was run from our Sydney office over a business-class
internet connection. The test throttles the connection to simulate typical 4G mobile phone
speeds, and uses a mobile phone user agent/screen size to view the mobile version of the site. We did not throttle
CPU performance like Lighthouse does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; We're excluding First Input Delay in our results as Google defines First Input Delay (FID)
as a real user measurement (RUM). FID measures the time taken for the website to respond to the first
user interaction, such as clicking a link or button. Technically this interaction can happen any time after the first content
appears in the browser; in practice, most people won't click something until after a page is visually complete,
and this timing is highly variable. We do measure First Input Delay by simulating a click, but our interaction
happens soon after the FCP, while content is still loading. That would cause more sites to fail the metric
than would fail in real life, so we're excluding it from our calculations.&lt;/p&gt;
&lt;p&gt;On to the results. We did not expect strong numbers, but the results were still worse than expected.&lt;/p&gt;
&lt;h2&gt;The results&lt;/h2&gt;
&lt;p&gt;The first check was for the number of websites that achieve a good rating in any of the Web Vitals metrics.&lt;/p&gt;
&lt;table class="table"&gt;
    &lt;td colspan="5" style="text-align: center"&gt;
        &lt;em&gt;Percentage of sites that are 'Good'&lt;/em&gt;
    &lt;/td&gt;
&lt;tr&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;TTFB (&lt; 0.1s)&lt;/th&gt;
    &lt;th&gt;FCP (&lt; 1s)&lt;/th&gt;
    &lt;th&gt;LCP (&lt; 2.5s)&lt;/th&gt;
    &lt;th&gt;CLS (&lt;.1)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;th&gt;Result&lt;/th&gt;
    &lt;td&gt;99 (3.3%)&lt;/td&gt;
    &lt;td&gt;71 (2.37%)&lt;/td&gt;
    &lt;td&gt;254 (8.47%)&lt;/td&gt;
    &lt;td&gt;1074 (35.8%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;CLS was the strongest result, which isn't a surprise. The remaining results are not encouraging: only 8.5% pass LCP. Let's
see how many need improvement.&lt;/p&gt;
&lt;table class="table"&gt;
&lt;tr&gt;
    &lt;td colspan="5" style="text-align: center;"&gt;
        &lt;em&gt;Percentage of sites that 'Needs Improvement'&lt;/em&gt;
    &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;TTFB (&lt; 0.6s)&lt;/th&gt;
    &lt;th&gt;FCP (&lt; 2s)&lt;/th&gt;
    &lt;th&gt;LCP (&lt; 4s)&lt;/th&gt;
    &lt;th&gt;CLS (&lt; .25)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;th&gt;Result&lt;/th&gt;
    &lt;td&gt;383 (12.7%)&lt;/td&gt;
    &lt;td&gt;728 (24.3%)&lt;/td&gt;
    &lt;td&gt;470 (15.7%)&lt;/td&gt;
    &lt;td&gt;625 (20.8%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;An additional 15.7% beat the 4s cut-off for LCP. That still means that 3/4 of Australian Magento
stores take longer than 4s to visually load on a mobile device. Visualised, the numbers are not pretty.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/magento-web-vitals.svg" style="width: 100%"/&gt;&lt;/p&gt;
&lt;p&gt;Australian Magento sites are likely missing potential sales. Recent performance studies show:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The probability of a customer bouncing increases 90% if the page load time increases from 1s to 5s. &lt;em&gt;(source Google)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;A 100 millisecond delay in load time can hurt conversion rates by 7%. &lt;em&gt;(source Akamai)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The experience of our own &lt;a href="/case-studies/ecsso/"&gt;Magento 1 clients&lt;/a&gt; and &lt;a href="/case-studies/savvysupporter/"&gt;Magento 2 clients&lt;/a&gt;
backs this up: improving website speed affects conversions and revenue.&lt;/p&gt;
&lt;h2&gt;Sites that pass all criteria&lt;/h2&gt;
&lt;p&gt;Of our 2998 websites, we only found &lt;strong&gt;163&lt;/strong&gt; that &lt;a href="/learning/performance/how-to-pass-core-web-vitals/"&gt;pass Core&lt;/a&gt; Web Vital &lt;em&gt;'good'&lt;/em&gt; criteria. That's only &lt;strong&gt;5.5%&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If we again relax to include &lt;em&gt;'needs improvement'&lt;/em&gt; that list grows to &lt;strong&gt;520&lt;/strong&gt;, or &lt;strong&gt;17.3%&lt;/strong&gt; of sites tested.&lt;/p&gt;
&lt;h2&gt;How you can test your site&lt;/h2&gt;
&lt;p&gt;Google provides online analytics that you can query via BigQuery. If you want to reproduce the report this analysis
is based on and compare your website to your competitors, you can use the Peakhour.IO &lt;a href="/pages/website-competitor-speed-test/"&gt;Website Speed Comparison report&lt;/a&gt;.
We automatically discover your competitors, run them through Web Vitals, and graph the results.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If nothing changes, quite a few Australian Magento stores could lose search visibility in May 2021. The majority of sites don't
use Magento 2's ability to &lt;a href="/dynamic-content-caching/"&gt;cache dynamic pages&lt;/a&gt;, and if they do, they're often not
&lt;a href="/image-optimisation/"&gt;serving optimal images&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Core Web Vitals becomes a search signal next year. For Magento teams, it is time to get ready.&lt;/p&gt;</content><category term="Performance"></category><category term="Core Web Vitals"></category><category term="Magento"></category><category term="Web Performance"></category><category term="CDN"></category><category term="Drupal"></category></entry><entry><title>Website Performance testing with WebPageTest.org</title><link href="https://www.peakhour.io/blog/testing-website-speed-webpagetest/" rel="alternate"></link><published>2020-09-13T13:00:00+10:00</published><updated>2026-07-06T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2020-09-13:/blog/testing-website-speed-webpagetest/</id><summary type="html">&lt;p&gt;In this installment of our website performance series we're taking a look at webpagetest.org, one of the best tools you can use to analyse real world performance of your website.&lt;/p&gt;</summary><content type="html">&lt;p&gt;WebPageTest is one of our favourite tools for measuring website performance:
&lt;a href="https://www.webpagetest.org"&gt;webpagetest.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;WebPageTest is a web page performance testing tool developed by AOL and open-sourced in 2008. It produces its metrics
using real-world browsers to load the web page being tested. It's actively maintained by Google on GitHub, so you can download
and install it on your own server if you prefer. It is also free.&lt;/p&gt;
&lt;p&gt;The key advantage of WebPageTest is that tests can be run from locations around the world, using real browsers at actual
connection speeds. That lets you test performance where your users are, and see real load times rather than arbitrary
scores out of 100. You can run simple tests, advanced multi-step tests, video capture, content blocking, multi-site visual
comparisons, and traceroute testing.
While this is still a synthetic test, it is about as close to real-world performance measurement as you can get without using
RUM.&lt;/p&gt;
&lt;p&gt;Use WebPageTest when you need to understand why a page is slow. Use the &lt;a href="/blog/what-is-the-chrome-ux-report-crux/"&gt;Chrome UX Report&lt;/a&gt; when you need to know how real Chrome users experienced the page over time. Use Peakhour's &lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt; when you want a fast comparison between your domain and competitor domains using CrUX field data.&lt;/p&gt;
&lt;h2&gt;Running a Simple Test&lt;/h2&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest.jpg" alt="webpagetest home" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;To run a simple test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Visit &lt;a href="https://www.webpagetest.org"&gt;webpagetest.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Enter the URL of the page that you want to test in the 'Enter a Website URL' field. We usually enter only the domain
    name, with no www and no https:// at the front. This simulates someone typing your domain into a browser address bar
    and captures how much time any redirects add to the page load. Sometimes redirects can be very slow.&lt;/li&gt;
&lt;li&gt;Select the location of the test from the 'Test Location' drop down. Choose locations that reflect where your users
    are. &lt;em&gt;Note Australian locations are at the very bottom.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Choose the browser to run the test from. Chrome is the default.&lt;/li&gt;
&lt;li&gt;Press 'Start Test'&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Note there are advanced options but for simple testing you don't need to change these.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The test will perform three visits to the specified page, with the browser cache cleared in between each visit. This simulates
someone visiting your site for the first time, including the parts of the page load that only happen on an uncached visit.&lt;/p&gt;
&lt;h2&gt;Interpreting the results.&lt;/h2&gt;
&lt;h3&gt;A,B,C's&lt;/h3&gt;
&lt;p&gt;The results page has a summary section at the top giving you summary grades for several categories. Here's a sample report
using our website, peakhour.io. Take note of the sections at the bottom, because we'll refer to them later.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest-abc.jpg" alt="webpagetest performance grades" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;While this section isn't representative of performance it can still give you actionable information. Here's a summary of
the information (ignoring security since we're concerned with speed):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First Byte Time&lt;/strong&gt;: How long it takes the server to respond to the browser request with the first byte of information.
This is the same as the Web Vital Time To First Byte (TTFB).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep Alive Enabled&lt;/strong&gt;: A server option that leaves the connection from the browser to the server open for a short time,
usually a few seconds, after the server has finished transmitting a request. This allows the browser to reuse a connection
and saves time because it does not need to reconnect as often.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compress Transfer&lt;/strong&gt;: When files are transferred from the server they are compressed, usually via gzip, to make sure
transfer sizes are as small as possible. &lt;em&gt;It looks like we have a problem here.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compress Images&lt;/strong&gt;: Images are usually the largest part of a web page by transfer size. Making sure they're well
compressed is important for fast sites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cache Static Content&lt;/strong&gt;: Checks that static files, e.g. JavaScript, CSS, and images, have appropriate cache headers so your
browser doesn't re-fetch them every time it views a page. &lt;em&gt;We're developing our site so have caching turned off at the
moment, hence the bad score&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Effective Use of CDN&lt;/strong&gt;: Detects whether your website is using a CDN known by WebPageTest.&lt;/p&gt;
&lt;p&gt;This report points to problems with transfer compression and static content caching.&lt;/p&gt;
&lt;h3&gt;Performance Metrics (The Important Stuff)&lt;/h3&gt;
&lt;p&gt;The next section summarises the key performance metrics of the page load. Google's
&lt;a href="/blog/web-vitals/"&gt;web vitals&lt;/a&gt; are represented alongside lab metrics such as speed index, total blocking time, and
page weight.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest-summary.jpg" alt="webpagetest summary" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;Despite some poor marks in the grading section, the peakhour.io website loads very quickly. Its Largest
Contentful Paint (LCP) is less than 1s, well below Google's 2.5s target for a good result.
We've already covered them in our &lt;a href="/blog/web-vitals/"&gt;web vitals&lt;/a&gt;, which also defines ideal
values for each metric, so we won't cover them again here. One detail worth noting is that &lt;strong&gt;Total Blocking Time&lt;/strong&gt; is still a useful lab signal for main-thread blocking. It is not the same as &lt;strong&gt;Interaction to Next Paint&lt;/strong&gt;, because INP is measured from real user interactions in the field, but it helps identify JavaScript and third-party work that can harm responsiveness.&lt;/p&gt;
&lt;h3&gt;The Detail Section&lt;/h3&gt;
&lt;p&gt;Remember the sections immediately below the grade summaries? Now we're going to click on the &lt;strong&gt;'Details'&lt;/strong&gt; section. The
part we want to highlight here is the &lt;strong&gt;Waterfall&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest-waterfall-key.jpg" alt="webpagetest waterfall key" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;At the top of the waterfall chart is a colour key for reading the diagram. The key concepts are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dns (Dark Green)&lt;/strong&gt;: This measures the time it takes for the browser to look up the location of your server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;connect (Orange)&lt;/strong&gt;: This measures the time taken to establish the TCP connection to download a resource. It should only
appear on the first resource for a given host. Remember the keep-alives grade: if that is turned off, there will be
more connections here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ssl (Dark Purple)&lt;/strong&gt;: Any resources that are loading from a secure website will need to be processed as such – the
purple will signify how long it is taking to connect to that SSL item.&lt;/p&gt;
&lt;h4&gt;The Waterfall View&lt;/h4&gt;
&lt;p&gt;The waterfall is an easy-to-read view of how your website loads, with all
the resources listed in the order they're requested, along with the time taken to load each resource. You can click on
any resource to view the request/response headers, file size, protocol and more.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest-waterfall.jpg" alt="webpagetest waterfall" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;In the example above the first line has a yellow background, which signifies a redirect. The three lines towards the bottom
with red backgrounds signify 404 not found errors, which need to be fixed. The colourful vertical lines indicate
where major load events, like first paint and document loaded, happen.&lt;/p&gt;
&lt;p&gt;The other thing to look for is any resource that takes a long time to load. In our example the 2nd row, which
is the main HTML document, only took 149ms, which is fast. A lot of websites take 2-5s to load the main document,
putting the user experience under pressure before the rest of the page has started. The main concerns here are
rows 27 and 28: two SVG images that took around three quarters of a second to load.&lt;/p&gt;
&lt;h3&gt;The Performance Review Section&lt;/h3&gt;
&lt;p&gt;The final section we're going to cover is the &lt;strong&gt;Performance Review&lt;/strong&gt; section.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/webpagetest-details.jpg" alt="webpagetest details" width="100%"/&gt;&lt;/p&gt;
&lt;p&gt;This section gives more detail for the performance grades at the top of the report. In the earlier example Peakhour.io
scored poorly for &lt;strong&gt;Compress Transfer&lt;/strong&gt; and now we can see why: we're not compressing SVG images, something that can
save 234kb of file downloads.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;WebPageTest gives you enough detail to see where a page is losing time, not just whether it passed a headline score.
There is more in the tool than we've covered here, but this is enough to test a website, read the main report sections,
and identify practical issues to fix. If you are starting with a market view rather than a single waterfall, compare your field data first with the &lt;a href="/pages/website-competitor-speed-test/"&gt;website speed comparison tool&lt;/a&gt;, then come back to WebPageTest for the diagnostic pass.&lt;/p&gt;</content><category term="Performance"></category><category term="Web Performance"></category><category term="Core Web Vitals"></category><category term="Analytics"></category><category term="Caching"></category><category term="Bot Management"></category><category term="Drupal"></category></entry><entry><title>Application Performance Optimisation</title><link href="https://www.peakhour.io/blog/introduction-to-website-performance-testing/" rel="alternate"></link><published>2020-09-12T13:00:00+10:00</published><updated>2020-09-12T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2020-09-12:/blog/introduction-to-website-performance-testing/</id><summary type="html">&lt;p&gt;A practical primer for finding where website requests lose time, from cache state and origin work to browser rendering.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Website performance testing is most useful when it explains where a request lost time. A single score can tell you that a page is slow, but it rarely tells the site team what to change next. The better starting point is the request path: where the visitor is, where the origin is, whether the response was cached, how much the browser had to download, and what work blocked rendering or interaction.&lt;/p&gt;
&lt;p&gt;This article is a primer for that diagnosis. Start with a representative page, test it from a location that matches your users, then read the evidence in order.&lt;/p&gt;
&lt;h2&gt;Start With the First Request&lt;/h2&gt;
&lt;p&gt;The browser cannot render a page until it receives the main HTML document. That first request includes DNS lookup, TCP connection setup, TLS negotiation, any redirect, cache handling, origin processing, and the first byte coming back. When the origin is far away, latency compounds. Peakhour's performance material uses simple Australian examples: Sydney to Melbourne is about 5 ms one way, Sydney to Perth about 25 ms, and Sydney to San Francisco about 75 ms. Those numbers become larger when a page load needs several round trips.&lt;/p&gt;
&lt;p&gt;This is why the main document matters in &lt;a href="/blog/testing-website-speed-webpagetest/"&gt;WebPageTest&lt;/a&gt;. In one Peakhour example, the main HTML document took 149 ms, which is fast. Many sites take 2 to 5 seconds before the browser receives that document. If the main response is already slow, the page has little chance of a good Largest Contentful Paint because the browser has not yet discovered the resources needed to paint the largest content.&lt;/p&gt;
&lt;p&gt;Cache state is part of the same first request. A cache hit at the edge should look different from a miss that forwards to origin. Headers such as &lt;code&gt;Cache-Status&lt;/code&gt; can show hit, miss, TTL, stored state, cache key, and collapsed request behaviour. Without that evidence, teams often guess whether a slow page is caused by the CDN, the origin, the application, or a cache-bypass rule.&lt;/p&gt;
&lt;h2&gt;Use WebPageTest for the Waterfall&lt;/h2&gt;
&lt;p&gt;WebPageTest is useful because it loads the page in a real browser and lets you choose test locations and connection profiles. That matters for Australian sites because a test from the wrong continent can make a local problem look worse or hide a regional problem from view.&lt;/p&gt;
&lt;p&gt;The waterfall is the main working view. Read the first rows before looking at the rest of the page. A redirect on the first request adds delay before the useful page is even requested. Long DNS, connect, or TLS blocks point to connection setup or third-party domains. A long wait on the main document points to cache miss, origin processing, or backend work. Large downloads point to page weight. Red rows show broken requests, and the domain view shows how many external services the browser had to contact.&lt;/p&gt;
&lt;p&gt;The filmstrip is just as important. It shows what the user saw while the waterfall was happening. If the HTML arrived quickly but the filmstrip stayed blank, the issue may be render-blocking CSS, JavaScript, fonts, or a hero image. If the page starts to render quickly but then jumps, you are looking at layout stability, not just network speed.&lt;/p&gt;
&lt;h2&gt;Use Lighthouse for Browser Work&lt;/h2&gt;
&lt;p&gt;&lt;a href="/blog/testing-sitespeed-lighthouse/"&gt;Lighthouse&lt;/a&gt; is good at surfacing work inside the browser. It runs a controlled test, reports Core Web Vitals-related metrics, and lists opportunities such as eliminating render-blocking resources, reducing unused CSS, deferring JavaScript, compressing assets, and reducing main-thread work.&lt;/p&gt;
&lt;p&gt;Treat the score as a prompt, not a verdict. Lighthouse scores can vary between runs because the local CPU, network conditions, and server response can vary. The useful part is the diagnostics. If Lighthouse points to a font, a theme stylesheet, or a third-party script that delays rendering, compare that with the WebPageTest waterfall and filmstrip. If both tools point to the same resource, you have a stronger case for change.&lt;/p&gt;
&lt;h2&gt;Connect Metrics to Causes&lt;/h2&gt;
&lt;p&gt;Core Web Vitals are easier to act on when each metric is tied to the part of the path it describes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Where to look&lt;/th&gt;
&lt;th&gt;Likely next question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;High TTFB&lt;/td&gt;
&lt;td&gt;WebPageTest first row, cache headers, origin logs&lt;/td&gt;
&lt;td&gt;Did the request hit cache, miss to origin, or wait on application work?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow FCP&lt;/td&gt;
&lt;td&gt;Waterfall, filmstrip, render-blocking resources&lt;/td&gt;
&lt;td&gt;Did HTML, CSS, or synchronous JavaScript stop the first paint?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow LCP&lt;/td&gt;
&lt;td&gt;Main document timing, hero media, image weight&lt;/td&gt;
&lt;td&gt;Was the largest element discovered and delivered early enough?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High CLS&lt;/td&gt;
&lt;td&gt;Filmstrip, image dimensions, injected banners, fonts&lt;/td&gt;
&lt;td&gt;Did content move after the visitor started reading?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poor INP&lt;/td&gt;
&lt;td&gt;Lighthouse main-thread diagnostics, third-party scripts&lt;/td&gt;
&lt;td&gt;Is JavaScript delaying the next paint after interaction?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High page weight&lt;/td&gt;
&lt;td&gt;WebPageTest content breakdown&lt;/td&gt;
&lt;td&gt;Are images, JavaScript, fonts, or unused assets carrying the load?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Too many domains&lt;/td&gt;
&lt;td&gt;WebPageTest domains view&lt;/td&gt;
&lt;td&gt;Which third-party services are adding connection setup and blocking work?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclear cache behaviour&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cache-Status&lt;/code&gt;, CDN analytics, debug headers&lt;/td&gt;
&lt;td&gt;Which paths are hits, misses, bypasses, stale responses, or collapsed misses?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This table is not a checklist to run forever. It is a triage map. Pick the symptom that matches the page, then follow the evidence to the next request-path decision.&lt;/p&gt;
&lt;h2&gt;Diagnose the Common Failures&lt;/h2&gt;
&lt;p&gt;Latency is the first failure to rule in or out. If the origin is in the United States and most customers are in Australia, the page can lose time before WordPress, Magento, Drupal, or a custom application has done any work. Edge caching helps when the response is safe to reuse, because the first byte can come from a location closer to the visitor.&lt;/p&gt;
&lt;p&gt;Page weight is the next obvious drag. WebPageTest's content breakdown shows whether images, JavaScript, CSS, fonts, or other assets dominate the transfer. Unoptimised images are common, especially on CMS and ecommerce sites. Image variants in AVIF or WebP, responsive sizes, compression, and stable dimensions can improve both download time and LCP.&lt;/p&gt;
&lt;p&gt;Render-blocking resources explain why a page can download quickly and still look blank. CSS and synchronous JavaScript in the head can stop the browser from painting. Large theme bundles and plugin scripts often include code that is not needed on the current page. The fix might be removing unused files, deferring non-critical scripts, splitting code by route, self-hosting critical third-party resources, or using &lt;code&gt;preconnect&lt;/code&gt; only where the first view depends on a third-party domain.&lt;/p&gt;
&lt;p&gt;Third-party domains need a sober review. Analytics, marketing tags, chat widgets, social embeds, payment scripts, and fonts can all be legitimate. They can also add DNS, TCP, TLS, download, parsing, and main-thread cost. If a third-party script is not needed for the first view, it should not block the first view.&lt;/p&gt;
&lt;h2&gt;Where Peakhour Evidence Fits&lt;/h2&gt;
&lt;p&gt;Peakhour performance work should show both user experience and origin relief. For caching, that means hit ratio, miss causes, &lt;code&gt;Cache-Status&lt;/code&gt;, purge state, cache keys, shielded misses, collapsed requests, and origin fetch volume. For images, it means original size, transformed size, selected format, responsive variant, and cache hit state. For Core Web Vitals, it means LCP, CLS, INP, TTFB, page weight, and the same page tested before and after changes.&lt;/p&gt;
&lt;p&gt;Security belongs in the performance review when it changes the request path. Bot filtering, WAF rules, rate limits, and login protection can reduce origin load by stopping abusive or noisy traffic before PHP, database, search, or API work begins. They can also create friction if rules are too broad. Measure the edge decision, the latency, the origin effect, and the false-positive risk. Do not treat "security enabled" as a generic performance story.&lt;/p&gt;
&lt;p&gt;Good performance testing ends with a specific change to validate: cache this public route, purge it by tag, move this script later, replace this image variant, reduce these third-party domains, protect this login path, or investigate this slow origin query. Then run the same test again from the same location and compare the evidence.&lt;/p&gt;</content><category term="Performance"></category><category term="Application Security"></category><category term="DevSecOps"></category><category term="Drupal"></category><category term="DDoS"></category><category term="Threat Detection"></category><category term="Rate Limiting"></category></entry><entry><title>Core Web Vitals Optimisation</title><link href="https://www.peakhour.io/blog/web-vitals/" rel="alternate"></link><published>2020-09-11T13:00:00+10:00</published><updated>2020-09-11T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2020-09-11:/blog/web-vitals/</id><summary type="html">&lt;p&gt;Comprehensive guide to Core Web Vitals optimisation with integrated security. Learn how modern application security platforms improve both performance metrics and protection whilst boosting search rankings and user experience.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A good &lt;a href="/learning/crux-chrome-user-experience/"&gt;user experience&lt;/a&gt; matters for any website.
It matters to users, and Google also
measures aspects of the user experience when ranking your website in its organic results.&lt;/p&gt;
&lt;p&gt;Google ranks websites and pages in its search results using 'search signals'.
These signals include the quality of content on a page, the number of sites linking to that page, and the
experience of the user browsing the site. Factors that make up a great experience include ease of use,
accessibility, speed and responsiveness.&lt;/p&gt;
&lt;p&gt;The set of search signals that Google uses to measure user experience beyond a page's content value is called
&lt;a href="https://developers.google.com/search/docs/guides/page-experience"&gt;'Page experience'&lt;/a&gt;. Google updated
the Page Experience signals with metrics called Web Vitals.&lt;/p&gt;
&lt;p&gt;For years, performance testing used different proxies to determine whether a website was fast. Browser events like
'Page Load' or 'Dom Load' were used before it was realised that these do not necessarily reflect what an end user
experiences. Testing then fragmented, with different teams focusing on the measures they considered important. Web Vitals is
Google's performance testing initiative to provide &lt;strong&gt;&lt;em&gt;"unified guidance for quality signals
that they believe are essential to delivering a great user experience on the web."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The 'Core' Web Vitals join the existing search signals: mobile-friendliness, safe-browsing, HTTPS, and no intrusive
interstitials as seen in the graphic below from Google.&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/images/blog/page-experience-signals.jpg" width="100%" alt="Page Experience Signals"/&gt;&lt;/p&gt;
&lt;h2&gt;Understanding the Core Web Vitals&lt;/h2&gt;
&lt;div class="mb-10 sm:grid sm:grid-cols-3"&gt;
    &lt;div class="text-center"&gt;
        &lt;img src="/static/images/blog/lcp.svg" alt="Largest Contenful Paint" style="max-width: 300px"/&gt;
    &lt;/div&gt;
    &lt;div class="text-center"&gt;
        &lt;img src="/static/images/blog/fid.svg" alt="First Input Delay" style="max-width: 300px"/&gt;
    &lt;/div&gt;
    &lt;div class="text-center"&gt;
        &lt;img src="/static/images/blog/cls.svg" alt="Cumulative Layout Shift" style="max-width: 300px"/&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Before diving in, it is worth noting that the metrics that make up Core Web Vitals are expected to
&lt;a href="https://web.dev/vitals/#evolving-web-vitals"&gt;evolve&lt;/a&gt; over time. Google states that
&lt;strong&gt;&lt;em&gt;"these signals are not perfect and future improvements or additions should be expected."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Largest Contentful Paint (LCP)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://web.dev/lcp/"&gt;Largest Contentful Paint (LCP)&lt;/a&gt; measures when the "largest", or "main" piece of content has completely
loaded and is visible, usually a hero image. Content refers to text, foreground images, background images, and elements. LCP
complements First Contentful Paint (FCP), a metric that marks the initial web page loading experience. LCP calculates
how quickly a user can see page content. Scores below 2.5 seconds are considered in the 'Good' range.&lt;/p&gt;
&lt;h3&gt;First Input Delay (FID) (Due to be replaced March 2024 by &lt;a href="/blog/interaction-to-next-paint/"&gt;Interaction to Next Paint (INP)&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://web.dev/lcp/"&gt;First Input Delay (FID)&lt;/a&gt; measures page interactivity: how long it takes for a page
to respond to input from the user, such as a key press or a mouse click. Low FID
scores ensure pages are usable. FID is a real-world metric that cannot be measured in the lab. The Total Blocking
Time (TBT) metric found in Lighthouse is lab-measurable and correlates with FID to simulate real-world interactivity.&lt;/p&gt;
&lt;h3&gt;Cumulative Layout Shift (CLS)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://web.dev/cls/"&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; measures visual stability while the page is loading. This means
that once displayed, a piece of content stays where it is; it does not jump around the screen as other content
loads. Lower CLS scores mean that users are not experiencing unnecessary
content shifts. CLS scores below 0.10 are 'Good'.&lt;/p&gt;
&lt;h2&gt;Understanding the rest of the Web Vitals&lt;/h2&gt;
&lt;p&gt;The Web Vitals metrics that are not part of the Core Web Vitals are &lt;strong&gt;&lt;em&gt;Time to First Byte
(TTFB)&lt;/em&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;em&gt;First Contentful Paint (FCP)&lt;/em&gt;&lt;/strong&gt;. They provide additional ways to
improve a web user’s experience, and help diagnose specific issues, either in the lab or in the field.&lt;/p&gt;
&lt;h3&gt;Time to First Byte (TTFB)&lt;/h3&gt;
&lt;p&gt;Time to First Byte is the time it takes for a user's browser to receive the first byte of page content.&lt;/p&gt;
&lt;h3&gt;First Contentful Paint (FCP)&lt;/h3&gt;
&lt;p&gt;First Contentful Paint (FCP) &lt;strong&gt;&lt;em&gt;"measures the point from when a web page starts loading to when ANY
content starts rendering on screen."&lt;/em&gt;&lt;/strong&gt; The term 'Content' means text, images, &amp;lt;svg&amp;gt; elements,
or non-white &amp;lt;canvas&amp;gt; elements. Just remember that FCP can be triggered
very early in the page load, but may not necessarily deliver any visible content or information to the user.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Web Vitals are Google's attempt to unify the metrics webmasters use to measure the Page Experience of their
site. With Core Vitals included in the ranking signals, site owners can see which performance measures Google treats as
important. Measuring performance is the topic of our next few posts.&lt;/p&gt;</content><category term="Performance"></category><category term="Core Web Vitals"></category><category term="SEO"></category><category term="Web Performance"></category><category term="Analytics"></category><category term="Drupal"></category><category term="Caching"></category></entry><entry><title>WordPress Performance Optimisation</title><link href="https://www.peakhour.io/blog/wordpress-performance-optimisation-security-cdn/" rel="alternate"></link><published>2019-05-27T13:00:00+10:00</published><updated>2019-05-27T13:00:00+10:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2019-05-27:/blog/wordpress-performance-optimisation-security-cdn/</id><summary type="html">&lt;p&gt;How to speed up WordPress by separating public cacheable pages from private, expensive, and abused request paths.&lt;/p&gt;</summary><content type="html">&lt;p&gt;WordPress speed problems are usually request-path problems. A public article, product page, or campaign landing page should not make PHP, plugins, and the database rebuild the same HTML for every visitor. A login attempt, checkout session, admin request, or private API call should not be treated like public content just because the site is under load.&lt;/p&gt;
&lt;p&gt;The job is to separate those paths clearly. Cache what can be reused, protect the routes that are expensive or abused, and keep enough evidence to prove that the change improved the visitor experience without hiding origin risk.&lt;/p&gt;
&lt;h2&gt;Start With Public Page Caching&lt;/h2&gt;
&lt;p&gt;Most WordPress sites have a large set of pages that are dynamic only because WordPress generated them. The content itself is public and often identical for many visitors: posts, pages, category archives, product listings, campaign pages, media assets, CSS, and JavaScript. These are the paths where full-page caching and edge delivery can change the result quickly.&lt;/p&gt;
&lt;p&gt;When a public page is served from cache, the browser avoids the long trip to origin and the origin avoids running WordPress for a repeat response. That can move Time to First Byte and Largest Contentful Paint in the right direction before anyone touches the theme. Peakhour's older full-page caching examples showed the practical size of this change: a Magento main document fell from 2.07 seconds before caching to 82 ms after caching. WordPress sites have different internals, but the same pattern applies when anonymous pages are safe to reuse.&lt;/p&gt;
&lt;p&gt;The cache policy should be route-aware, not blanket. A simple operating model looks like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WordPress path&lt;/th&gt;
&lt;th&gt;Delivery stance&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Posts, pages, category pages, campaign pages&lt;/td&gt;
&lt;td&gt;Cache publicly with tags and purge controls&lt;/td&gt;
&lt;td&gt;Confirm logged-out content is shared and fresh after publishing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Featured images, media library files, theme assets&lt;/td&gt;
&lt;td&gt;Cache and optimise variants&lt;/td&gt;
&lt;td&gt;Track image weight, format, dimensions, and cache hit state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WooCommerce product and category pages&lt;/td&gt;
&lt;td&gt;Cache when no cart/session dependency changes the response&lt;/td&gt;
&lt;td&gt;Keep stock, price, and promotion purges tied to content changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cart, checkout, account, previews, admin&lt;/td&gt;
&lt;td&gt;Bypass shared cache&lt;/td&gt;
&lt;td&gt;Preserve session privacy and correctness.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wp-login.php&lt;/code&gt;, &lt;code&gt;/wp-admin/&lt;/code&gt;, &lt;code&gt;xmlrpc.php&lt;/code&gt;, sensitive plugin endpoints&lt;/td&gt;
&lt;td&gt;Protect and rate-limit before origin&lt;/td&gt;
&lt;td&gt;Keep noisy automation away from PHP workers and admin paths.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Publishing Should Not Clear the Whole Site&lt;/h2&gt;
&lt;p&gt;Caching WordPress is easy until someone edits content. Clearing the whole cache after every post update is safe in the narrow sense, but it damages hit ratio and pushes avoidable traffic back to origin. During a busy publishing period or campaign, that can make the site slower exactly when fresh content is being promoted.&lt;/p&gt;
&lt;p&gt;Cache tags are a better fit. Tags label cached responses by the content or template they depend on, so a post update can purge the post, related archives, and affected modules without invalidating unrelated pages. The updated Peakhour WordPress plugin generates cache tags automatically and sends purge requests when content changes in the WordPress admin. That lets teams set longer cache lifetimes for public content while still publishing with confidence.&lt;/p&gt;
&lt;p&gt;This is where &lt;a href="/products/advanced-caching/"&gt;advanced caching&lt;/a&gt; becomes operational rather than theoretical. The site team can review which tags were purged, which routes stayed cached, and whether the next request was a hit, miss, or stored response.&lt;/p&gt;
&lt;h2&gt;Images and Browser Work Still Matter&lt;/h2&gt;
&lt;p&gt;Page caching improves the start of the request path. It does not fix a 3 MB hero image, missing image dimensions, unused CSS, or a third-party script that blocks rendering. WordPress themes and plugin stacks often ship CSS and JavaScript for features that are not used on the current page. The browser still has to download and parse that code before it can paint useful content.&lt;/p&gt;
&lt;p&gt;Use Lighthouse to find render-blocking resources and main-thread pressure. Use WebPageTest to see whether the main HTML arrived quickly but the filmstrip still stayed blank while CSS, fonts, scripts, or images loaded. Those are different failures, and they need different fixes.&lt;/p&gt;
&lt;p&gt;For images, the best results usually come from serving the right variant rather than only compressing the original. &lt;a href="/products/image-optimisation-and-transformation/"&gt;Peakhour image optimisation&lt;/a&gt; can generate AVIF or WebP outputs, choose responsive sizes, and cache the resulting variants. That helps LCP when the largest visible element is an image, and it helps CLS when dimensions are kept stable.&lt;/p&gt;
&lt;p&gt;For CSS and JavaScript, remove unused files where possible, defer non-critical scripts, self-host critical third-party assets when practical, and reserve &lt;code&gt;preconnect&lt;/code&gt; for third-party domains that genuinely affect the first view. Moving every script later can break dependencies, so test the actual page type rather than applying a generic rule across the whole theme.&lt;/p&gt;
&lt;h2&gt;Protect Expensive WordPress Paths&lt;/h2&gt;
&lt;p&gt;Performance and security meet at origin capacity. Login floods, XML-RPC abuse, aggressive crawlers, scraper traffic, and noisy plugin endpoints can consume PHP workers and database connections that should be serving real visitors. If those requests are filtered only after WordPress has loaded, the site can look like it has a speed problem when it really has an unsorted traffic problem.&lt;/p&gt;
&lt;p&gt;For WordPress, protection should be specific. &lt;code&gt;wp-login.php&lt;/code&gt;, &lt;code&gt;/wp-admin/&lt;/code&gt;, &lt;code&gt;xmlrpc.php&lt;/code&gt;, the REST API, and plugin-specific endpoints should have their own bot, WAAP, and rate-limit policy. WooCommerce needs separate handling again: public catalogue pages can often be cached, but cart, checkout, account, and payment paths must stay dynamic and private.&lt;/p&gt;
&lt;p&gt;This does not mean putting heavy checks in front of every visitor. It means making edge decisions before origin work: allow clean public page requests, serve cache hits, challenge or rate-limit suspicious login traffic, bypass cache for private sessions, and log what happened.&lt;/p&gt;
&lt;h2&gt;Measure the Outcome&lt;/h2&gt;
&lt;p&gt;The evidence should line up across tools. WebPageTest should show a faster main document on cache hits, fewer slow origin fetches, and a waterfall where critical resources are visible early. Lighthouse should show fewer render-blocking opportunities and less main-thread pressure. Core Web Vitals should move where the page had the relevant bottleneck: LCP for slow HTML or heavy hero media, CLS for unstable layout, and INP for JavaScript and interaction work.&lt;/p&gt;
&lt;p&gt;Peakhour evidence should add the delivery side: cache hit ratio, miss causes, purge state, &lt;code&gt;Cache-Status&lt;/code&gt;, image savings, shielded misses, blocked login or XML-RPC abuse, and origin request volume. That combination tells the site team whether WordPress is faster because visitors received lighter pages from the edge, because abusive traffic stopped draining origin, or because browser work was reduced.&lt;/p&gt;
&lt;p&gt;Fast WordPress is not a plugin list. It is a set of clear route decisions backed by before-and-after measurements.&lt;/p&gt;</content><category term="Performance"></category><category term="WordPress"></category><category term="Drupal"></category><category term="Core Web Vitals"></category><category term="Rate Limiting"></category><category term="Web Performance"></category><category term="Application Security"></category></entry><entry><title>Boost Your Website Speed with Full Page Caching</title><link href="https://www.peakhour.io/blog/magento-1-plugin/" rel="alternate"></link><published>2019-05-10T13:00:00+10:00</published><updated>2023-11-02T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2019-05-10:/blog/magento-1-plugin/</id><summary type="html">&lt;p&gt;Our Magento 1 plugin is now available in the Magento Store. Discover how it improves website performance and capability.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/blog/opencart/opencart-3-caching-plugin/"&gt;Full page&lt;/a&gt; caching is not a cosmetic optimisation. Users expect quick load times, and search engines reward fast websites with higher rankings. Full page caching supports both by storing fully rendered HTML pages, so the server does not have to rebuild the page from scratch for each visitor.&lt;/p&gt;
&lt;h2&gt;How Full Page Caching Enhances Performance and Capability&lt;/h2&gt;
&lt;p&gt;By caching full HTML pages, you create a snapshot of a page at a particular moment. When a user requests that page, the server can deliver the snapshot instead of generating the page again. This saves compute and database resources, allowing your server to handle more users simultaneously. The result is straightforward: faster load times for users and less strain on the server.&lt;/p&gt;
&lt;h3&gt;Real-World Impact&lt;/h3&gt;
&lt;p&gt;Our early adopters have reported clear improvements in &lt;a href="/blog/wordpress-plugin/"&gt;website speed&lt;/a&gt; and server performance. One client saw a 40% reduction in server load and a 20% increase in page load speed. Those results point to a better user experience and potentially higher conversion rates.&lt;/p&gt;
&lt;h2&gt;Challenges with Magento 1 and How We Solved Them&lt;/h2&gt;
&lt;p&gt;Magento 1 presents specific challenges for full &lt;a href="/blog/opencart-3-plugin/"&gt;page caching&lt;/a&gt;, and our plugin handles them directly.&lt;/p&gt;
&lt;h3&gt;Mini Cart Issue&lt;/h3&gt;
&lt;p&gt;The mini cart needs to show real-time data, which makes it a barrier to full page caching. Our plugin uses targeted AJAX calls to fetch this data only when required, reducing unnecessary load on the server.&lt;/p&gt;
&lt;h3&gt;Form Key Problem&lt;/h3&gt;
&lt;p&gt;Magento 1 uses form keys to prevent CSRF attacks, but those keys make caching difficult. Our plugin replaces form keys with a strict referrer check, which is secure and cache-friendly.&lt;/p&gt;
&lt;h3&gt;Additional Features&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automatic Expires Headers&lt;/strong&gt;: You do not need to manually set expiration times for your cache; our plugin does it for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache Tagging&lt;/strong&gt;: This allows for precise cache management. When you update a product, only the relevant cached pages are flushed, keeping the process efficient.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The plugin is available for download, with the aim of improving performance and increasing server capability on Magento 1 stores.&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="Web Performance"></category><category term="Drupal"></category><category term="WordPress"></category><category term="CDN"></category><category term="Core Web Vitals"></category></entry><entry><title>Boost Your Website Speed with Peakhour's Full Page Caching</title><link href="https://www.peakhour.io/blog/wordpress-plugin/" rel="alternate"></link><published>2019-04-02T13:00:00+11:00</published><updated>2019-04-02T13:00:00+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2019-04-02:/blog/wordpress-plugin/</id><summary type="html">&lt;p&gt;Experience a significant boost in website speed and performance with Peakhour's Full Page Caching feature, now easily accessible through our Wordpress plugin.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Why Full Page Caching Matters&lt;/h2&gt;
&lt;p&gt;Site speed affects user experience, conversion, and server load. Slow pages can increase bounce rates and cost revenue. &lt;a href="/blog/opencart/opencart-3-caching-plugin/"&gt;Full Page&lt;/a&gt; Caching improves response times by storing and serving static versions of dynamic pages. This reduces load on your server and gives visitors quicker page loads.&lt;/p&gt;
&lt;h2&gt;How Peakhour Optimises Your Site&lt;/h2&gt;
&lt;p&gt;Peakhour helps speed up and secure your website with a DNS change. Unlike basic caching solutions, Peakhour serves these static pages from its global ANYCast network. Your site's visitors download content from a server close to them, reducing latency and speeding up downloads.&lt;/p&gt;
&lt;h2&gt;Peakhour's Transparent Caching and Delivery&lt;/h2&gt;
&lt;p&gt;In addition to Full Page Caching, Peakhour can act as a transparent delivery and cache layer. It caches and optimises static assets like CSS, JavaScript, and images. You don't need to make any other changes to your site; a DNS change is all it takes.&lt;/p&gt;
&lt;h2&gt;Wordpress Plugin for Easy Integration&lt;/h2&gt;
&lt;p&gt;During our beta phase, many clients used early versions of our WordPress plugin. The plugin integrates with Peakhour's API to automate content flushing when you make edits in the WordPress admin panel. This simplifies publishing and allows you to set longer lifetimes for your dynamic content in our global cache.&lt;/p&gt;
&lt;h2&gt;Improved Performance Metrics&lt;/h2&gt;
&lt;p&gt;With Peakhour, sites can see faster download times, a higher cache hit rate, and reduced load on the origin server. Site owners get lower origin demand; visitors get faster pages.&lt;/p&gt;</content><category term="CMS"></category><category term="Caching"></category><category term="Web Performance"></category><category term="WordPress"></category><category term="Drupal"></category><category term="CDN"></category><category term="Rate Limiting"></category></entry></feed>