requestmetrics.com.rss.xml - sfeed_tests - sfeed tests and RSS and Atom files
 (HTM) git clone git://git.codemadness.org/sfeed_tests
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       requestmetrics.com.rss.xml (187688B)
       ---
            1 <?xml version="1.0" encoding="UTF-8"?>
            2 <rss version="2.0"
            3   xmlns:atom="http://www.w3.org/2005/Atom"
            4   xmlns:content="http://purl.org/rss/1.0/modules/content/"
            5   xmlns:media="http://search.yahoo.com/mrss/"
            6   xmlns:webfeeds="http://webfeeds.org/rss/1.0">
            7   <channel>
            8     <title>Web Performance | Request Metrics</title>
            9     <link rel="alternate">https://requestmetrics.com/web-performance/</link>
           10     <atom:link rel="self" href="https://requestmetrics.com/web-performance/feed.xml" type="application/rss+xml" />
           11     <description>Learn about performance on the web with tutorials on the concepts, tools, and tactics to make your site faster.</description>
           12     <category>Web Performance</category>
           13     <image>
           14       <url>https://requestmetrics.com/assets/images/request_metrics_logo_rss.png</url>
           15       <title>Web Performance | Request Metrics</title>
           16       <link>https://requestmetrics.com/web-performance/</link>
           17     </image>
           18     <webfeeds:cover image="https://requestmetrics.com/assets/images/share.min.png" />
           19     <webfeeds:icon>https://requestmetrics.com/assets/images/favicon/apple-touch-icon.png</webfeeds:icon>
           20     <webfeeds:logo>https://requestmetrics.com/assets/images/request_metrics_logo.svg</webfeeds:logo>
           21     <webfeeds:accentColor>4879D9</webfeeds:accentColor>
           22     <webfeeds:related layout="card" target="browser"/>
           23     <webfeeds:analytics id="UA-42539664-5" engine="GoogleAnalytics" />
           24     <language>en-us</language>
           25     <managingEditor>hello@requestmetrics.com (Request Metrics)</managingEditor>
           26     <webMaster>hello@requestmetrics.com (Request Metrics)</webMaster>
           27     <copyright>2019-2021 TrackJS LLC. All rights reserved.</copyright>
           28     <pubDate>Wed, 15 Dec 2021 15:23:05 +0000</pubDate>
           29     <lastBuildDate>Wed, 15 Dec 2021 15:23:05 +0000</lastBuildDate>
           30 
           31     
           32     
           33     
           34     
           35     <item>
           36       <title>HTTP/3 is Fast</title>
           37       <link>https://requestmetrics.com/web-performance/http3-is-fast</link>
           38       <guid isPermaLink="true">https://requestmetrics.com/web-performance/http3-is-fast</guid>
           39       <description>
           40 HTTP/3 is here, and it’s a big deal for web performance.  See just how much faster it makes websites!
           41 
           42 </description>
           43       <media:content url="https://requestmetrics.com/assets/images/webperf/http3/http3-teaser-2000.png" medium="image" type="image/png" height="350" width="700" />
           44       <enclosure url="https://requestmetrics.com/assets/images/webperf/http3/http3-teaser-2000.png" type="image/png" />
           45       <content:encoded><![CDATA[
           46         <div><img src="https://requestmetrics.com/assets/images/webperf/http3/http3-teaser-2000.png" alt="HTTP/3 is Fast" class="webfeedsFeaturedVisual" /></div>
           47         
           48 <p>HTTP/3 is here, and it’s a big deal for web performance.  See just how much faster it makes websites!</p>
           49 
           50 <!--more-->
           51 
           52 <p>Wait, wait, wait, what happened to HTTP/2?  Wasn’t that all the rage only a few short years ago?  It sure was, but there were some <a href="https://en.wikipedia.org/wiki/HTTP/2#Criticisms">problems</a>. To address them, there’s a <a href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html">new version</a> of the venerable protocol working its way through the standards track.</p>
           53 
           54 <p>Ok, but does HTTP/3 actually make things faster?  It sure does, and we’ve got the benchmarks to prove it.</p>
           55 
           56 <h2 id="a-quick-preview">A Quick Preview</h2>
           57 <p>Before we get too deep in the details, let’s look at a quick preview of the benchmark results.  In the charts below the same browser was used to request the same site, over the same network, varying only the HTTP protocol in-use.  Each site was retrieved 20 times and the response time measured via the performance API. (More details on <a href="#benchmarking-http3">benchmark methodology</a> later)</p>
           58 
           59 <p>You can clearly see the performance improvement as each new version of the HTTP protocol is used:</p>
           60 
           61 <figure class="wide flex">
           62   <div class="image-wrap-3x wide-wrap">
           63     <img src="https://requestmetrics.com/assets/images/webperf/http3/ny-all-protocols.png" width="1144" loading="lazy" height="353" alt="Comparing the three HTTP protocol versions when loading pages from NY" />
           64   </div>
           65 </figure>
           66 
           67 <p>And the changes become even more pronounced when requesting resources over larger geographic distances and less reliable networks.</p>
           68 
           69 <p>But before we can get fully in to all the HTTP/3 benchmark minutiae, a little context is required.</p>
           70 
           71 <h2 id="a-brief-history-of-http">A Brief History of HTTP</h2>
           72 
           73 <p>The <a href="https://datatracker.ietf.org/doc/html/rfc1945">first official version</a> of HTTP (Hypertext Transfer Protocol 1.0) was finalized in 1996.  There were some practical issues and parts of the standard that needed updating, so <a href="https://datatracker.ietf.org/doc/html/rfc2068">HTTP/1.1</a> was released a year later in 1997.  Per the authors:</p>
           74 
           75 <blockquote>
           76   <p>However, HTTP/1.0 does not sufficiently take into consideration the effects of hierarchical proxies, caching, the need for persistent connections, and virtual  hosts. In addition, the proliferation of incompletely-implemented applications calling themselves “HTTP/1.0” has necessitated a protocol version change in order for two communicating applications to determine each other’s true capabilities.</p>
           77 </blockquote>
           78 
           79 <p>It would be 18 more years before a new version of HTTP was released.  In 2015, and with much fanfare, <a href="https://datatracker.ietf.org/doc/html/rfc7540">RFC 7540</a> would standardize HTTP/2 as the next major version of the protocol.</p>
           80 
           81 <h3 id="one-file-at-a-time">One File at a Time</h3>
           82 <p>If a web page requires 10 javascript files, the web browser needs to retrieve those 10 files before the page can finish loading.  In HTTP/1.1-land, the web browser can only download a single file at a time over a TCP connection with the server.  This means the files are downloaded sequentially, and any delay in one file would block everything else behind it.  This is called <a href="https://en.wikipedia.org/wiki/Head-of-line_blocking">Head-of-line Blocking</a> and it’s not good for performance.</p>
           83 
           84 <p>To work around this, browsers can open multiple TCP connections to the server to parallelize the data retrieval.  But this approach is resource intensive.  Each new TCP connection requires client and server resources, and when you add TLS in the mix there’s plenty of SSL negotiation happening too.  A better way was needed.</p>
           85 
           86 <h3 id="multiplexing-with-http2">Multiplexing with HTTP/2</h3>
           87 <p>HTTP/2’s big selling point was multiplexing. It fixed <em>application level</em> head-of-line blocking issues by switching to a binary over-the-wire format that allowed multiplexed file downloads.  That is, a client could request all 10 files at once and start downloading them all in parallel over a single TCP connection.</p>
           88 
           89 <p>Unfortunately HTTP/2 still suffers from a head-of-line blocking issue, just one layer lower. TCP itself becomes the weak link in the chain.  Any data stream that loses a packet must wait until that packet is <a href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-prior-versions-of-http">retransmitted to continue</a>.</p>
           90 
           91 <blockquote>
           92   <p>However, because the parallel nature of HTTP/2’s multiplexing is not visible to TCP’s loss recovery mechanisms, a lost or reordered packet causes all active transactions to experience a stall regardless of whether that transaction was directly impacted by the lost packet.</p>
           93 </blockquote>
           94 
           95 <p>In fact, in high packet loss environments, HTTP/1.1 performs better because of the multiple parallel TCP connections the browser opens!</p>
           96 
           97 <h3 id="true-multiplexing-with-http3-and-quic">True Multiplexing with HTTP/3 and QUIC</h3>
           98 <p>Enter HTTP/3.  The major difference between HTTP/2 and HTTP/3 is which transport protocol they use. Instead of TCP, HTTP/3 uses a new protocol called <a href="https://www.rfc-editor.org/rfc/rfc9000.html">QUIC</a>.  QUIC is a general purpose transport protocol meant to address the head-of-line blocking issues HTTP/2 has with TCP.  It allows you to create a <a href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-delegation-to-quic">series of stateful streams</a> (similar to TCP) over UDP.</p>
           99 
          100 <figure class="wide">
          101   <img src="https://requestmetrics.com/assets/images/webperf/http3/udp-joke.min.png" width="400" height="480" loading="lazy" alt="I have a UDP Joke... but you might not get it" />
          102 </figure>
          103 
          104 <blockquote>
          105   <p>The QUIC transport protocol incorporates stream multiplexing and per-stream flow control, similar to that provided by the HTTP/2 framing layer. By providing reliability at the stream level and congestion control across the entire connection, <strong>QUIC has the capability to improve the performance of HTTP compared to a TCP mapping</strong></p>
          106 </blockquote>
          107 
          108 <p>And improve the performance of HTTP it does!  <a href="#so-how-fast-is-http3">Jump to the results if you don’t care about how the test was conducted</a></p>
          109 
          110 <h2 id="benchmarking-http3">Benchmarking HTTP/3</h2>
          111 <p>To see just what sort of performance difference HTTP/3 makes, a benchmarking test setup was needed.</p>
          112 <h3 id="the-html">The HTML</h3>
          113 <p>In order to more closely approximate actual usage, the test setup consisted of three scenarios - a small site, a content-heavy site (lots of images and some JS), and a single page application (heavy on the JS).  I looked at several real-world sites and averaged the number of images and JS files for each, then coded up some demo sites that matched those resource counts (and sizes).</p>
          114 
          115 <ul>
          116   <li>Small Site
          117     <ul>
          118       <li><strong>10</strong> JS files from 2kb to 100kb</li>
          119       <li><strong>10</strong> images from 1kb to 50kb</li>
          120       <li>Total payload size <strong>600kb</strong>, 20 blocking resources total</li>
          121     </ul>
          122   </li>
          123   <li>Content Site
          124     <ul>
          125       <li><strong>50</strong> JS files from 2kb to 1mb</li>
          126       <li><strong>55</strong> images ranging in size from 1kb to 1mb.</li>
          127       <li>Total payload size <strong>10MB</strong>, 105 resources total (look at cnn.com sometime in dev tools and you’ll see why this is so big)</li>
          128     </ul>
          129   </li>
          130   <li>Single Page Application
          131     <ul>
          132       <li><strong>85</strong> JS files from 2kb to 1mb</li>
          133       <li><strong>30</strong> images ranging in size from 1kb to 50kb.</li>
          134       <li>Total payload size <strong>15MB</strong>, 115 resources total (look at JIRA sometime in dev tools)</li>
          135     </ul>
          136   </li>
          137 </ul>
          138 
          139 <h3 id="the-server">The Server</h3>
          140 <p><a href="https://caddyserver.com/">Caddy</a> was used to serve all assets and HTML.</p>
          141 
          142 <ul>
          143   <li>All responses were served with <code class="language-plaintext highlighter-rouge">Cache-Control: "no-store"</code> to ensure the browser would re-download every time.</li>
          144   <li>TLS 1.2 was used for HTTP/1.1 and HTTP/2</li>
          145   <li><a href="https://www.rfc-editor.org/rfc/rfc9001.html">TLS 1.3</a> was used for HTTP/3.</li>
          146   <li><a href="https://www.rfc-editor.org/rfc/rfc9001.html#name-0-rtt">0-RTT</a> was enabled for all HTTP/3 connections</li>
          147 </ul>
          148 
          149 <h3 id="the-locations">The Locations</h3>
          150 <p>The tests were conducted from my computer in Minnesota, to three separate datacenters hosted by Digital Ocean:</p>
          151 
          152 <ul>
          153   <li>New York, USA</li>
          154   <li>London, England</li>
          155   <li>Bangalore, India</li>
          156 </ul>
          157 
          158 <h3 id="the-client">The Client</h3>
          159 <p>I automated the browser to request the same page 20 times in a row, waiting 3 seconds after page load to begin the next request.  The internet connection is rated at 200mbps.  No other applications were running on the computer at the time of data capture.</p>
          160 
          161 <h2 id="so-how-fast-is-http3">So How Fast Is HTTP/3?</h2>
          162 <h3 id="new-york-usa">New York, USA</h3>
          163 <p>Here’s the response times of HTTP/2 vs. HTTP/3 when requesting the three different sites from the NY datacenter:</p>
          164 
          165 <figure class="wide">
          166   <div class="wide-wrap">
          167     <img src="https://requestmetrics.com/assets/images/webperf/http3/ny-http2and3.png" width="749" loading="lazy" height="353" alt="Comparing HTTP/2 and HTTP/3 protocol versions when loading pages from NY" />
          168   </div>
          169 </figure>
          170 
          171 <p>HTTP/3 is:</p>
          172 <ul>
          173   <li><strong>200ms</strong> faster for the Small Site</li>
          174   <li><strong>325ms</strong> faster for the Content Site</li>
          175   <li><strong>300ms</strong> faster for the Single Page Application</li>
          176 </ul>
          177 
          178 <p>The distance from Minnesota to New York is 1,000 miles, which is pretty small by networking standards.  It’s significant that even at a relatively short distance HTTP/3 was able to improve performance this much.</p>
          179 
          180 <h3 id="london-england">London, England</h3>
          181 <p>I’ve included the HTTP/1.1 benchmarking run for London in the results as well.  In order to show just how much faster HTTP/2 and HTTP/3 are, I’ve kept the graphs to the same scale.  You can see that for the Content Site, the timings are so slow that they don’t even fit entirely on the graph!</p>
          182 
          183 <figure class="wide">
          184   <div class="wide-wrap">
          185     <img src="https://requestmetrics.com/assets/images/webperf/http3/london-all-protocols.png" width="1144" loading="lazy" height="353" alt="Comparing the three HTTP protocol versions when loading pages from London" />
          186   </div>
          187 </figure>
          188 
          189 <p>As you can see, the speed increase is even more pronounced when greater distances over the network are in play.  HTTP/3 is:</p>
          190 <ul>
          191   <li><strong>600ms</strong> faster for the Small Site (<strong>3x</strong> the speedup compared with New York)</li>
          192   <li><strong>1200ms</strong> faster for the Content Site (over <strong>3.5x</strong> the speedup compared with New York)</li>
          193   <li><strong>1000ms</strong> faster for the Single Page Application (over <strong>3x</strong> the speedup compared with New York)</li>
          194 </ul>
          195 
          196 <h3 id="bangalore-india">Bangalore, India</h3>
          197 <p>The performance improvement with HTTP/3 is extremely pronounced when loading pages from the server in India.  I didn’t even run an HTTP/1.1 test because it was so slow.  Here are the results of HTTP/2 vs. HTTP/3:</p>
          198 
          199 <figure class="wide">
          200   <div class="wide-wrap">
          201     <img src="https://requestmetrics.com/assets/images/webperf/http3/india-http2and3.png" width="749" loading="lazy" height="353" alt="Comparing the three HTTP protocol versions when loading pages from India" />
          202   </div>
          203 </figure>
          204 
          205 <p>HTTP/3 continues to pull ahead when larger geographies and more network hops are involved.  What’s perhaps more striking is just how tightly grouped the response times are for HTTP/3.  QUIC is having a big impact when packets are traveling thousands of miles.</p>
          206 
          207 <p>In every case HTTP/3 was faster than its predecessor!</p>
          208 
          209 <h3 id="why-is-http3-so-much-faster">Why is HTTP/3 so Much Faster?</h3>
          210 
          211 <h4 id="real-multiplexing">Real Multiplexing</h4>
          212 <p>The true multiplexed nature of HTTP/3 means that there is no Head-of-line blocking happening anywhere on the stack.  When requesting resources from further away, geographically, there is a much higher chance of packet loss and the need for TCP to re-transmit those packets.</p>
          213 
          214 <h4 id="0-rtt-is-a-game-changer">0-RTT Is a Game Changer</h4>
          215 <p>Additionally, HTTP/3 supports <a href="https://www.rfc-editor.org/rfc/rfc9001.html#section-4.6-1">O-RTT</a> QUIC connections, which lowers the number of round trips required to establish a secure TLS connection with the server.</p>
          216 
          217 <blockquote>
          218   <p>The 0-RTT feature in QUIC allows a client to send application data before the handshake is complete. This is made possible by reusing negotiated parameters from a previous connection. To enable this, 0-RTT depends on the client remembering critical parameters and providing the server with a TLS session ticket that allows the server to recover the same information.</p>
          219 </blockquote>
          220 
          221 <p>However, 0-RTT should not be blindly enabled.  There are some <a href="https://www.rfc-editor.org/rfc/rfc8446#section-2.3">possible</a> <a href="https://www.rfc-editor.org/rfc/rfc9001.html#name-replay-attacks-with-0-rtt">security concerns</a> depending on your threat model.</p>
          222 
          223 <blockquote>
          224   <p>The security properties for 0-RTT data are weaker than those for other kinds of TLS data.  Specifically:</p>
          225   <ol>
          226     <li>This data is not forward secret, as it is encrypted solely under keys derived using the offered PSK.</li>
          227     <li>There are no guarantees of non-replay between connections.</li>
          228   </ol>
          229 </blockquote>
          230 
          231 <h2 id="can-i-use-http3-today">Can I Use HTTP/3 Today?</h2>
          232 <p>Maybe!  While the protocol is currently in <em>Internet-Draft</em> status, there are plenty of existing <a href="https://en.wikipedia.org/wiki/HTTP/3#Server">implementations</a>.</p>
          233 
          234 <p>I specifically chose <strong>Caddy</strong> for these benchmarks because HTTP/3 can be enabled with a <a href="https://caddyserver.com/docs/caddyfile/options#protocol">simple config value</a> in the <code class="language-plaintext highlighter-rouge">Caddyfile</code></p>
          235 
          236 <p>NGINX also has experimental support and is <a href="https://www.nginx.com/blog/our-roadmap-quic-http-3-support-nginx/">working towards an official HTTP/3</a> release in the near future.</p>
          237 
          238 <p>The big tech players like Google and Facebook are serving their traffic over HTTP/3 already.  <a href="https://google.com">Google.com</a> is entirely served over HTTP/3 for modern browsers.</p>
          239 
          240 <p>For those folks stuck in the Windows ecosystem, supposedly Windows Server 2022 will support HTTP/3, with some rather <a href="https://techcommunity.microsoft.com/t5/networking-blog/enabling-http-3-support-on-windows-server-2022/ba-p/2676880">esoteric steps required</a> to enable it.</p>
          241 
          242 <h2 id="conclusion">Conclusion</h2>
          243 <p>HTTP/3 can make a big difference in how users experience your site.  In general, the more resources your site requires, the bigger the performance improvement you’ll see with HTTP/3 and QUIC.  As the standard continues to inch closer to finalization, it may be time to start looking at enabling it for your sites.</p>
          244 
          245 
          246       ]]></content:encoded>
          247       <pubDate>Mon, 29 Nov 2021 11:00:00 +0000</pubDate>
          248       <author>hello@requestmetrics.com (Request Metrics)</author>
          249     </item>
          250     
          251     
          252     <item>
          253       <title>Using HTTP Caching: 2021 Guide</title>
          254       <link>https://requestmetrics.com/web-performance/http-caching</link>
          255       <guid isPermaLink="true">https://requestmetrics.com/web-performance/http-caching</guid>
          256       <description>The fastest website is the website that is already loaded, and that’s exactly what we can do with HTTP caching. HTTP caching lets web browsers reuse of previously loaded resources, like pages, images, JavaScript, and CSS. It’s a powerful tool to improve your web performance, but misconfiguration can cause big performance problems. Here’s what you need to know to use HTTP caching without reading hundreds of pages of HTTP Caching Spec.
          257 
          258 </description>
          259       <media:content url="https://requestmetrics.com/assets/images/webperf/http-caching/http-caching-2000.png" medium="image" type="image/png" height="350" width="700" />
          260       <enclosure url="https://requestmetrics.com/assets/images/webperf/http-caching/http-caching-2000.png" type="image/png" />
          261       <content:encoded><![CDATA[
          262         <div><img src="https://requestmetrics.com/assets/images/webperf/http-caching/http-caching-2000.png" alt="Using HTTP Caching: 2021 Guide" class="webfeedsFeaturedVisual" /></div>
          263         <p>The fastest website is the website that is already loaded, and that’s exactly what we can do with HTTP caching. HTTP caching lets web browsers reuse of previously loaded resources, like pages, images, JavaScript, and CSS. It’s a powerful tool to improve your web performance, but misconfiguration can cause <a href="/web-performance/how-hackernews-crushed-davidwalshblog">big performance problems</a>. Here’s what you need to know to use HTTP caching without reading hundreds of pages of <a href="https://datatracker.ietf.org/doc/html/rfc7234">HTTP Caching Spec</a>.</p>
          264 
          265 <!--more-->
          266 
          267 <p>HTTP caching is controlled by headers returned as part of the server response. The most important of these is the <code class="language-plaintext highlighter-rouge">Cache-Control</code> header, which informs the browser how and when a resource may be cached. The <code class="language-plaintext highlighter-rouge">Cache-Control</code> header has many, many options that control caching behavior. But to avoid writing a novel, we’ll focus on the basics of controlling cache, and give you some recipes for common scenarios.</p>
          268 
          269 <h2 id="how-to-use-the-browser-cache">How to use the Browser Cache</h2>
          270 <p>The browser calculates “Cache Freshness” using headers in the HTTP response. Cache freshness is how long a cached asset is valid since it was downloaded. Freshness is calculated depending on which headers are returned.</p>
          271 
          272 <h3 id="the-cache-control-header">The <code class="language-plaintext highlighter-rouge">Cache-Control</code> Header</h3>
          273 <p>The <code class="language-plaintext highlighter-rouge">Cache-Control</code> header has a number of directives to control caching behavior, but the most common is <code class="language-plaintext highlighter-rouge">max-age</code>. Max-age specifies how many seconds after download the cached resource is valid. Here’s an example:</p>
          274 
          275 <figure class="code oneliner" id="code-79">
          276     <div class="code-wrap">
          277         <pre class="prettyprint lang-bsh">
          278 # Cache this response for 10 minutes (600 seconds).
          279 Cache-Control: max-age=600</pre>
          280         <!-- <button type="button" class="btn copy-button yes-js">
          281             <img src="/assets/images/copy.svg" alt="Copy" />
          282             <span class="label">Copy</span>
          283         </button> -->
          284         
          285     </div>
          286 </figure>
          287 
          288 <h3 id="the-expires-header">The <code class="language-plaintext highlighter-rouge">Expires</code> Header</h3>
          289 <p>The <code class="language-plaintext highlighter-rouge">Expires</code> header contains a date and time at which the cached resource should be marked stale, but only if you didn’t already use the <code class="language-plaintext highlighter-rouge">max-age</code> <code class="language-plaintext highlighter-rouge">Cache-Control</code> option. <code class="language-plaintext highlighter-rouge">Expires</code> is used to determine freshness if the response also contains a <code class="language-plaintext highlighter-rouge">Date</code> header for when the response was sent. Freshness is simply subtracting <code class="language-plaintext highlighter-rouge">Date</code> from the <code class="language-plaintext highlighter-rouge">Expires</code> time.</p>
          290 
          291 <figure class="code oneliner" id="code-147">
          292     <div class="code-wrap">
          293         <pre class="prettyprint lang-bsh">
          294 # This response can be cached for 1 hour (Expires - Date == freshness).
          295 Expires: Tue, 09 Nov 2021 21:09:28 GMT
          296 Date: Tue, 09 Nov 2021 20:09:28 GMT</pre>
          297         <!-- <button type="button" class="btn copy-button yes-js">
          298             <img src="/assets/images/copy.svg" alt="Copy" />
          299             <span class="label">Copy</span>
          300         </button> -->
          301         
          302     </div>
          303 </figure>
          304 
          305 <h2 id="the-browsers-automatic-caching">The Browser’s Automatic Caching</h2>
          306 <p>Even if you don’t use the <code class="language-plaintext highlighter-rouge">Cache-Control</code> or <code class="language-plaintext highlighter-rouge">Expires</code> header, most web browsers will cache resources automatically and <em>guess</em> how long they will remain fresh. This guessing is referred to as <strong>“heuristic freshness”</strong>. Usually, the guess is based on the <code class="language-plaintext highlighter-rouge">Last-Modified</code> header included automatically by most web servers. But each browser implements this differently, so it’s dangerous to rely on it for your caching.</p>
          307 
          308 <p>One method that browser’s use is to assume a resource is “fresh” for 10% of the time since the resource was last modified.</p>
          309 
          310 <figure class="code oneliner" id="code-179">
          311     <div class="code-wrap">
          312         <pre class="prettyprint lang-bsh">
          313 # Freshness = 2 hours  (20 hours since last modified)
          314 # (Date - Last-Modified) * 10% == freshness
          315 Last-Modified: Tue, 09 Nov 2021 02:00:00 GMT
          316 Date: Tue, 09 Nov 2021 22:00:00 GMT</pre>
          317         <!-- <button type="button" class="btn copy-button yes-js">
          318             <img src="/assets/images/copy.svg" alt="Copy" />
          319             <span class="label">Copy</span>
          320         </button> -->
          321         
          322     </div>
          323 </figure>
          324 
          325 <!-- class="wide">
          326   <div class="wrap callout left flex">
          327     <div class="picture">
          328       <img loading="lazy" class="lazyload" src="https://requestmetrics.com/assets/images/illustrations/sloth_laptop_500.png" srcset="https://requestmetrics.com/assets/images/illustrations/sloth_laptop_500.png   500w,
          329                      https://requestmetrics.com/assets/images/illustrations/sloth_laptop_1000.png 1000w" sizes="(max-width: 900px) 100vw, 400px" alt="Sloth Laptop" width="600" height="450" />
          330       </div>
          331       <div class="blurb">
          332       <h2>Check Your Caching Headers!</h2>
          333       <p>
          334         Check how your caching is configured right now! We made a neat tool that checks your HTTP cache settings.
          335       </p>
          336       <div class="flex cta-buttons">
          337         <a class="btn btn-big btn-grey" href="/tools/http-cache-checker">
          338           Test My Cache
          339         </a>
          340       </div>
          341     </div>
          342   </div>
          343 -->
          344 
          345 <h2 id="handling-expired-resources">Handling Expired Resources</h2>
          346 
          347 <p>What happens when a resource “expires”? This is referred to as a <strong>“stale resource”</strong>,  and the browser must re-validate the resource from the server. In some cases, the browser can validate the resource without downloading it again. Otherwise, the browser re-downloads the entire resource and caches the new version.</p>
          348 
          349 <p>There are a couple ways this validation can happen, depending on which <em>HTTP Validation Headers</em> are sent with your resources.</p>
          350 
          351 <h3 id="validating-with-the-etag-header">Validating With the <code class="language-plaintext highlighter-rouge">ETag</code> Header</h3>
          352 <p>The <code class="language-plaintext highlighter-rouge">ETag</code> header allows the browser to tell the server what version it currently has. The header contains a string which uniquely identifies the content, usually a checksum of the file.</p>
          353 
          354 <p>When a resource expires that had an ETag, the browser will send a validation request with a <code class="language-plaintext highlighter-rouge">If-None-Match</code> header containing the ETag value it already has. If the resource is unchanged, the server replies with an empty 304 (Not Modified) HTTP response. Otherwise, the server sends the resource like normal when the content has changed.</p>
          355 
          356 <figure class="code " id="code-172">
          357     <div class="code-wrap">
          358         <pre class="prettyprint lang-bsh">
          359 # In original resource response headers:
          360 ETag: &quot;123abc987654&quot;
          361 
          362 # Browser sends in the validation request headers:
          363 If-None-Match: &quot;123abc987654&quot;
          364 </pre>
          365         <!-- <button type="button" class="btn copy-button yes-js">
          366             <img src="/assets/images/copy.svg" alt="Copy" />
          367             <span class="label">Copy</span>
          368         </button> -->
          369         
          370         <figcaption><a href="#code-172">Cache Validation Using ETag</a></figcaption>
          371         
          372     </div>
          373 </figure>
          374 
          375 <h3 id="validating-with-the-modified-date-header">Validating With the <code class="language-plaintext highlighter-rouge">Modified-Date</code> Header</h3>
          376 <p>When an ETag is unavailable, web servers may send a <code class="language-plaintext highlighter-rouge">Modified-Date</code> header, with the last modified date of the source file. Similar to ETags, the browser can send that date in a validation request with the <code class="language-plaintext highlighter-rouge">If-Modified-Since</code> header to tell the server which version it has.</p>
          377 
          378 <p>The server returns an empty 304 (Not Modified) response if the content has not changed since the date specified.</p>
          379 
          380 <figure class="code " id="code-224">
          381     <div class="code-wrap">
          382         <pre class="prettyprint lang-bsh">
          383 # In original resource response headers:
          384 Modified-Date: Tue, 09 Nov 2021 20:00:00 GMT
          385 
          386 # Browser sends in the validation request headers:
          387 If-Modified-Since: Tue, 09 Nov 2021 20:00:00 GMT
          388 </pre>
          389         <!-- <button type="button" class="btn copy-button yes-js">
          390             <img src="/assets/images/copy.svg" alt="Copy" />
          391             <span class="label">Copy</span>
          392         </button> -->
          393         
          394         <figcaption><a href="#code-224">Cache Validation Using Modified-Date</a></figcaption>
          395         
          396     </div>
          397 </figure>
          398 
          399 <h3 id="no-validation">No Validation</h3>
          400 <p>If the original resource had neither <code class="language-plaintext highlighter-rouge">ETag</code> or <code class="language-plaintext highlighter-rouge">Modified-Date</code> headers, then the browser simply requests the entire resource and uses the result.</p>
          401 
          402 <h2 id="busting-the-browsers-cache">Busting the Browsers Cache</h2>
          403 <p>When something changes, such as a new image, refreshed session, or an updated release of your code, you’ll want to invalidate (or bust!) the browser cache so that your users get the new stuff. If you’ve aggressively set caching headers, this can be challenging, but there are a couple ways to solve it.</p>
          404 
          405 <h3 id="1-changing-the-url-to-the-resource">1. Changing the URL to the Resource</h3>
          406 
          407 <p>The most common cache busting strategy is just to change the name of your resources when they change. This could be something like including a hash, version, or date in the filename when you build your site.</p>
          408 
          409 <figure class="code " id="code-58">
          410     <div class="code-wrap">
          411         <pre class="prettyprint lang-bsh">scripts.e7686eaf.min.js</pre>
          412         <!-- <button type="button" class="btn copy-button yes-js">
          413             <img src="/assets/images/copy.svg" alt="Copy" />
          414             <span class="label">Copy</span>
          415         </button> -->
          416         
          417         <figcaption><a href="#code-58">Example filename ready to be busted</a></figcaption>
          418         
          419     </div>
          420 </figure>
          421 
          422 <h3 id="2-adding-a-query-parameter">2. Adding a Query Parameter</h3>
          423 
          424 <p>If you can’t change the name of your resources, you can add a querystring parameter with a changeable key, like a version or date. When you change your site, or a resource, updating the querystring to a new value will invalidate all browser caches.</p>
          425 
          426 <figure class="code " id="code-62">
          427     <div class="code-wrap">
          428         <pre class="prettyprint lang-bsh">/my/images.png?v=2021119</pre>
          429         <!-- <button type="button" class="btn copy-button yes-js">
          430             <img src="/assets/images/copy.svg" alt="Copy" />
          431             <span class="label">Copy</span>
          432         </button> -->
          433         
          434         <figcaption><a href="#code-62">Example querystring ready to be busted</a></figcaption>
          435         
          436     </div>
          437 </figure>
          438 
          439 <p>If you have a look at the source of our page here, you’ll see what we use this strategy, adding a date representation of the build time to all our scripts and styles.</p>
          440 
          441 <h3 id="3-using-the-vary-header">3. Using the <code class="language-plaintext highlighter-rouge">Vary</code> Header</h3>
          442 
          443 <p>The <code class="language-plaintext highlighter-rouge">Vary</code> header is can be returned in resource responses and tells the browser when a resource should be cached as a unique variation of the resource. It does this by specifying one or more headers to use as a unique key.</p>
          444 
          445 <p>The browser will never be able to use its cached responses if the header values change on every request. <code class="language-plaintext highlighter-rouge">Vary</code> is often omitted entirely, and should be used carefully when needed.</p>
          446 
          447 <figure class="code " id="code-251">
          448     <div class="code-wrap">
          449         <pre class="prettyprint lang-bsh">
          450 # Good: A common value that should not impact caching
          451 # Caches gzip vs non-gzip responses separately
          452 Vary: Accept-Encoding
          453 
          454 # Bad: Probably not what you want.
          455 # Any change to X-App-Version will invalidate your cache!
          456 Vary: X-App-Version
          457 </pre>
          458         <!-- <button type="button" class="btn copy-button yes-js">
          459             <img src="/assets/images/copy.svg" alt="Copy" />
          460             <span class="label">Copy</span>
          461         </button> -->
          462         
          463         <figcaption><a href="#code-251">Vary Examples</a></figcaption>
          464         
          465     </div>
          466 </figure>
          467 
          468 <h2 id="http-caching-recipes">HTTP Caching Recipes</h2>
          469 <p>Different resources are cached differently. Here’s how to accomplish a few common caching scenarios.</p>
          470 
          471 <h3 id="1-never-cache-anything">1. Never Cache Anything!</h3>
          472 <p>Some resources are dynamic or time sensitive and should never be cached. This will force the browser to re-download resources each and every time the user loads the page. Force the browser to always makes a request:</p>
          473 
          474 <figure class="code oneliner" id="code-23">
          475     <div class="code-wrap">
          476         <pre class="prettyprint lang-bsh">Cache-Control: no-store</pre>
          477         <!-- <button type="button" class="btn copy-button yes-js">
          478             <img src="/assets/images/copy.svg" alt="Copy" />
          479             <span class="label">Copy</span>
          480         </button> -->
          481         
          482     </div>
          483 </figure>
          484 
          485 <h3 id="2-cache-but-always-revalidate">2. Cache, But Always Revalidate</h3>
          486 <p>Some resources are cacheable, but change often enough that they should be re-validated before use. We can accomplish this with the confusingly named <code class="language-plaintext highlighter-rouge">no-cache</code> directive. The browser will request an updated version of the resource, but will accept a 304 (Not Modified) response to save download bandwidth.</p>
          487 
          488 <figure class="code oneliner" id="code-97">
          489     <div class="code-wrap">
          490         <pre class="prettyprint lang-bsh">
          491 Cache-Control: no-cache
          492 
          493 # no-cache is equivalent to:
          494 Cache-Control: max-age=0, must-revalidate
          495 </pre>
          496         <!-- <button type="button" class="btn copy-button yes-js">
          497             <img src="/assets/images/copy.svg" alt="Copy" />
          498             <span class="label">Copy</span>
          499         </button> -->
          500         
          501     </div>
          502 </figure>
          503 
          504 <h3 id="3-cache-for-a-day">3. Cache For A Day</h3>
          505 <p>Some resources change, but do so slowly. Setting a “just right” <code class="language-plaintext highlighter-rouge">max-age</code> on these allows them to be cached but updated in a timely manner when changed. Don’t depend on <code class="language-plaintext highlighter-rouge">max-age</code> alone if it’s critical that the browser immediately uses a new version, use a Cache-Buster!</p>
          506 
          507 <figure class="code oneliner" id="code-28">
          508     <div class="code-wrap">
          509         <pre class="prettyprint lang-bsh">Cache-Control: max-age=86400</pre>
          510         <!-- <button type="button" class="btn copy-button yes-js">
          511             <img src="/assets/images/copy.svg" alt="Copy" />
          512             <span class="label">Copy</span>
          513         </button> -->
          514         
          515     </div>
          516 </figure>
          517 
          518 <h3 id="4-cache-forever">4. Cache “Forever”</h3>
          519 <p>You probably don’t want to do this unless you are using a cache-busting strategy. There isn’t actually a “forever” cache directive, but you can get close enough by specifying a very large <code class="language-plaintext highlighter-rouge">max-age</code>.</p>
          520 
          521 <figure class="code oneliner" id="code-65">
          522     <div class="code-wrap">
          523         <pre class="prettyprint lang-bsh">
          524 # Cache this resource for a year
          525 Cache-Control: max-age=31536000</pre>
          526         <!-- <button type="button" class="btn copy-button yes-js">
          527             <img src="/assets/images/copy.svg" alt="Copy" />
          528             <span class="label">Copy</span>
          529         </button> -->
          530         
          531     </div>
          532 </figure>
          533 
          534 <hr />
          535 
          536 <h2 id="conclusion">Conclusion</h2>
          537 <p>That’s it! You can use these headers and recipes to greatly accelerate your website and save a ton of redundant download bandwidth.  Proper caching can improve the way customers perceive your site’s performance. But don’t take our word for it, you should be monitoring your website performance with <a href="https://requestmetrics.com">Request Metrics</a> to check and improve your website performance.</p>
          538 
          539       ]]></content:encoded>
          540       <pubDate>Fri, 19 Nov 2021 08:00:00 +0000</pubDate>
          541       <author>hello@requestmetrics.com (Request Metrics)</author>
          542     </item>
          543     
          544     
          545     <item>
          546       <title>Synthetic Testing and Real User Monitoring</title>
          547       <link>https://requestmetrics.com/web-performance/synthetic-testing-and-real-user-monitoring</link>
          548       <guid isPermaLink="true">https://requestmetrics.com/web-performance/synthetic-testing-and-real-user-monitoring</guid>
          549       <description>
          550 Synthetic Testing and Real User Monitoring are the most important tools in your performance toolbox. But they do different things and are useful at different times and many developers only spend time mastering one of these tools and only see a part of their performance problems, like trying to hammer in a screw.
          551 
          552 </description>
          553       <media:content url="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/synthetic-testing-and-real-user-monitoring-2000.png" medium="image" type="image/png" height="350" width="700" />
          554       <enclosure url="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/synthetic-testing-and-real-user-monitoring-2000.png" type="image/png" />
          555       <content:encoded><![CDATA[
          556         <div><img src="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/synthetic-testing-and-real-user-monitoring-2000.png" alt="Synthetic Testing and Real User Monitoring" class="webfeedsFeaturedVisual" /></div>
          557         
          558 <p>Synthetic Testing and Real User Monitoring are the most important tools in your performance toolbox. But they do different things and are useful at different times and many developers only spend time mastering one of these tools and only see a part of their performance problems, like trying to hammer in a screw.</p>
          559 
          560 <!--more-->
          561 
          562 <p>Let’s look at these tools, what they measure, and when to use them.</p>
          563 
          564 <h2 id="synthetic-testing">Synthetic Testing</h2>
          565 <p><em>Synthetic Testing</em> measures the performance of a website under a controlled environment. Examples of this are Lighthouse audits from Chrome Devtools or Pagespeed Insights. The test simulates the location, latency, bandwidth, browser, and device in order to approximate the experience of a visitor to your website.</p>
          566 
          567 <p>For a Synthetic Test to be accurate and valuable, you need to know things about your likely visitors: where they are, what kind of network they are on, and what device they are using. Then the test needs to accurately simulate these characteristics. Both of these things are difficult.</p>
          568 
          569 <p>The internet is a big and diverse place, and developers don’t always know enough about our users. We can make guesses, but because we often run on fast networks with new laptops, we often overestimate the capability of our users because. <em>It’s fast on my machine.</em></p>
          570 
          571 <figure class="">
          572   <img src="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/laptop_1000_apng.png" style="max-width: 400px" loading="lazy" class="lazyload" alt="Sloth on fast laptop" width="1000" height="1000" />
          573 </figure>
          574 
          575 <p>Plus, you likely have <em>more than one kind of user</em> that should be tested. Some of your users will visit from laptops at work. Others will try and login on a phone from the train while commuting, or on their tablet with flaky wi-fi from the coffee shop. Each user will have a different perspective on performance, and would need to be simulated with a different test.</p>
          576 
          577 <p>The biggest benefit of a this kind of tool is that you can <a href="https://developers.google.com/speed/pagespeed/insights/">run a Synthetic Test on your website right now</a>, regardless of whether you have any users. And the results will probably tell you about your biggest performance problems.</p>
          578 
          579 <p>The test will be flawed, and that’s okay because it will give you an idea of performance. Synthetic Testing will never tell you how fast your website really is—only how fast it <em>might</em> be right now.</p>
          580 
          581 <h2 id="real-user-monitoring">Real User Monitoring</h2>
          582 <p>Real User Monitoring is just that: <strong>real</strong>. Real User Monitoring (or RUM) records the <em>actual</em> performance from users that visited your website. RUM doesn’t guess or simulate a user, it just records the actual performance they experienced.</p>
          583 
          584 <p>Real User Monitoring is more accurate than Synthetic Testing, but there is also more noise and more delay.</p>
          585 
          586 <p>RUM data will inherently include data from <em>all users</em>, even that guy using a GameBoy to browse your website from Mongolia. You’ll have to apply <em>statistics</em> to the data to understand what it really means—things like medians, percentiles, and distributions. Used correctly, RUM data tells you how your fastest users, typical users, and worst users experience your website.</p>
          587 
          588 <!-- class="wide">
          589   <div class="wrap callout left flex">
          590     <div class="picture" style="border: 1px solid #DDE4EB; border-radius: 6px;">
          591       <img src="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/rum-distribution-400.png" srcset="https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/rum-distribution-400.png   400w,
          592                      https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/rum-distribution-800.png   800w,
          593                      https://requestmetrics.com/assets/images/webperf/synthetic-testing-and-real-user-monitoring/rum-distribution-1600.png 1600w" sizes="(max-width: 900px) 100vw, 400px" loading="lazy" class="lazyload" alt="Request Metrics Performance Distribution" width="1600" height="800" />
          594       </div>
          595       <div class="blurb">
          596       <p>
          597         RUM tools like Request Metrics do the statistics for you! This distribution of the performance for our homepage, shows the load time of most users, 75% of users, and the slowest users. You can check out our live data and explore this chart in our interactive demo.
          598       </p>
          599       <div class="flex cta-buttons">
          600         <a class="btn btn-big btn-grey" href="https://app.requestmetrics.com/demo">
          601           Live Demo
          602         </a>
          603       </div>
          604     </div>
          605   </div>
          606 -->
          607 
          608 <p>The biggest limitation of RUM is the delay. RUM can’t tell you how fast your site will be until users start visiting it. You’ll have to release that change and measure the impact to see if your site sped up–or not. Synthetic Testing can make some guesses at the performance early, which help find obvious problems, but to really prove your site is fast, you have to use RUM.</p>
          609 
          610 <p>Some folks at Google even looked at this, <a href="https://philipwalton.com/articles/my-challenge-to-the-web-performance-community/">comparing websites Synthetic performance with RUM</a>. Almost half of sites with perfect Synthetic Tests failed the minimum requirements for Core Web Vital scores! Half! Half of people bragging about their perfect Lighthouse score are being <a href="https://requestmetrics.com/web-performance/web-vitals">penalized by Google for poor performance</a>.</p>
          611 
          612 <h2 id="signal-vs-noise">Signal vs Noise</h2>
          613 <p>Synthetic Testing and Real User Monitoring is about <em>Signal vs Noise</em>. Synthetic Tests don’t have much noise—each Lighthouse test you run is a valid measurement of performance for those conditions. Run the test again with the same conditions and there will be very similar results.</p>
          614 
          615 <p>But as the Google research showed, there is not a lot of signal in those synthetic results either. That Lighthouse report isn’t how any user will experience your page (unless they are browsing your website from your laptop on your network).</p>
          616 
          617 <p>Real User Monitoring is the opposite. Each bit of data you get from RUM is how your website really performed for a visitor. But those visitors can be wildly different. Some will have an awesome experience on your website. Others will think they are still on AOL (read: old really slow internet).</p>
          618 
          619 <p>The trick is, which users do you care about? If you are building a site for corporate users in the United States, then it doesn’t matter what the performance is for mobile users in Ukraine. RUM tools like Request Metrics help you filter out noise and aggregate the data to give you a clearer picture of your target user.</p>
          620 
          621 <hr />
          622 
          623 <p>Both Synthetic Testing and Real User Monitoring are valuable tools for any developer that wants to build fast websites. Use Synthetic Testing, like Lighthouse, to test your changes before release. It will help you catch obvious mistakes.</p>
          624 
          625 <p>And use <a href="https://requestmetrics.com">Real User Monitoring tools like Request Metrics</a> to see if that change <em>really</em> sped things up. You don’t know how fast your website is until your visitors tell you.</p>
          626 
          627       ]]></content:encoded>
          628       <pubDate>Wed, 10 Nov 2021 09:00:00 +0000</pubDate>
          629       <author>hello@requestmetrics.com (Request Metrics)</author>
          630     </item>
          631     
          632     
          633     <item>
          634       <title>Advertising's Performance Tradeoffs</title>
          635       <link>https://requestmetrics.com/web-performance/advertising-performance-tradeoffs</link>
          636       <guid isPermaLink="true">https://requestmetrics.com/web-performance/advertising-performance-tradeoffs</guid>
          637       <description>Advertising is everywhere on the web and users have noticed. More than 40% of internet users block ads. Are these users sticking it to the man or just tired of slow site performance? To find out, we measure advertising’s performance impact on a few popular sites.
          638 
          639 </description>
          640       <media:content url="https://requestmetrics.com/assets/images/webperf/perf-ads/advertising-performance-tradeoffs-2000.png" medium="image" type="image/png" height="350" width="700" />
          641       <enclosure url="https://requestmetrics.com/assets/images/webperf/perf-ads/advertising-performance-tradeoffs-2000.png" type="image/png" />
          642       <content:encoded><![CDATA[
          643         <div><img src="https://requestmetrics.com/assets/images/webperf/perf-ads/advertising-performance-tradeoffs-2000.png" alt="Advertising's Performance Tradeoffs" class="webfeedsFeaturedVisual" /></div>
          644         <p>Advertising is everywhere on the web and users have noticed. More than <a href="https://backlinko.com/ad-blockers-users">40% of internet users block ads</a>. Are these users sticking it to the man or just tired of slow site performance? To find out, we measure advertising’s performance impact on a few popular sites.</p>
          645 
          646 <!--more-->
          647 
          648 <h2 id="getting-real-measurements">Getting Real Measurements</h2>
          649 <p>We picked <a href="https://www.yahoo.com/">Yahoo</a>, <a href="https://www.reddit.com/">Reddit</a>, <a href="https://www.bbc.com/">BBC</a>, <a href="https://www.cnn.com/">CNN</a>, and <a href="https://www.nytimes.com/">The New York Times</a> because they have ads and are popular according to <a href="https://www.alexa.com/topsites">Alexa Top Sites</a>. Measurements were captured out of Chrome after loading each site in a fresh incognito window. Closing Chrome between runs to ensures there are no cached assets. We then reran the tests with <a href="https://ublockorigin.com/">uBlock Origin</a> enabled.</p>
          650 
          651 <h2 id="what-users-want">What Users Want</h2>
          652 <p>Users expect pages to show something quickly, finish loading fast and not to change under them once loaded.</p>
          653 
          654 <p>The user’s perception of page performance is influenced by many aspects of the page load, and no single metric can capture all of the performance experience. Because of this, we gathered Load Event timings along with core web vital metrics to get a better picture of each site’s performance and how it is impacted by advertisements.</p>
          655 
          656 <h3 id="quick-main-content-paints">Quick Main Content Paints</h3>
          657 <p>The more quickly a site can get main content pieces displayed to the user, the more snappy the site will feel. The <a href="https://requestmetrics.com/web-performance/first-contentful-paint-fcp">First Contentful Paint (FCP)</a> and <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint (LCP)</a> Core Web Vitals are a good measurements for this.</p>
          658 
          659 <p>Surprisingly, ads did not notably impact these first paints! To top it off, most sites fall into the “Good” range for these metrics. Clearly, a lot of effort is devoted to minimizing the time to render. Much of this is accomplished through deferring advertising loads till later in the life of the page, as we’ll see later.</p>
          660 
          661 <figure class="border">
          662   <img src="https://requestmetrics.com/assets/images/webperf/perf-ads/fcp-chart.png" loading="lazy" class="lazyload" alt="FCP is hardly impacted by ads" width="624" height="278" />
          663   <figcaption>FCP is hardly impacted by ads</figcaption>
          664 </figure>
          665 
          666 <figure class="border">
          667   <img src="https://requestmetrics.com/assets/images/webperf/perf-ads/lcp-chart.png" loading="lazy" class="lazyload" alt="LCP doesn't care about ads either" width="624" height="278" />
          668   <figcaption>LCP doesn't care about ads either</figcaption>
          669 </figure>
          670 
          671 <h3 id="fast-page-loads">Fast Page Loads</h3>
          672 <p>Pages with fast first paints still feel slow if the last piece of content takes a long time to load. Timing how long the page takes to fire the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event">load event</a> can tell us if this is happening.</p>
          673 
          674 <p>Less surprisingly, loading the entire page does take longer when ads are being shown. Some sites were worse than others, but all showed a negative performance impact from ads.</p>
          675 
          676 <figure class="border">
          677   <img src="https://requestmetrics.com/assets/images/webperf/perf-ads/load-chart.png" loading="lazy" class="lazyload" alt="Load times are heavily impacted by Advertisements" width="624" height="278" />
          678   <figcaption>Load times are heavily impacted by Advertisements </figcaption>
          679 </figure>
          680 
          681 <h3 id="no-jank">No Jank</h3>
          682 <p>Fast loading pages can still feel slow to a user if content is asynchronously rendered after the load is complete. Layout shifts, often referred to as “jank”, are a common side effect of asynchronous loads.</p>
          683 
          684 <figure>
          685     <video controls="" muted="" preload="metadata">
          686         <source src="/assets/images/webperf/perf-ads/bbc-jank.mp4" type="video/mp4" />
          687     </video>
          688     <figcaption>The BBC demonstrating some egregious layout shift.</figcaption>
          689 </figure>
          690 
          691 <p>All tested sites asynchronously loaded advertisements. This has the major advantage of keeping page loads and initial paints quick but comes at the expense of more jank. This is clearly reflected in <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Cumulative Layout Shift (CLS)</a> measurements.</p>
          692 
          693 <figure class="border">
          694   <img src="https://requestmetrics.com/assets/images/webperf/perf-ads/cls-chart.png" loading="lazy" class="lazyload" alt="Cumulative Layout Shift shows the advert jank" width="624" height="278" />
          695   <figcaption>Cumulative Layout Shift shows the advert jank</figcaption>
          696 </figure>
          697 
          698 <h2 id="no-metric-tells-the-whole-story">No Metric Tells the Whole Story</h2>
          699 <p>Advertisements don’t impact all aspects of page performance equally. This emphasizes the importance of a holistic understanding of performance metrics and the tradeoffs needed to improve a given metric.</p>
          700 
          701 <h2 id="performance-is-a-tradeoff">Performance Is a Tradeoff</h2>
          702 <p>While initial content renders were not notably impacted by ads, total page load and layout shifts were negatively effected by them. The jank alone probably drives a large number of internet users to the cozy arms of ad blockers.</p>
          703 
          704 <p>Many sites depend on the revenue from ads for their very survival. And yet, the tradeoff between first impression speed and jank is made by almost all sites containing ads. Have they made the right choice? Many users appear to think they have not.</p>
          705 
          706       ]]></content:encoded>
          707       <pubDate>Tue, 28 Sep 2021 00:00:00 +0000</pubDate>
          708       <author>hello@requestmetrics.com (Request Metrics)</author>
          709     </item>
          710     
          711     
          712     <item>
          713       <title>High Performance Images: 2021 Guide</title>
          714       <link>https://requestmetrics.com/web-performance/high-performance-images</link>
          715       <guid isPermaLink="true">https://requestmetrics.com/web-performance/high-performance-images</guid>
          716       <description>
          717 Images engage users, drive clicks, and generally make everything better–except performance. Images are giant blobs of bytes that are usually the slowest part of your website. This 2021 guide has everything you need to know for fast images on the web.
          718 
          719 
          720 
          721 Images are big. Really big. The bytes required for an image dwarf most site’s CSS and JavaScript assets. Slow images will damage your Core Web Vitals, impacting your SEO and costing you traffic. Images are usually the element driving Largest ...</description>
          722       <media:content url="https://requestmetrics.com/assets/images/webperf/high-performance-images/high-performance-images-2000.png" medium="image" type="image/png" height="350" width="700" />
          723       <enclosure url="https://requestmetrics.com/assets/images/webperf/high-performance-images/high-performance-images-2000.png" type="image/png" />
          724       <content:encoded><![CDATA[
          725         <div><img src="https://requestmetrics.com/assets/images/webperf/high-performance-images/high-performance-images-2000.png" alt="High Performance Images: 2021 Guide" class="webfeedsFeaturedVisual" /></div>
          726         
          727 <p>Images engage users, drive clicks, and generally make everything better–<em>except performance</em>. Images are giant blobs of bytes that are usually the slowest part of your website. This 2021 guide has everything you need to know for fast images on the web.</p>
          728 
          729 <!-- more -->
          730 
          731 <p>Images are big. Really big. The bytes required for an image dwarf most site’s CSS and JavaScript assets. Slow images will <strong>damage your Core Web Vitals</strong>, impacting your SEO and costing you traffic. Images are usually the element driving <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint (LCP)</a> and load delays can increase your <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Cumulative Layout Shift (CLS)</a>. If you’re not familiar with these metrics, check them out in the <strong><a href="https://requestmetrics.com/web-performance/measure-web-performance">Definitive Guide to Measuring Web Performance</a></strong>.</p>
          732 
          733 <figure class="border">
          734   <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">
          735     <img src="https://requestmetrics.com/assets/images/metrics/lcp_range_400.png" loading="lazy" class="lazyload" style="display:inline;" alt="Largest Contentful Paint" width="200" height="200" />
          736   </a>
          737   <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">
          738     <img src="https://requestmetrics.com/assets/images/metrics/cls_range_400.png" loading="lazy" class="lazyload" style="display:inline;" alt="Cumulative Layout Shift" width="200" height="200" />
          739   </a>
          740   <figcaption>LCP and CLS metric ranges</figcaption>
          741 </figure>
          742 
          743 <p>This guide covers optimizing your <em>image format</em>, <em>resolution</em>, <em>quality</em>, as well as <em>embedded images</em> and <em>lazy-loading</em>. Let’s get started!</p>
          744 
          745 <hr />
          746 
          747 <h2 id="1-image-format">1. Image Format</h2>
          748 
          749 <p>First and foremost, your images need to be in the correct format. Image formats are designed for a particular <em>kind</em> of image, so using the wrong format can make a big image even worse.</p>
          750 
          751 <p>A good general rule is to <strong>use jpg for photographs</strong> and <strong>use pngs for graphics</strong>. Here’s a quick example:</p>
          752 
          753 <figure class="border">
          754   <img src="https://requestmetrics.com/assets/images/webperf/high-performance-images/sloth_photograph_200.jpg" loading="lazy" class="lazyload" alt="sloth photograph" width="200" height="200" style="display:inline;" />
          755   <img src="https://requestmetrics.com/assets/images/webperf/high-performance-images/sloth_graphic_200.png" loading="lazy" class="lazyload" alt="sloth graphic" width="200" height="200" style="display:inline;" />
          756   <figcaption>Sloth Photograph and Graphic</figcaption>
          757 </figure>
          758 
          759 <p>On the left is a photograph of our buddy Sam, the sloth. As a JPG, the file is only <strong>32.7 kilobytes</strong>. Convert the same file to a PNG, and it more than doubles to <strong>90.6 kilobytes</strong>!</p>
          760 
          761 <p>The right is an illustration of Sam, and is better served as a PNG. It’s only <strong>5.5 kilobytes</strong>. But converting it to a JPG balloons it to <strong>11.3 kilobytes</strong>.</p>
          762 
          763 <p>Note that graphic illustrations tend to be significantly smaller than photographs. Be sure to consider this when designing the look and feel of your pages.</p>
          764 
          765 <p>There’s <a href="https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types">lots of other formats too</a>! If you have artwork as vectors (lines and paths), SVG is a perfect format. Newer browsers support newer formats as well, like AVIF and WebP, which might be even smaller.</p>
          766 
          767 <h2 id="2-responsive-image-resolution">2. Responsive Image Resolution</h2>
          768 
          769 <p>Your website will not be viewed the same way by everyone. Some of your users will have a huge 1600px wide display. Others may have a 900px tablet or a 600px phone. A 1200px wide image would need a lot of wasteful bytes for those smaller devices, where the image will get scaled down anyway.</p>
          770 
          771 <figure class="border">
          772         <img src="https://requestmetrics.com/assets/images/webperf/high-performance-images/responsive_images_370.png" srcset="https://requestmetrics.com/assets/images/webperf/high-performance-images/responsive_images_370.png   370w,
          773                  https://requestmetrics.com/assets/images/webperf/high-performance-images/responsive_images_740.png   740w,
          774                  https://requestmetrics.com/assets/images/webperf/high-performance-images/responsive_images_1480.png 1480w" sizes="(max-width: 700px) 100vw, 700px" loading="lazy" class="lazyload" alt="Responsive Image Resolution" height="832" width="1480" />
          775         <figcaption>Responsive Image Resolution</figcaption>
          776 </figure>
          777 
          778 <p>Why not scale down the images <em>before</em> your users download them? Use <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">HTML responsive attributes</a> to describe the sizes available for an image, and when to use them.</p>
          779 
          780 <figure class="code " id="code-259">
          781     <div class="code-wrap">
          782         <pre class="prettyprint lang-html">
          783 &lt;img src=&quot;picture-1200.jpg&quot;
          784   srcset=&quot;picture-600.jpg   600w,
          785           picture-900.jpg   900w,
          786           picture-1200.jpg 1200w&quot;
          787   sizes=&quot;(max-width: 900px) 100vw, 1200px&quot;
          788   alt=&quot;my awesome picture&quot; height=&quot;900&quot; width=&quot;1200&quot; /&gt;
          789 </pre>
          790         <!-- <button type="button" class="btn copy-button yes-js">
          791             <img src="/assets/images/copy.svg" alt="Copy" />
          792             <span class="label">Copy</span>
          793         </button> -->
          794         
          795         <figcaption><a href="#code-259">Using Responsive Image Markup</a></figcaption>
          796         
          797     </div>
          798 </figure>
          799 
          800 <p>In this case, the base image is 1200px wide, and that’s also the default <code class="language-plaintext highlighter-rouge">src</code>. The <code class="language-plaintext highlighter-rouge">srcset</code> defines 3 images at 600, 900, and 1200px wide. The <code class="language-plaintext highlighter-rouge">sizes</code> uses CSS media queries to hint the browser on the viewable size available for the image. If the window is less than 900px wide, the frame will be fullwidth, 100vw. Otherwise, the frame will never be bigger than 1200px wide.</p>
          801 
          802 <p>Most image tools, like Photoshop, <a href="https://www.gimp.org/">Gimp</a>, and <a href="https://www.getpaint.net/">Paint.NET</a>, can export images at multiple resolutions. Your native image viewer can probably do some limited resizing as well. To automate this on a large scale, you may want to consider a command line tool like <a href="https://imagemagick.org/index.php">ImageMagick</a>.</p>
          803 
          804 <h3 id="hiding-images-on-mobile">Hiding Images on Mobile</h3>
          805 
          806 <p>For some websites, you may not want to show an image on mobile devices at all because they are simply too big. Setting a <code class="language-plaintext highlighter-rouge">display:none</code> style on the image isn’t very helpful because the browser will still waste time and bytes downloading the image. Instead, you can use <code class="language-plaintext highlighter-rouge">sizes</code> to tell the browser when the image will not be shown.</p>
          807 
          808 <figure class="code " id="code-269">
          809     <div class="code-wrap">
          810         <pre class="prettyprint lang-html">
          811 &lt;img src=&quot;picture-1200.jpg&quot;
          812   srcset=&quot;picture-600.jpg   600w,
          813           picture-900.jpg   900w,
          814           picture-1200.jpg 1200w&quot;
          815   sizes=&quot;(max-width: 600px) 0, 600px&quot;
          816   alt=&quot;my awesome picture&quot; height=&quot;900&quot; width=&quot;1200&quot; /&gt;
          817 </pre>
          818         <!-- <button type="button" class="btn copy-button yes-js">
          819             <img src="/assets/images/copy.svg" alt="Copy" />
          820             <span class="label">Copy</span>
          821         </button> -->
          822         
          823         <figcaption><a href="#code-269">Hiding images on mobile and saving bandwidth</a></figcaption>
          824         
          825     </div>
          826 </figure>
          827 
          828 <p>For screen sizes less than 600px, the frame of the image is 0px wide. So the browser knows it doesn’t have to bother downloading anything because there is nothing to show.</p>
          829 
          830 <h2 id="3-image-quality">3. Image Quality</h2>
          831 
          832 <p>Aside from image format and resolution, there are often settings to adjust the <em>quality</em> of the image using <strong>lossy compression</strong>. These are algorithms that remove parts of an image that you wouldn’t notice, but still take up space. Check out this example:</p>
          833 
          834 <figure class="border">
          835         <img src="https://requestmetrics.com/assets/images/webperf/high-performance-images/image_optimize.png" loading="lazy" class="lazyload" alt="Image Optimization" width="740" height="334" />
          836         <figcaption>Image Optimization</figcaption>
          837 </figure>
          838 
          839 <p>This reduction is accomplished by pulling out unused colors, or by combining colors and pixels that too similar to notice. But you don’t need to worry about that, most optimization tools can detect the appropriate level of quality for an image. <a href="https://tinypng.com/">TinyPNG</a> and <a href="https://github.com/imagemin/imagemin">ImageMin</a> are great for this.</p>
          840 
          841 <!-- class="wide">
          842   <div class="wrap callout left flex">
          843     <div class="picture">
          844       <img loading="lazy" class="lazyload" src="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png" srcset="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png   600w,
          845                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-900.min.png   900w,
          846                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-1200.min.png 1200w" sizes="(max-width: 900px) 100vw, 400px" alt="Core Web Vital Performance Metrics" width="600" height="450" />
          847       </div>
          848       <div class="blurb">
          849       <h2>Core Web Vital Performance Monitoring</h2>
          850       <p>
          851         Let us handle your performance work. Monitor your real-user Core Web Vital metrics with
          852         <strong>Request Metrics</strong>. The fastest, easiest, and cheapest way to build a faster website.
          853       </p>
          854       <div class="flex cta-buttons">
          855         <a class="btn btn-big btn-grey" href="https://requestmetrics.com/core-web-vitals">
          856           Learn about the Core Web Vitals
          857         </a>
          858       </div>
          859     </div>
          860   </div>
          861 -->
          862 
          863 <h2 id="4-embedding-images">4. Embedding Images</h2>
          864 
          865 <p>Sometimes an image is essential for a webpage to be useful, such as a button, logo, or icon. Once you’ve optimized it as small as you can make it, the only way to go faster is to <em>embed the image on the page</em>. This will save a network request and show an image as soon as the document starts rendering.</p>
          866 
          867 <p>You embed an image by converting it into a <em>base64 string</em> and putting it right in the html tag, like this:</p>
          868 
          869 <figure class="code " id="code-210">
          870     <div class="code-wrap">
          871         <pre class="prettyprint lang-html">
          872 &lt;img src=&quot;data:image/png;base64; iVBORw0KGgoAAAANSUhEUgAAAAUA
          873     AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
          874         9TXL0Y4OHwAAAABJRU5ErkJggg==&quot;
          875   alt=&quot;my awesome picture&quot; /&gt;
          876 </pre>
          877         <!-- <button type="button" class="btn copy-button yes-js">
          878             <img src="/assets/images/copy.svg" alt="Copy" />
          879             <span class="label">Copy</span>
          880         </button> -->
          881         
          882         <figcaption><a href="#code-210">Embedded image</a></figcaption>
          883         
          884     </div>
          885 </figure>
          886 
          887 <p>This may look strange, but it’s 100% supported as a <em>data url</em>. The <code class="language-plaintext highlighter-rouge">src</code> defines the format as a PNG image thats base64 encoded. The remainder is the actual contents of the image, in this case a small red dot.</p>
          888 
          889 <figure class="border">
          890   <img src="data:image/png;base64,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" alt="Embedded HTML Image" />
          891   <figcaption>Example HTML embedded image</figcaption>
          892 </figure>
          893 
          894 <p>Pop open devtools and look at the source of the image above, it’s embedded! It’s also quite long, over 9,000 characters. It will download faster than an image reference, but it also slows down the document itself, so use this technique sparingly.</p>
          895 
          896 <p>Google uses embedded images frequently in the display of search results. One of the many reasons Google feels so fast. <a href="https://requestmetrics.com/web-performance/performance-profiling-google">Check out our review of Google’s performance</a> and what you can learn from it.</p>
          897 
          898 <p>Here’s a <a href="https://www.base64-image.de/">handy web tool for converting your images to base64</a>.</p>
          899 
          900 <h2 id="5-lazy-loading-images">5. Lazy-Loading Images</h2>
          901 
          902 <p>Websites have a lot of images that don’t need to be loaded right away. But the browser doesn’t know what images it will need, so its gotta download them all! Downloading the extra images creates lag and network congestion that slows down the critical images.</p>
          903 
          904 <p>Instead, tell the browser to be lazy! Lazy-loading is a technique to tell the browser to wait to download certain images until they are visible to the user. This can have <em>huge</em> impacts to <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint (LCP)</a> performance because the browser can focus on the critical elements to render.</p>
          905 
          906 <p>The HTML spec is experimenting with a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading"><code class="language-plaintext highlighter-rouge">loading</code> attribute</a> for lazy-loading images, but it is still experimental. Safari doesn’t support it yet, so it’s not particularly useful for most websites yet. Fortunately, <em>we can do it with JavaScript!</em></p>
          907 
          908 <p>There are a few lazy-loading libraries, like <a href="https://github.com/aFarkas/lazysizes">lazysizes</a>. These libraries handle a lot of the edge cases and browser compatibility of doing this, but essentially they run code like this:</p>
          909 
          910 <figure class="code " id="code-459">
          911     <div class="code-wrap">
          912         <pre class="prettyprint lang-javascript">
          913 var lazyEls = [].slice.call(document.querySelectorAll(&quot;[data-src]&quot;));
          914 
          915 var lazyObserver = new IntersectionObserver(function(entries) {
          916   entries.forEach(function(entry) {
          917     if (entry.isIntersecting) {
          918       var el = entry.target;
          919       var src = el.getAttribute(&quot;data-src&quot;);
          920       if (src) { el.setAttribute(&quot;src&quot;, src); }
          921       lazyObserver.unobserve(el);
          922     }
          923   });
          924 });
          925 
          926 lazyEls.forEach(function(el) {
          927   lazyObserver.observe(el);
          928 });
          929 </pre>
          930         <!-- <button type="button" class="btn copy-button yes-js">
          931             <img src="/assets/images/copy.svg" alt="Copy" />
          932             <span class="label">Copy</span>
          933         </button> -->
          934         
          935         <figcaption><a href="#code-459">Lazy-loading images</a></figcaption>
          936         
          937     </div>
          938 </figure>
          939 
          940 <p>This code uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">IntersectionObserver</a> to detect when an element is visible. When an image is visible, it moves the <code class="language-plaintext highlighter-rouge">data-src</code> attribute to <code class="language-plaintext highlighter-rouge">src</code>, and the image downloads. You can apply the same pattern to <code class="language-plaintext highlighter-rouge">srcset</code> and for any number of elements.</p>
          941 
          942 <p>You utilize this by renaming the <code class="language-plaintext highlighter-rouge">src</code> attribute to <code class="language-plaintext highlighter-rouge">data-src</code></p>
          943 
          944 <figure class="code " id="code-100">
          945     <div class="code-wrap">
          946         <pre class="prettyprint lang-html">
          947 &lt;img src=&quot;picture-1200.jpg&quot;
          948   loading=&quot;lazy&quot; class=&quot;lazy&quot; /&gt;
          949 </pre>
          950         <!-- <button type="button" class="btn copy-button yes-js">
          951             <img src="/assets/images/copy.svg" alt="Copy" />
          952             <span class="label">Copy</span>
          953         </button> -->
          954         
          955         <figcaption><a href="#code-100">Decorating an image for lazy load</a></figcaption>
          956         
          957     </div>
          958 </figure>
          959 
          960 <p>This markup uses both the experimental <code class="language-plaintext highlighter-rouge">loading</code> attribute as well as the generic attribute we can use with a JavaScript function or library.</p>
          961 
          962 <h3 id="layout-sizing">Layout Sizing</h3>
          963 
          964 <p>With late-loading images, it is even more important to specify the sizing of images to prevent <em>Layout Shift</em>. Layout shifts are when the positioning of elements changes during load because of dynamically sized content. <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Read more about layout shift and the impact on performance</a>.</p>
          965 
          966 <p>You prevent this by specifying a <code class="language-plaintext highlighter-rouge">height</code> and <code class="language-plaintext highlighter-rouge">width</code> attribute on your images.</p>
          967 
          968 <figure class="code " id="code-126">
          969     <div class="code-wrap">
          970         <pre class="prettyprint lang-html">
          971 &lt;img src=&quot;picture-1200.jpg&quot;
          972   loading=&quot;lazy&quot; class=&quot;lazy&quot;
          973   width=&quot;1200&quot; height=&quot;900&quot; /&gt;
          974 </pre>
          975         <!-- <button type="button" class="btn copy-button yes-js">
          976             <img src="/assets/images/copy.svg" alt="Copy" />
          977             <span class="label">Copy</span>
          978         </button> -->
          979         
          980         <figcaption><a href="#code-126">Specifying the height and width</a></figcaption>
          981         
          982     </div>
          983 </figure>
          984 
          985 <p>Notice that the height and width attributes. It’s not 1200px, it’s just 1200. It also doesn’t do exactly what you’d expect–the size of this element will not necessarily be 1200x900, it will be whatever the CSS styling and layout says it needs to be. But the browser remembers the <em>aspect ratio</em> from these attributes so that the height of an image will be correct given it’s width.</p>
          986 
          987 <p>So if your layout only has 800px wide for the image, the browser will know, without downloading the image, to reserve 600px of height as well to maintain the aspect ratio.</p>
          988 
          989 <hr />
          990 
          991 <h2 id="conclusion">Conclusion</h2>
          992 
          993 <p>These techniques will greatly speed your website and images. The correct format, resolution, quality, and load order for your images can transform the end user experience of your website for the better. Not sure how users see your website today? Try out <a href="https://requestmetrics.com/">performance monitoring from Request Metrics</a> to track your real-user performance metrics.</p>
          994 
          995       ]]></content:encoded>
          996       <pubDate>Tue, 25 May 2021 00:00:00 +0000</pubDate>
          997       <author>hello@requestmetrics.com (Request Metrics)</author>
          998     </item>
          999     
         1000     
         1001     <item>
         1002       <title>Fix Your First Contentful Paint: Cheat Sheet</title>
         1003       <link>https://requestmetrics.com/web-performance/fix-first-contentful-paint-fcp</link>
         1004       <guid isPermaLink="true">https://requestmetrics.com/web-performance/fix-first-contentful-paint-fcp</guid>
         1005       <description>
         1006 Are slow FCP scores getting you down? Worried that website performance is frustrating your users and hurting your SEO rankings? This FCP cheat sheet has all the tactics (with links) you’ll need to have screaming-fast FCP scores.
         1007 
         1008 First Contentful Paint (FCP) is a measurement of how long it takes to show the user the first bit of content. Measuring FCP encourages your website to respond quickly to requests so that users know their request has been received.
         1009 
         1010 If you’re not yet familiar with th...</description>
         1011       <media:content url="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/fixing-fcp-cheatsheet-2000.png" medium="image" type="image/png" height="350" width="700" />
         1012       <enclosure url="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/fixing-fcp-cheatsheet-2000.png" type="image/png" />
         1013       <content:encoded><![CDATA[
         1014         <div><img src="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/fixing-fcp-cheatsheet-2000.png" alt="Fix Your First Contentful Paint: Cheat Sheet" class="webfeedsFeaturedVisual" /></div>
         1015         
         1016 <p>Are slow FCP scores getting you down? Worried that website performance is frustrating your users and hurting your SEO rankings? This FCP cheat sheet has all the tactics (with links) you’ll need to have screaming-fast FCP scores.</p>
         1017 
         1018 <p>First Contentful Paint (FCP) is a measurement of how long it takes to show the user the first bit of content. Measuring FCP encourages your website to respond quickly to requests so that users know their request has been received.</p>
         1019 
         1020 <p>If you’re not yet familiar with the FCP metric, check out <a href="https://requestmetrics.com/web-performance/first-contentful-paint-fcp">Using First Contentful Paint</a> for a thorough description of the metric and measurement API.</p>
         1021 
         1022 <hr />
         1023 
         1024 <p>You can improve your FCP scores by making your <strong>servers quick</strong>, your <strong>resources small and few</strong>, and the <strong>network hops short</strong>. Plus, all of your improvements to FCP will also help your Largest Contentful Paint (LCP) score!</p>
         1025 
         1026 <figure class="border">
         1027   <img src="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/improving_fcp_740.png" srcset="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/improving_fcp_370.png   370w,
         1028                  https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/improving_fcp_740.png   740w,
         1029                  https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/improving_fcp_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Improving FCP Scores" width="740" height="416" />
         1030   <figcaption>Improving FCP Scores</figcaption>
         1031 </figure>
         1032 
         1033 <p><strong>Here are the 6 best ways to improve your FCP and LCP scores</strong>:</p>
         1034 
         1035 <h2 id="1-reduce-server-work">1. Reduce Server Work</h2>
         1036 
         1037 <p>Whether your server is WordPress, ASP.NET, or a node.js service running in AWS, it needs to respond quick to user requests. Assuming that your server has sufficient capacity for your traffic, you can only make it faster by <em>doing fewer things</em>.</p>
         1038 
         1039 <p>Do you make database requests return static HTML content? You can save a <strong>ton</strong> of time by caching the HTML and returning <em>static content</em>. <a href="https://wordpress.org/plugins/wp-super-cache/">Looking at you WordPress</a>.</p>
         1040 
         1041 <p>Maybe your server is making requests to some API to assemble the document. Consider caching the API responses so you don’t need to do it every time. You can apply this if your server is doing expensive calculations as well, cache those results! Use <a href="https://redis.io/">Redis</a> or <a href="https://github.com/google/leveldb">LevelDB</a> to create a “<a href="https://dzone.com/articles/using-read-through-amp-write-through-in-distribute">read-through caching</a>” pattern that can speed everything up. <!-- , [and many other things](TODO)! --></p>
         1042 
         1043 <h2 id="2-return-smaller-documents">2. Return Smaller Documents</h2>
         1044 
         1045 <p>Every byte returned by your server needs to be transferred, checked, assembled, parsed, and rendered. The best way to make that faster is to send fewer bytes.</p>
         1046 
         1047 <p>Compression is an easy way to make your documents smaller without much thought. Nearly all web platforms can return content with gzip compression (here’s how to <a href="https://docs.nginx.com/nginx/admin-guide/web-server/compression/">enable compression in nginx</a> and <a href="https://httpd.apache.org/docs/2.4/mod/mod_deflate.html">apache</a>). Some will support the newer Brotli compression.</p>
         1048 
         1049 <p>Compression makes sense for documents larger than about <strong>4 kilobytes</strong>. Smaller documents may not see a reduction significant enough to overcome the compression cost.</p>
         1050 
         1051 <p>Depending on the kind of file you’re returning, there may be other ways to optimize its size. Optimizing your images to be sized and formatted correctly can be a huge win. <a href="https://tinypng.com/">TinyPNG</a> and <a href="https://github.com/imagemin/imagemin">ImageMin</a> are great for this.</p>
         1052 
         1053 <figure class="border">
         1054   <img src="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/image_optimize.png" loading="lazy" class="lazyload" alt="Image Optimization" width="740" height="334" />
         1055   <figcaption>Image Optimization</figcaption>
         1056 </figure>
         1057 
         1058 <h2 id="3-send-less-css">3. Send Less CSS</h2>
         1059 
         1060 <p>Most websites have an obnoxious amount of files needed before the browser can start painting content. The browser needs to have all your styles to know how to layout your content, so nothing happens until <strong>all CSS files are downloaded</strong>.</p>
         1061 
         1062 <p>Maybe you don’t need all of that, at least not at the beginning.</p>
         1063 
         1064 <p>If you have your whole website’s CSS in a single file, it’s likely way bigger than it should be. Break it apart into core styles and page-specific styles. In many cases, a few smaller CSS files is faster than a single huge one.</p>
         1065 
         1066 <p>When you break apart your CSS, avoid using <code class="language-plaintext highlighter-rouge">@import</code>, which chains CSS files together. <code class="language-plaintext highlighter-rouge">@import</code> forces the browser to download each CSS file serially as the previous is parsed. Unless you have a huge site with lots of modular concepts, this probably doesn’t make sense. Use a build tool like <a href="https://sass-lang.com/">SASS</a> to bundle imported styles together.</p>
         1067 
         1068 <p>If your CSS is still huge, consider hunting down unused styles from your code, or your frameworks. Check out <a href="https://purgecss.com/">PurgeCSS</a> to analyze your CSS and rip out the bits that aren’t needed for your page.</p>
         1069 
         1070 <!-- class="wide">
         1071   <div class="wrap callout left flex">
         1072     <div class="picture">
         1073       <img loading="lazy" class="lazyload" src="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png" srcset="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png   600w,
         1074                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-900.min.png   900w,
         1075                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-1200.min.png 1200w" sizes="(max-width: 900px) 100vw, 400px" alt="Core Web Vital Performance Metrics" width="600" height="450" />
         1076       </div>
         1077       <div class="blurb">
         1078       <h2>FCP Performance Monitoring</h2>
         1079       <p>
         1080         Let us handle the hard stuff. Monitor your real-user metrics like First Contentful Paint with
         1081         <strong>Request Metrics</strong>.
         1082       </p>
         1083       <div class="flex cta-buttons">
         1084         <a class="btn btn-big btn-grey" href="https://requestmetrics.com/core-web-vitals">
         1085           Learn about the Core Web Vitals
         1086         </a>
         1087       </div>
         1088     </div>
         1089   </div>
         1090 -->
         1091 
         1092 <h2 id="4-use-fewer-and-more-efficient-fonts">4. Use Fewer and More Efficient Fonts</h2>
         1093 
         1094 <p>Fonts are expensive to download, even if you’re using <a href="https://fonts.google.com/">Google Fonts</a>. Try to use fewer fonts, maybe even a <a href="https://web.dev/variable-fonts/">variable font</a>. If you’re having trouble with FCP scores on mobile devices, try removing custom fonts from those devices with a <code class="language-plaintext highlighter-rouge">media</code> attribute.</p>
         1095 
         1096 <figure class="code " id="code-139">
         1097     <div class="code-wrap">
         1098         <pre class="prettyprint lang-html">
         1099 &lt;link href=&quot;https://fonts.googleapis.com/...&quot; rel=&quot;stylesheet&quot;
         1100         media=&quot;only screen and (min-width: 600px)&quot;&gt;
         1101 </pre>
         1102         <!-- <button type="button" class="btn copy-button yes-js">
         1103             <img src="/assets/images/copy.svg" alt="Copy" />
         1104             <span class="label">Copy</span>
         1105         </button> -->
         1106         
         1107         <figcaption><a href="#code-139">Using Media Queries with Fonts</a></figcaption>
         1108         
         1109     </div>
         1110 </figure>
         1111 
         1112 <p>Most mobile devices have a good set of default fonts, and the savings from skipping custom fonts on mobile networks can be huge.</p>
         1113 
         1114 <h2 id="5-serve-content-through-a-cdn">5. Serve Content Through a CDN</h2>
         1115 
         1116 <p>Regardless of the size of your documents, they all need to cross the Internet to get to the user. Sometimes, those users are really far away, and it can take a long time.</p>
         1117 
         1118 <p>If your servers are in San Francisco and the user is in New York, it <em>will</em> take more than <strong>100 milliseconds</strong> simply for the bytes to move through the Internet. Over dozens of requests, that delay can really add up. Stupid physics.</p>
         1119 
         1120 <figure class="border">
         1121   <img src="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/using_cdn_740.png" srcset="https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/using_cdn_370.png   370w,
         1122                  https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/using_cdn_740.png   740w,
         1123                  https://requestmetrics.com/assets/images/webperf/fix-first-contentful-paint/using_cdn_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Saving Network Hops with a CDN" width="740" height="416" />
         1124   <figcaption>Saving Network Hops with a CDN</figcaption>
         1125 </figure>
         1126 
         1127 <p>Using a CDN allows <em>some</em> of your documents to be stored near your users, which reduces the network delay. It can also reduce the load on your servers and make everything else run faster. <a href="https://www.cloudflare.com/">Cloudflare</a> is a great CDN, but you have to give them control of your DNS. <a href="https://www.stackpath.com/maxCDN">MaxCDN</a> is a good alternative.</p>
         1128 
         1129 <h2 id="6-set-browser-caching">6. Set Browser Caching</h2>
         1130 
         1131 <p>Once a user has waited for your webpage to load once, don’t make them wait again! You can tell their browser to remember (cache) your website’s resources by sending the <strong><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control"><code class="language-plaintext highlighter-rouge">Cache-Control</code></a></strong> HTTP headers with your requests.</p>
         1132 
         1133 <p>For example, if you’d like your users to remember an image for at least a week before downloading it again, you’d send this header:</p>
         1134 
         1135 <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Cache-Control: max-age=604800
         1136 </code></pre></div></div>
         1137 
         1138 <p>You can configure most platforms to attach these headers by the type of file being returned. Here’s how you would specify <a href="https://www.nginx.com/blog/nginx-caching-guide/">resource caching in nginx</a>.</p>
         1139 
         1140 <p>Warning! Browser caching can also cause problems when you change your website. If you tell users to cache your images, CSS, or JavaScript, changes to those files will not be seen until the browser cache expires. You can handle this using various <a href="https://css-tricks.com/strategies-for-cache-busting-css/">cache-busting techniques</a>.</p>
         1141 
         1142 <hr />
         1143 
         1144 <h2 id="conclusion">Conclusion</h2>
         1145 
         1146 <p>Apply these techniques to your website and your FCP scores will improve. Not sure what your FCP is? Try out <a href="https://requestmetrics.com/core-web-vitals">performance monitoring from Request Metrics</a> to understand your user’s experience on your website.</p>
         1147 
         1148       ]]></content:encoded>
         1149       <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
         1150       <author>hello@requestmetrics.com (Request Metrics)</author>
         1151     </item>
         1152     
         1153     
         1154     <item>
         1155       <title>Using First Contentful Paint - FCP</title>
         1156       <link>https://requestmetrics.com/web-performance/first-contentful-paint-fcp</link>
         1157       <guid isPermaLink="true">https://requestmetrics.com/web-performance/first-contentful-paint-fcp</guid>
         1158       <description>
         1159 First Contentful Paint, or FCP, measures the time take to render the first element of a webpage. It’s a modern, user-centric measurement of how fast users see a response from your website. Here’s everything you need to know about the metric and how to use it.
         1160 
         1161 </description>
         1162       <media:content url="https://requestmetrics.com/assets/images/webperf/fcp/using-fcp-2000.png" medium="image" type="image/png" height="350" width="700" />
         1163       <enclosure url="https://requestmetrics.com/assets/images/webperf/fcp/using-fcp-2000.png" type="image/png" />
         1164       <content:encoded><![CDATA[
         1165         <div><img src="https://requestmetrics.com/assets/images/webperf/fcp/using-fcp-2000.png" alt="Using First Contentful Paint - FCP" class="webfeedsFeaturedVisual" /></div>
         1166         
         1167 <p>First Contentful Paint, or FCP, measures the time take to render the first element of a webpage. It’s a modern, user-centric measurement of how fast users see a response from your website. Here’s everything you need to know about the metric and how to use it.</p>
         1168 
         1169 <!--more-->
         1170 
         1171 <p>FCP is one of the <strong>Core Web Vital</strong> performance metrics that measure user’s perceived performance of websites. These metrics are incredibly important to delivering fast user experiences, and <a href="https://developers.google.com/search/blog/2020/05/evaluating-page-experience">avoiding SEO performance penalties</a>. Check out how FCP compares with other performance metrics in <a href="https://requestmetrics.com/web-performance/measure-web-performance">The Definitive Guide to Measuring Web Performance</a>.</p>
         1172 
         1173 <h2 id="first-contentful-paint-metric">First Contentful Paint Metric</h2>
         1174 
         1175 <p>The First Contentful Paint metric considers all the steps required to show the user the first bit of requested content. This includes the time for your servers to respond, network transfer time, HTML size and complexity, and required CSS assets.</p>
         1176 
         1177 <p>In this case, <em>Contentful</em> refers to any text or image elements, but not the background color of the document.</p>
         1178 
         1179 <p>Check out this example <strong>waterfall chart</strong>, and where FCP is marked.</p>
         1180 
         1181 <figure class="border">
         1182   <img src="https://requestmetrics.com/assets/images/webperf/fcp/fcp_loading_waterfall_740.png" srcset="https://requestmetrics.com/assets/images/webperf/fcp/fcp_loading_waterfall_370.png   370w,
         1183                  https://requestmetrics.com/assets/images/webperf/fcp/fcp_loading_waterfall_740.png   740w,
         1184                  https://requestmetrics.com/assets/images/webperf/fcp/fcp_loading_waterfall_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="FCP in the Loading Waterfall" width="740" height="416" />
         1185   <figcaption>FCP in the Loading Waterfall</figcaption>
         1186 </figure>
         1187 
         1188 <p>Loading webpage images is not needed for FCP, but the browser does need to download all the CSS styles, including styles chained with <code class="language-plaintext highlighter-rouge">@import</code> statements. Nesting multiple levels of styles can dramatically slow down your FCP performance.</p>
         1189 
         1190 <figure class="border">
         1191   <img src="https://requestmetrics.com/assets/images/metrics/fcp_range_400.png" loading="lazy" class="lazyload" alt="FCP Metric Range" width="400" height="400" />
         1192   <figcaption>FCP Metric Range</figcaption>
         1193 </figure>
         1194 
         1195 <p>Google defines the acceptable ranges for FCP to be less than <strong>1.0 seconds</strong>. Anything over that risks a negative user experience and a possible ranking penalty. FCP scores larger than <strong>3.0 seconds</strong> indicate a serious performance problem for your website.</p>
         1196 
         1197 <h2 id="measuring-fcp-with-performanceobserver">Measuring FCP with PerformanceObserver</h2>
         1198 
         1199 <p>First Contentful Paint is measured using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver"><code class="language-plaintext highlighter-rouge">PerformanceObserver</code></a> API and is supported in Blink-based browsers, such as Chrome, Edge, Android, and Opera. Other browsers, including Chrome on iOS, Safari, and Firefox, cannot report FCP metrics.</p>
         1200 
         1201 <p>Here’s a little code to illustrate the API:</p>
         1202 
         1203 <figure class="code " id="code-197">
         1204     <div class="code-wrap">
         1205         <pre class="prettyprint lang-javascript">
         1206 new PerformanceObserver((entryList) =&gt; {
         1207     console.log(entryList.getEntriesByName(&quot;first-contentful-paint&quot;));
         1208 }).observe({ type: &quot;paint&quot;, buffered: true });
         1209 </pre>
         1210         <!-- <button type="button" class="btn copy-button yes-js">
         1211             <img src="/assets/images/copy.svg" alt="Copy" />
         1212             <span class="label">Copy</span>
         1213         </button> -->
         1214         
         1215         <figcaption><a href="#code-197">Example of First Contentful Paint API</a></figcaption>
         1216         
         1217     </div>
         1218 </figure>
         1219 
         1220 <p>Unlike the <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint</a> metric, there is not a dedicated type for FCP. You must listen to all paint events and filter them by name. The <code class="language-plaintext highlighter-rouge">buffered</code> option allows you to gather data after it has happened. Paste in that code into DevTools on any page and you’ll see something like this:</p>
         1221 
         1222 <figure class="border">
         1223   <img src="https://requestmetrics.com/assets/images/webperf/fcp/fcp_observer_api.png" loading="lazy" class="lazyload" alt="FCP Performance Entry" width="600" height="271" />
         1224   <figcaption>FCP Performance Entry</figcaption>
         1225 </figure>
         1226 
         1227 <p>Note that the measurement value is <code class="language-plaintext highlighter-rouge">startTime</code>, not <code class="language-plaintext highlighter-rouge">duration</code>. <code class="language-plaintext highlighter-rouge">startTime</code> is when the FCP event <em>started</em>, while <em>duration</em> is how long the event lasted, which is usually 0.</p>
         1228 
         1229 <h3 id="quirks-gotchas-and-unexpected-behavior">Quirks, Gotchas, and Unexpected Behavior</h3>
         1230 
         1231 <p>Likely, you won’t be measuring FCP yourself. You’ll rely on a library like <a href="https://github.com/GoogleChrome/web-vitals">web-vitals</a> or a service like <strong><a href="/">Request Metrics</a></strong> to gather the data for you. But if you do want to measure FCP yourself, there are a few unexpected things you should look out for.</p>
         1232 
         1233 <!-- class="wide">
         1234   <div class="wrap callout left flex">
         1235     <div class="picture">
         1236       <img loading="lazy" class="lazyload" src="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png" srcset="https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-600.min.png   600w,
         1237                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-900.min.png   900w,
         1238                      https://requestmetrics.com/assets/images/callouts/callout-webvitals-aside-1200.min.png 1200w" sizes="(max-width: 900px) 100vw, 400px" alt="Core Web Vital Performance Metrics" width="600" height="450" />
         1239       </div>
         1240       <div class="blurb">
         1241       <h2>FCP Performance Monitoring</h2>
         1242       <p>
         1243         Let us handle the hard stuff. Monitor your real-user metrics like First Contentful Paint with
         1244         <strong>Request Metrics</strong>.
         1245       </p>
         1246       <div class="flex cta-buttons">
         1247         <a class="btn btn-big btn-grey" href="https://requestmetrics.com/core-web-vitals">
         1248           Learn about the Core Web Vitals
         1249         </a>
         1250       </div>
         1251     </div>
         1252   </div>
         1253 -->
         1254 
         1255 <h4 id="1-dont-measure-background-pages">1. Don’t Measure Background Pages</h4>
         1256 
         1257 <p>Pages that are loaded in the background, such as opened in another tab or while minimized, will not have accurate FCP measurements. The <code class="language-plaintext highlighter-rouge">first-contentful-paint</code> performance entry is fired once the page is viewed, which will be significantly slower than the actual loading time of the page.</p>
         1258 
         1259 <p>You can detect whether a page is in the background and filter out metrics:</p>
         1260 
         1261 <figure class="code " id="code-550">
         1262     <div class="code-wrap">
         1263         <pre class="prettyprint lang-javascript">
         1264 var hiddenTime = document.visibilityState === &#39;hidden&#39; ? 0 : Infinity;
         1265 
         1266 document.addEventListener(&#39;visibilitychange&#39;, (event) =&gt; {
         1267     hiddenTime = Math.min(hiddenTime, event.timeStamp);
         1268 }, { once: true });
         1269 
         1270 new PerformanceObserver(entryList =&gt; {
         1271     entryList.getEntriesByName(&quot;first-contentful-paint&quot;).forEach((entry) =&gt; {
         1272         if (entry.startTime &lt; hiddenTime) {
         1273             // This entry occurred before the page was hidden
         1274             console.log(entry);
         1275         }
         1276     };
         1277 }).observe({ type: &quot;paint&quot;, buffered: true });
         1278 </pre>
         1279         <!-- <button type="button" class="btn copy-button yes-js">
         1280             <img src="/assets/images/copy.svg" alt="Copy" />
         1281             <span class="label">Copy</span>
         1282         </button> -->
         1283         
         1284         <figcaption><a href="#code-550">Don't Background Pages</a></figcaption>
         1285         
         1286     </div>
         1287 </figure>
         1288 
         1289 <h4 id="2-always-use-feature-detection">2. Always Use Feature Detection</h4>
         1290 
         1291 <p>Many (most) browser do not support <code class="language-plaintext highlighter-rouge">PerformanceObserver</code> or the <code class="language-plaintext highlighter-rouge">paint</code> type. Some browsers, the object is not defined, while others will throw an error if you attempt to use it. Try/catch is the most reliable way to detect browser compatibility.</p>
         1292 
         1293 <figure class="code " id="code-317">
         1294     <div class="code-wrap">
         1295         <pre class="prettyprint lang-javascript">
         1296 try {
         1297     new PerformanceObserver(entryList =&gt; {
         1298         console.log(entryList.getEntriesByName(&quot;first-contentful-paint&quot;));
         1299     })
         1300     // Some browsers throw when &#39;type&#39; is passed:
         1301     .observe({ type: &quot;paint&quot;, buffered: true });
         1302 }
         1303 catch (e) { /* Not Supported */ }
         1304 </pre>
         1305         <!-- <button type="button" class="btn copy-button yes-js">
         1306             <img src="/assets/images/copy.svg" alt="Copy" />
         1307             <span class="label">Copy</span>
         1308         </button> -->
         1309         
         1310         <figcaption><a href="#code-317">Detect Largest Contentful Paint API with try/catch</a></figcaption>
         1311         
         1312     </div>
         1313 </figure>
         1314 
         1315 <h4 id="3-styles-to-the-document-dont-count">3. Styles to the Document Don’t Count</h4>
         1316 
         1317 <p>Some pages will apply inline styles to their <code class="language-plaintext highlighter-rouge">html</code> or <code class="language-plaintext highlighter-rouge">body</code> elements, such as background-color, borders, or outlines. While these styles do <em>paint</em> to the screen, they are not considered for the first-contentful-paint metric.</p>
         1318 
         1319 <h2 id="improving-your-fcp-scores">Improving Your FCP Scores</h2>
         1320 
         1321 <p>FCP includes all the time waiting and fetching the document, CSS files, and synchronous scripts. You can improve your FCP scores by making your servers quick, your resources small and few, and the network hops short. Here are some common tactics that can help:</p>
         1322 
         1323 <ul>
         1324   <li>Reduce server work and cache expensive actions</li>
         1325   <li>Use compression on your HTML, CSS, and other resources</li>
         1326   <li>Reduce the critical CSS required to render the page</li>
         1327   <li>Use fewer and more efficient fonts</li>
         1328   <li>Serve content through a CDN</li>
         1329   <li>Use efficient http caching settings</li>
         1330 </ul>
         1331 
         1332 <p>All the improvements that you make to FCP will also help your <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint</a> scores. For more about improving your FCP and details on each of these tactics, checkout <a href="https://requestmetrics.com/web-performance/fix-first-contentful-paint-fcp">Fixing Your FCP</a>.</p>
         1333 
         1334 <h2 id="conclusion">Conclusion</h2>
         1335 
         1336 <p>The First Contentful Paint metric is an important part of the web performance story, but it’s not the only one! Learn more about the other web performance metrics and how to measure them in the <a href="https://requestmetrics.com/web-performance/measure-web-performance">Definitive Guide to Measuring Web Performance</a>.</p>
         1337 
         1338       ]]></content:encoded>
         1339       <pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate>
         1340       <author>hello@requestmetrics.com (Request Metrics)</author>
         1341     </item>
         1342     
         1343     
         1344     <item>
         1345       <title>Measuring Web Performance in 2021: The Definitive Guide</title>
         1346       <link>https://requestmetrics.com/web-performance/measure-web-performance</link>
         1347       <guid isPermaLink="true">https://requestmetrics.com/web-performance/measure-web-performance</guid>
         1348       <description>
         1349 This is the complete guide to the metrics, methods, and measurements of web performance in 2021.
         1350 
         1351 If you run a website, this guide has all the fundamental ideas you need to understand to build a fast website for your users, and for search engines.
         1352 
         1353 
         1354 
         1355 
         1356 
         1357 Contents
         1358 
         1359 
         1360         
         1361                 
         1362                         
         1363                                 
         1364                         
         1365                         Chapter 1
         1366                         Web Performance Fundamentals
         1367                 
         1368         
         1369         
         1370                 
         1371                         
         1372                                 
         1373                         
         1374                         Chapter 2
         1375                         Web Performance Metrics
         1376                 
         1377         
         1378         
         1379                 
         1380                         
         1381                                 
         1382                         
         1383                         Chapter 3
         1384                         Kinds of Web Performance Data
         1385                 
         1386         
         1387         
         1388                 
         1389                         
         1390                                 
         1391                         
         1392                         Chapter 4
         1393         ...</description>
         1394       <media:content url="https://requestmetrics.com/assets/images/webperf/measure-web-performance/title-2000.png" medium="image" type="image/png" height="350" width="700" />
         1395       <enclosure url="https://requestmetrics.com/assets/images/webperf/measure-web-performance/title-2000.png" type="image/png" />
         1396       <content:encoded><![CDATA[
         1397         <div><img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/title-2000.png" alt="Measuring Web Performance in 2021: The Definitive Guide" class="webfeedsFeaturedVisual" /></div>
         1398         
         1399 <p>This is the complete guide to the metrics, methods, and measurements of web performance in 2021.</p>
         1400 
         1401 <p>If you run a website, this guide has all the fundamental ideas you need to understand to build a fast website for your users, and for search engines.</p>
         1402 
         1403 <!-- more -->
         1404 
         1405 <hr />
         1406 
         1407 <h2 style="border: none;">Contents</h2>
         1408 
         1409 <ol class="toc">
         1410         <li class="toc-item">
         1411                 <a href="#chapter-1-web-performance-fundamentals">
         1412                         <div class="chapter-img blue">
         1413                                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_fundamentals_280.png" alt="Web Performance Fundamentals" width="240" height="240" />
         1414                         </div>
         1415                         <div class="chapter-num">Chapter 1</div>
         1416                         <div class="chapter-text">Web Performance Fundamentals</div>
         1417                 </a>
         1418         </li>
         1419         <li class="toc-item">
         1420                 <a href="#chapter-2-web-performance-metrics">
         1421                         <div class="chapter-img green">
         1422                                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_measurements_280.png" alt="Web Performance Metrics" width="240" height="240" />
         1423                         </div>
         1424                         <div class="chapter-num">Chapter 2</div>
         1425                         <div class="chapter-text">Web Performance Metrics</div>
         1426                 </a>
         1427         </li>
         1428         <li class="toc-item">
         1429                 <a href="#chapter-3-kinds-of-web-performance-data">
         1430                         <div class="chapter-img red">
         1431                                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_data_280.png" alt="Kinds of Web Performance Data" width="240" height="240" />
         1432                         </div>
         1433                         <div class="chapter-num">Chapter 3</div>
         1434                         <div class="chapter-text">Kinds of Web Performance Data</div>
         1435                 </a>
         1436         </li>
         1437         <li class="toc-item">
         1438                 <a href="#chapter-4-common-web-performance-tools">
         1439                         <div class="chapter-img indigo">
         1440                                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_tools_280.png" alt="Common Web Performance Tools" width="240" height="240" />
         1441                         </div>
         1442                         <div class="chapter-num">Chapter 4</div>
         1443                         <div class="chapter-text">Common Web Performance Tools</div>
         1444                 </a>
         1445         </li>
         1446 
         1447 </ol>
         1448 
         1449 <h2 class="blue" id="chapter-1-web-performance-fundamentals"><small>Chapter 1:</small><br /> Web Performance Fundamentals</h2>
         1450 <div class="chapter-intro blue">
         1451         <div class="text">
         1452                 <p>
         1453                         In this first chapter, we'll start with the basics of web performance, what it is, and why you need to care about it.
         1454                 </p>
         1455                 <p>
         1456                         You'll see why web performance has never been more important. Let's go!
         1457                 </p>
         1458         </div>
         1459         <div class="illustration">
         1460                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_fundamentals_280.png" loading="lazy" class="lazyload" alt="Web Performance Fundamentals" width="280" height="280" />
         1461         </div>
         1462 </div>
         1463 
         1464 <h3 id="what-is-web-performance">What is Web Performance?</h3>
         1465 
         1466 <p>Web performance is about how fast your website <em>feels</em> to your users. A slow website causes frustration by slowing down the user doing their work. These user feelings are sometimes called <strong>Perceived Performance</strong>.</p>
         1467 
         1468 <p>Feelings are difficult to measure. Each person can have a different expectation for how fast a website should be, based on what the user is doing and the kind of website. Users probably won’t wait 6 seconds for a click-bait story, but will wait 10 seconds or longer for Gmail to start.</p>
         1469 
         1470 <p><a href="https://www.youtube.com/watch?v=7i_yQyHdxUo">Simon Hearne shared</a> that in the psychology work in <a href="https://davidmaister.com/articles/the-psychology-of-waiting-lines/">“The Psychology of Waiting Lines”</a>, David Maister defines 6 laws that people tend to follow when waiting in line, or waiting for a website:</p>
         1471 
         1472 <figure class="border">
         1473         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/psychology_of_waiting_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/psychology_of_waiting_370.png   370w,
         1474                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/psychology_of_waiting_740.png   740w,
         1475                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/psychology_of_waiting_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Psychology of Waiting" width="740" height="416" />
         1476         <figcaption>Psychology of Waiting</figcaption>
         1477 </figure>
         1478 
         1479 <h4 id="1-people-want-to-get-started">1. People want to get started</h4>
         1480 
         1481 <p>You’re excited about an idea, or you’ve finally overcome procrastination to start. When you open up a web application to begin your work, you don’t want to wait for it. Slowness seems slower when you’re eager to get started with your work.</p>
         1482 
         1483 <h4 id="2-bored-waits-feel-longer">2. Bored waits feel longer</h4>
         1484 
         1485 <p>You’re sitting in the back seat on a long road trip. Back in the days before cellphones and handheld games. All there was to do was stare out the window. That trip took <em>forever</em>. It felt longer than it was because you were so bored.</p>
         1486 
         1487 <p>Same with a website. When you’re avoiding your work and scrolling through Twitter, if a link is slow to load, you lose interest real fast.</p>
         1488 
         1489 <h4 id="3-anxious-waits-feel-longer">3. Anxious waits feel longer</h4>
         1490 
         1491 <p>When you are nervous about the content of a page or the results of a request, delays feel longer than they are. Imagine waiting for a medical test result to load, or see the status of a mortgage application. Watching the loading spinners feels slow because of the importance of the result.</p>
         1492 
         1493 <h4 id="4-unexplained-waits-feel-slower">4. Unexplained waits feel slower</h4>
         1494 
         1495 <p>You’ve probably bought lots of things on the internet, and you know how it works. So when you visit an online store and add a product to your cart, you have an expectation of how long it should take. But as the seconds tick by, it feels wrong. You didn’t expect to have to wait here, and you start second-guessing if you really need that new graphic t-shirt.</p>
         1496 
         1497 <h4 id="5-uncertain-wait-times-feel-slower">5. Uncertain wait times feel slower</h4>
         1498 
         1499 <p>You submit a form and see a loading spinner with the text, “please wait”. Wait? How long? As the seconds pass, you think that maybe the form failed and needs to be resubmitted. You may even decide to abandon and try something else. When the user doesn’t understand how long they will wait, the wait time feels exaggerated.</p>
         1500 
         1501 <h4 id="6-people-wait-longer-for-value">6. People wait longer for value</h4>
         1502 
         1503 <p>When you click on that hot celebrity gossip link at TMZ, you’ll wait 4 or 5 seconds for it to load. But if it’s not ready, you’ll probably lose interest and move on. You should probably get to work.</p>
         1504 
         1505 <p>So you open your work GMail account. GMail is a big webapp, and sometimes it takes 6 seconds or longer to load. But you’ll wait for it, because it’s important.</p>
         1506 
         1507 <h3 id="why-is-web-performance-important">Why is Web Performance Important?</h3>
         1508 
         1509 <p>You’ve probably seen the case studies. Lots of <a href="https://www.slideshare.net/devonauerswald/walmart-pagespeedslide/46">eCommerce sites</a>, <a href="https://mollar-luciano.medium.com/how-agrofy-optimised-core-web-vitals-and-improved-business-metrics-2f73311bca">marketing sites</a>, and <a href="https://www.cloudflare.com/learning/performance/more/website-performance-conversion-rates/">software services</a> have shown a correlation between performance improvements and the success of their website.</p>
         1510 
         1511 <p>Slow web pages frustrate users and make them less likely to stick around. Less likely to buy that thing, click on your link, or subscribe to your service. Google can see this in search behavior–people are less likely to stay on slow sites.</p>
         1512 
         1513 <figure class="border">
         1514         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/performance_is_important_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/performance_is_important_370.png   370w,
         1515                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/performance_is_important_740.png   740w,
         1516                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/performance_is_important_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Performance is Important" width="740" height="416" />
         1517         <figcaption>Performance is Important</figcaption>
         1518 </figure>
         1519 
         1520 <h3 id="why-does-google-care-about-my-performance">Why Does Google Care About My Performance?</h3>
         1521 
         1522 <p>Google wants search results to be as relevant and useful as possible–slow sites are not very usable. Search users are more likely to bounce off a slow search result than a fast one.</p>
         1523 
         1524 <p>Performance is such a strong signal to Google that website performance is considered part of a website’s search ranking. So if for no other reason, make sure your site is fast so you don’t lose ranking to your competitors that do.</p>
         1525 
         1526 <p>How does Google measure your website performance? The next chapter covers all the metrics.</p>
         1527 
         1528 <h2 class="green" id="chapter-2-web-performance-metrics"><small>Chapter 2:</small><br /> Web Performance Metrics</h2>
         1529 <div class="chapter-intro green">
         1530         <div class="text">
         1531                 <p>
         1532                         Now that you know <em>why</em> your website needs to be fast, you need to know how to go about measuring it.
         1533                 </p>
         1534                 <p>
         1535                         This chapter will show you the common metrics used to measure website performance and what they are measuring.
         1536                 </p>
         1537         </div>
         1538         <div class="illustration">
         1539                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_measurements_280.png" loading="lazy" class="lazyload" alt="Web Performance Metrics" width="280" height="280" />
         1540         </div>
         1541 </div>
         1542 
         1543 <h3 id="page-load-time">Page Load Time</h3>
         1544 
         1545 <p>In the beginning, there was PageLoad. Website performance was measured with a single measurement of the time until the PageLoad event is fired.</p>
         1546 
         1547 <figure class="border">
         1548         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/pageload_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/pageload_370.png   370w,
         1549                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/pageload_740.png   740w,
         1550                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/pageload_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Pageload Metric on Geocities.com" width="740" height="416" />
         1551         <figcaption>Pageload Metric on Geocities.com</figcaption>
         1552 </figure>
         1553 
         1554 <p>But PageLoad doesn’t fully describe performance. Some sites initially load really fast, but dynamic content needs to load. PageLoad doesn’t fully capture whether a website feels <em>fast</em>.</p>
         1555 
         1556 <p>Worse, PageLoad was easy to manipulate. Developers could improve their PageLoad time by deferring work with JavaScript. Lazy-loading, async script loaders, client-side rendering, and dynamic content were all patterns that often improved PageLoad time, but created a slower experience from the end user perspective.</p>
         1557 
         1558 <p>What do we do instead?</p>
         1559 
         1560 <p>There are lots of ways a website can feel slow: slow to start, slow to finish, jumping around, slow to respond, and more. We can’t use one metric to understand performance anymore.</p>
         1561 
         1562 <h3 id="the-core-web-vitals">The Core Web Vitals</h3>
         1563 
         1564 <p>In 2019, Google introduced a set of metrics intent on measuring the actual performance of a website as the users would see it. These metrics are collectively called the Core Web Vitals.</p>
         1565 
         1566 <figure class="border">
         1567         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/core_web_vitals_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/core_web_vitals_370.png   370w,
         1568                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/core_web_vitals_740.png   740w,
         1569                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/core_web_vitals_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="The Core Web Vital Metrics" width="740" height="416" />
         1570         <figcaption>The Core Web Vital Metrics</figcaption>
         1571 </figure>
         1572 
         1573 <p>They are measured in all Chrome-based browsers, including the Googlebot spider, which uses these scores to influence page rank.</p>
         1574 
         1575 <p>Note that <em>only</em> Chrome-based browsers support these metrics for now. Core Web Vital metrics measure the performance for Chrome desktop and Android mobile users, but not iOS devices, Safari users, or Firefox.</p>
         1576 
         1577 <h4 id="first-contentful-paint-fcp">First Contentful Paint (FCP)</h4>
         1578 
         1579 <p>“First Contentful Paint” measures how long it takes to show the user that the request has been received and the page will load. For example, when you click on a news story, FCP measures the time from the click until the NPR News header renders.</p>
         1580 
         1581 <figure class="border">
         1582         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_contentful_paint_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_contentful_paint_370.png   370w,
         1583                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_contentful_paint_740.png   740w,
         1584                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_contentful_paint_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="First Contentful Paint" width="740" height="416" />
         1585         <figcaption>First Contentful Paint</figcaption>
         1586 </figure>
         1587 
         1588 <p>FCP encourages websites to respond quickly to requests. Learn more about <a href="https://requestmetrics.com/web-performance/first-contentful-paint-fcp">First Contentful Paint and how to measure it</a>.</p>
         1589 
         1590 <figure class="border">
         1591         <img src="https://requestmetrics.com/assets/images/metrics/fcp_range_400.png" loading="lazy" class="lazyload" alt="FCP Metric Range" width="400" height="400" />
         1592         <figcaption>FCP Metric Range</figcaption>
         1593 </figure>
         1594 
         1595 <p>Google recommends that your FCP should be less than <strong>1.0 seconds</strong>. Scores of greater than <strong>3.0 seconds</strong> are notably poor and are likely to have a problem.</p>
         1596 
         1597 <h4 id="largest-contentful-paint-lcp">Largest Contentful Paint (LCP)</h4>
         1598 
         1599 <p>“Largest Contentful Paint” measures how long it takes until the browser renders the largest amount of content to the screen. At this point, ideally, the user can see the content they are looking for and believes the page is nearly done.</p>
         1600 
         1601 <p>It would be more accurate if you measured the time until the <em>most important</em> content was visible, but that’s difficult to do programmatically. LCP is a proxy measurement for <em>most important content</em>.</p>
         1602 
         1603 <p>An example, navigating to the homepage of NPR News has a few different renders, but this is the largest one by pixel area:</p>
         1604 
         1605 <figure class="border">
         1606         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/largest_contentful_paint_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/largest_contentful_paint_370.png   370w,
         1607                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/largest_contentful_paint_740.png   740w,
         1608                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/largest_contentful_paint_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Largest Contentful Paint" width="740" height="416" />
         1609         <figcaption>Largest Contentful Paint</figcaption>
         1610 </figure>
         1611 
         1612 <p>The advertisement is probably not what the user is looking for, but the article images might be.</p>
         1613 
         1614 <p>LCP encourages websites to <em>finish quickly</em> by emphasizing their primary content and making sure it loads fast. Learn more about <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint and how to measure it</a>.</p>
         1615 
         1616 <figure class="border">
         1617         <img src="https://requestmetrics.com/assets/images/metrics/lcp_range_400.png" loading="lazy" class="lazyload" alt="LCP Metric Range" width="400" height="400" />
         1618         <figcaption>LCP Metric Range</figcaption>
         1619 </figure>
         1620 
         1621 <p>Google recommends that your LCP should be less than <strong>2.5 seconds</strong>. Scores of greater than <strong>4.0 seconds</strong> are notably poor and are likely to have a problem.</p>
         1622 
         1623 <h4 id="cumulative-layout-shift-cls">Cumulative Layout Shift (CLS)</h4>
         1624 
         1625 <p>“Cumulative Layout Shift” is a little harder to understand because it does not measure time. CLS measures how much the content on a page moves around as other content is loaded and rendered. Like this:</p>
         1626 
         1627 <figure class="border">
         1628         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/cls-example.gif" loading="lazy" class="lazyload" alt="CLS Example" width="443" height="552" />
         1629         <figcaption>CLS Example</figcaption>
         1630 </figure>
         1631 
         1632 <p>Layout Shifts measure how late-rendered content affects the user experience of a page. Layout shifts that push important content around are really frustrating to use.</p>
         1633 
         1634 <p>CLS discourages websites from moving content around once the user sees it and minimizing the amount of late-rendered content. Learn more about <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Cumulative Layout Shift and how to measure it</a>.</p>
         1635 
         1636 <figure class="border">
         1637         <img src="https://requestmetrics.com/assets/images/metrics/cls_range_400.png" loading="lazy" class="lazyload" alt="CLS Metric Range" width="400" height="400" />
         1638         <figcaption>CLS Metric Range</figcaption>
         1639 </figure>
         1640 
         1641 <p>Your CLS score should be less than <strong>0.1</strong> and no worse than <strong>0.25</strong>.</p>
         1642 
         1643 <h4 id="first-input-delay-fid">First Input Delay (FID)</h4>
         1644 
         1645 <p>“First Input Delay” measures whether the page is really done when the user thinks it’s done. If the browser is busy downloading, parsing, and running JavaScript when the user clicks on the page, there will be a delay until the browser can handle the event and trigger the click event. FID measures this delay.</p>
         1646 
         1647 <figure class="border">
         1648         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_input_delay_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_input_delay_370.png   370w,
         1649                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_input_delay_740.png   740w,
         1650                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/first_input_delay_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="First Input Delay" width="740" height="416" />
         1651         <figcaption>First Input Delay</figcaption>
         1652 </figure>
         1653 
         1654 <p>FID discourages websites from loading too much JavaScript before the user begins interacting with the page. Learn more about <a href="https://requestmetrics.com/web-performance/first-input-delay">First Input Delay and how to measure it</a>.</p>
         1655 
         1656 <figure class="border">
         1657         <img src="https://requestmetrics.com/assets/images/metrics/fid_range_400.png" loading="lazy" class="lazyload" alt="FID Metric Range" width="400" height="400" />
         1658         <figcaption>FID Metric Range</figcaption>
         1659 </figure>
         1660 
         1661 <p>Interaction delays are easily noticeable by most people, so you shouldn’t tolerate much delay here. <strong>Less than 100 ms</strong> ideally, and definitely no worse than <strong>300 ms</strong>.</p>
         1662 
         1663 <h3 id="other-common-performance-metrics">Other Common Performance Metrics</h3>
         1664 
         1665 <p>Aside from the Core Web Vitals, there are several other metrics that are commonly used by popular performance tools.</p>
         1666 
         1667 <figure>
         1668         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/lighthouse_metrics.png" loading="lazy" class="lazyload" alt="Common Performance Metrics" width="1488" height="284" />
         1669         <figcaption>Common Performance Metrics</figcaption>
         1670 </figure>
         1671 
         1672 <h4 id="time-to-first-byte-ttfb">Time to First Byte (TTFB)</h4>
         1673 
         1674 <p>Time to First Byte measures the time until your server returns it’s first byte of data. This represents both the network latency of your users as well as the processing time your server requires to assemble the document.</p>
         1675 
         1676 <p>TTFB is a subset of the <em>First Contentful Paint</em> metric, which is also measuring the document size, parse, and render time.</p>
         1677 <h4 id="time-to-interactive-tti">Time to Interactive (TTI)</h4>
         1678 
         1679 <p>Time to Interactive is a measurement from Chrome Lighthouse that measures the time required until a page is <em>fully</em> interactive, including painted, JavaScript completed, and the browser is done with its background tasks.</p>
         1680 
         1681 <p>TTI includes both the <em>Largest Contentful Paint</em>, the <em>First Input Delay</em>, as well as other background processing timers.</p>
         1682 
         1683 <p>Google recommends a TTI of less than 3.8 seconds to be considered fast. Sites slower than 7.3 seconds have serious performance concerns. Note that this is a wider range than LCP.</p>
         1684 
         1685 <h4 id="total-blocking-time-tbt">Total Blocking Time (TBT)</h4>
         1686 
         1687 <p>Total Blocking Time is the time where user input is delayed due to browser background tasks, such as JavaScript processing or parsing CSS. It measures how busy the browser needs to be in order to load your webpage.</p>
         1688 
         1689 <h4 id="speed-index">Speed Index</h4>
         1690 
         1691 <p>Speed Index is a popular metric used by WebPageTest and Google performance tools that measures the visible changes while a webpage is loading to determine when the user believes that the webpage is done loading.</p>
         1692 
         1693 <hr />
         1694 
         1695 <p>Now that you know the web performance metrics, what they measure, and what a good scores are, you need to understand the ways to gather these metrics. We’ll cover that in the next chapter. Read on!</p>
         1696 
         1697 <h2 class="red" id="chapter-3-kinds-of-web-performance-data"><small>Chapter 3:</small><br /> Kinds of Web Performance Data</h2>
         1698 <div class="chapter-intro red">
         1699         <div class="text">
         1700                 <p>
         1701                         There are different ways to measure web performance, which test different things and have different results.
         1702                 </p>
         1703                 <p>
         1704                         Before you jump into performance tools, you need to understand what kind of performance it is measuring.
         1705                 </p>
         1706         </div>
         1707         <div class="illustration">
         1708                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_data_280.png" loading="lazy" class="lazyload" alt="Kinds of Web Performance Data" width="280" height="280" />
         1709         </div>
         1710 </div>
         1711 
         1712 <h3 id="lab-data-and-field-data">Lab Data and Field Data</h3>
         1713 
         1714 <p>There are two types of web performance data: <em>lab data</em> and <em>field data</em>.</p>
         1715 
         1716 <p>Lab performance data is gathered with a controlled test, such as a Lighthouse report. Lab data describes a single webpage load from a specified location on the network.</p>
         1717 
         1718 <figure class="border">
         1719         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/lab_data_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/lab_data_370.png   370w,
         1720                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/lab_data_740.png   740w,
         1721                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/lab_data_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Synthetic Lab Performance Data" width="740" height="416" />
         1722         <figcaption>Synthetic Lab Performance Data</figcaption>
         1723 </figure>
         1724 
         1725 <p>This kind of data is often called <strong>“Synthetic Testing”</strong> because it measures performance from a known device connected to the network. It does not measure the actual performance of any user, but an estimate for what performance will be.</p>
         1726 
         1727 <p>Field performance data is gathered directly from the users of the website using a <em>performance agent</em>. Because field data includes data for each website user, there is much more data to filter and consider.</p>
         1728 
         1729 <figure class="border">
         1730         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/field_data_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/field_data_370.png   370w,
         1731                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/field_data_740.png   740w,
         1732                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/field_data_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Real-User Field Performance Data" width="740" height="416" />
         1733         <figcaption>Real-User Field Performance Data</figcaption>
         1734 </figure>
         1735 
         1736 <p>Field data is often called <strong>“Real User Monitoring”</strong> because it describes the actual performance experienced by  users from a running website.</p>
         1737 
         1738 <p>Field data can produce <em>a lot</em> of data, and not all of it is relevant. To understand field data, you’ll get to use <em>statistics</em>! Don’t worry, it’s not so bad.</p>
         1739 
         1740 <h3 id="interpreting-performance-data-with-statistics">Interpreting Performance Data with Statistics</h3>
         1741 
         1742 <p>The <em>easiest</em> way to understand sets of data is with <strong>averages</strong>. But averages aren’t great because they are often misleading because of unusual performance distribution.</p>
         1743 
         1744 <p>Look at an example. An average Lighthouse score of <strong>80</strong> can come from either of these situations:</p>
         1745 
         1746 <figure class="border">
         1747         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/averages_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/averages_370.png   370w,
         1748                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/averages_740.png   740w,
         1749                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/averages_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Averaging Data is Misleading" width="740" height="416" />
         1750         <figcaption>Averaging Data is Misleading</figcaption>
         1751 </figure>
         1752 
         1753 <p>These tell very different stories. The top scores describe a site that has poor performance for half of its users and probably has an issue. The bottom score is being dragged down from a single outlier, and is probably doing okay.</p>
         1754 
         1755 <p>So what can we do instead?</p>
         1756 
         1757 <h3 id="median-and-percentiles">Median and Percentiles</h3>
         1758 
         1759 <p>Imagine if all your performance scores were sorted from best to worse. Your <strong>median</strong> performance is the value where half of your users had a faster experience. It’s a good proxy for your typical user and how they experience your website.</p>
         1760 
         1761 <p>Median can also be called the <em>50th Percentile</em> or p50 because 50% of your users will have a better score.</p>
         1762 
         1763 <p>Performance numbers are often measured by their p50, p75, and p95 scores. Or, the performance experience for  <em>“typical users”</em>, <em>“most users”</em>, and <em>“worst users”</em>.</p>
         1764 
         1765 <p>Some code might help. The Lighthouse score of 10 tests is stored in an array. You can get the percentiles like this:</p>
         1766 
         1767 <figure class="code " id="code-342">
         1768     <div class="code-wrap">
         1769         <pre class="prettyprint lang-javascript">
         1770 // Performance scores, sorted.
         1771 var lighthouseScores = [100, 100, 90, 90, 90, 80, 70, 70, 60, 50];
         1772 
         1773 // Desired percentile to calculate.
         1774 var percentile = 0.75;
         1775 
         1776 // Find the index 75% into the array.
         1777 var idx = Math.round( (lighthouseScores.length - 1) * percentile );
         1778 
         1779 var p75Score = lighthouseScores[idx];
         1780 </pre>
         1781         <!-- <button type="button" class="btn copy-button yes-js">
         1782             <img src="/assets/images/copy.svg" alt="Copy" />
         1783             <span class="label">Copy</span>
         1784         </button> -->
         1785         
         1786         <figcaption><a href="#code-342">Calculating percentiles in JavaScript</a></figcaption>
         1787         
         1788     </div>
         1789 </figure>
         1790 
         1791 <p>Or you might have your data in a spreadsheet, where you can use the <code class="language-plaintext highlighter-rouge">PERCENTILE</code> function.</p>
         1792 
         1793 <p>Ok, enough background. On to the tools!</p>
         1794 
         1795 <h2 class="indigo" id="chapter-4-common-web-performance-tools"><small>Chapter 4:</small><br /> Common Web Performance Tools</h2>
         1796 <div class="chapter-intro indigo">
         1797         <div class="text">
         1798                 <p>
         1799                         Now that you understand the metrics and the methods of measuring web performance, it's time to look at the tools!
         1800                 </p>
         1801                 <p>
         1802                         How do you know what tool to use, what it measures, and how accurate the data is? This chapter will help.
         1803                 </p>
         1804         </div>
         1805         <div class="illustration">
         1806                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_tools_280.png" loading="lazy" class="lazyload" alt="Common Web Performance Tools" width="280" height="280" />
         1807         </div>
         1808 </div>
         1809 
         1810 <p>These are some of the best web performance tools available. Each measures your website performance in different ways from different places. These measurements will not always line up, but by understanding what each tool is testing, you will get a more complete picture of how your website performs.</p>
         1811 
         1812 <h4 id="1-google-lighthouse">1. Google Lighthouse</h4>
         1813 
         1814 <div class="type-label">
         1815         <span class="name">Type</span>
         1816         <span class="value synthetic">Synthetic Lab Data</span>
         1817 </div>
         1818 
         1819 <p><a href="https://developers.google.com/web/tools/lighthouse">Lighthouse</a> is an open-source tool from Google that can be run from Chrome DevTools or from the command line.</p>
         1820 
         1821 <figure class="border">
         1822         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_lighthouse_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_lighthouse_370.png   370w,
         1823                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_lighthouse_740.png   740w,
         1824                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_lighthouse_1480.png 1480w" loading="lazy" class="lazyload" alt="Google Lighthouse" width="740" height="416" />
         1825         <figcaption>Google Lighthouse</figcaption>
         1826 </figure>
         1827 
         1828 <p>It’s run from you local computer, so it’s measuring the performance you experience with your hardware on your network. There are several places where you can run <em>Lighthouse-as-a-Service</em> from elsewhere on the internet, including Google’s <strong><a href="https://developers.google.com/speed/pagespeed/insights/">PageSpeed Insights</a></strong>. However these services have mixed results depending on the structure, location, and capacity of the service.</p>
         1829 
         1830 <p>Lighthouse is most useful for development-time testing of your site or performing audits on sites where you don’t have direct access (like snooping on what other websites are doing). Read more about the <a href="https://requestmetrics.com/web-performance/the-limitations-of-lighthouse">limitations of Lighthouse</a>.</p>
         1831 
         1832 <h4 id="2-webpagetest">2. WebPageTest</h4>
         1833 
         1834 <div class="type-label">
         1835         <span class="name">Type</span>
         1836         <span class="value synthetic">Synthetic Lab Data</span>
         1837 </div>
         1838 
         1839 <p><a href="https://www.webpagetest.org/" rel="nofollow">WebPageTest</a> is a free hosted service that performs performance tests on public websites. It can do a lot more things than Lighthouse, like setting up network locations, network speeds, and customizing requests.</p>
         1840 
         1841 <figure class="border">
         1842         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/webpagetest_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/webpagetest_370.png   370w,
         1843                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/webpagetest_740.png   740w,
         1844                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/webpagetest_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="WebPageTest Result" width="740" height="416" />
         1845         <figcaption>WebPageTest Result</figcaption>
         1846 </figure>
         1847 
         1848 <p>It also produces a more detailed (and more complex) report with network location, breakdown of timings, and a detailed waterfall chart.</p>
         1849 
         1850 <p>WebPageTest is great for auditing live websites to better understand how they are performing in production.</p>
         1851 
         1852 <h4 id="3-google-search-console">3. Google Search Console</h4>
         1853 
         1854 <div class="type-label">
         1855         <span class="name">Type</span>
         1856         <span class="value synthetic">Synthetic Lab Data</span>
         1857 </div>
         1858 
         1859 <p><a href="https://search.google.com/search-console">Google Search Console</a> shows the analytics, issues, and performance recorded by the Googlebot crawler when Google indexes your website. This includes User Experience metrics like the Core Web Vitals.</p>
         1860 
         1861 <figure class="border">
         1862         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_search_console_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_search_console_370.png   370w,
         1863                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_search_console_740.png   740w,
         1864                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/google_search_console_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Google Search Console Web Vital Report" width="740" height="416" />
         1865         <figcaption>Google Search Console Web Vital Report</figcaption>
         1866 </figure>
         1867 
         1868 <p>Although the Search Console metrics are synthetic, they are what Google will use to rank your site in search results. They represent a very important user: <em>Google</em>.</p>
         1869 
         1870 <p>The metrics you’ll see in Search Console will be quite slow to update, depending on the traffic to your website. It could be a week or more for Google to see changes in your performance scores, and the reports are very generic.</p>
         1871 
         1872 <p>You need to use Search Console to see how Google ranks your performance, but it’s not very useful for testing or discovering performance issues.</p>
         1873 
         1874 <h4 id="4-chrome-user-experience-report-crux">4. Chrome User Experience Report (CrUX)</h4>
         1875 
         1876 <div class="type-label">
         1877         <span class="name">Type</span>
         1878         <span class="value real">Real-User Field Data</span>
         1879 </div>
         1880 
         1881 <p>The Chrome browser itself collects performance metrics from opt-in users for the top million domains on the internet. Google publishes these metrics in the <a href="https://developers.google.com/web/tools/chrome-user-experience-report">Chrome User Experience Report</a> or <em>CrUX</em>. It’s real-user data!</p>
         1882 
         1883 <figure class="border">
         1884         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/crux_report_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/crux_report_370.png   370w,
         1885                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/crux_report_740.png   740w,
         1886                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/crux_report_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Chrome User Experience (CrUX) Report" width="740" height="416" />
         1887         <figcaption>Chrome User Experience (CrUX) Report</figcaption>
         1888 </figure>
         1889 
         1890 <p>The data is really interesting, but it’s only published monthly and summarized to an entire domain. CrUX data is only accessible through BigQuery and DataStudio, Google’s Data Warehouse tools. If you haven’t used those tools before (like most people), it can be difficult to get meaningful information.</p>
         1891 
         1892 <p>CrUX data is useful to see historical website performance, or compare your performance to other websites, but only if your website is large enough to qualify for inclusion.</p>
         1893 
         1894 <h4 id="5-request-metrics">5. Request Metrics</h4>
         1895 
         1896 <div class="type-label">
         1897         <span class="name">Type</span>
         1898         <span class="value real">Real-User Field Data</span>
         1899 </div>
         1900 
         1901 <p><a href="https://requestmetrics.com/">Request Metrics</a> is a real-user performance monitoring service. Unlike CrUX, Request Metrics shows you how your site is performing <em>right now</em> with no delay in the data.</p>
         1902 
         1903 <figure class="border">
         1904         <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/request_metrics_740.png" srcset="https://requestmetrics.com/assets/images/webperf/measure-web-performance/request_metrics_370.png   370w,
         1905                                                                  https://requestmetrics.com/assets/images/webperf/measure-web-performance/request_metrics_740.png   740w,
         1906                                                            https://requestmetrics.com/assets/images/webperf/measure-web-performance/request_metrics_1480.png 1480w" sizes="(max-width: 786px) 100vw, 700px" loading="lazy" class="lazyload" alt="Request Metrics Core Web Vitals" width="740" height="416" />
         1907         <figcaption>Request Metrics Core Web Vitals</figcaption>
         1908 </figure>
         1909 
         1910 <p>The charts are useful and easy to understand. The tool breaks down the large amount of data and gives you meaningful median, p75, and p95 metrics as well as some details on the causes of slowness.</p>
         1911 
         1912 <p>Request Metrics is best for active monitoring of your website performance. It is really helpful when you are making performance changes to see how your improvements impact real users.</p>
         1913 
         1914 <h2 class="pink" id="conclusion">Conclusion</h2>
         1915 <div class="chapter-intro pink">
         1916         <div class="text">
         1917                 <p>
         1918                         That's everything you need to know to start measuring web performance and making your website faster!
         1919                 </p>
         1920                 <p>
         1921                         Which performance metric is most meaningful to you? <a href="https://twitter.com/requestmetrics">Let us know on Twitter!</a>
         1922                 </p>
         1923         </div>
         1924         <div class="illustration">
         1925                 <img src="https://requestmetrics.com/assets/images/webperf/measure-web-performance/chapter_conclusion_280.png" loading="lazy" class="lazyload" alt="Conclusion" width="280" height="280" />
         1926         </div>
         1927 </div>
         1928 
         1929 <style>
         1930         .type-label {
         1931                 font-family: "Muli", Helvetica, Arial, sans-serif;
         1932                 font-size: 0.8em;
         1933                 font-weight: bold;
         1934                 display: flex;
         1935     flex-direction: row;
         1936     margin: 0 0 30px 0;
         1937                 background-color: #F3F6FA;
         1938                 border: 2px solid #DDE4EB;
         1939                 border-radius: 6px;
         1940     width: fit-content;
         1941         }
         1942                 .type-label .name {
         1943                         padding: 2px 20px;
         1944                         border-right: 2px solid #DDE4EB;
         1945                 }
         1946                 .type-label .value {
         1947                         padding: 2px 20px;
         1948                 }
         1949                         .type-label .value.synthetic {
         1950                                 background-color: #F5C0BE;
         1951                                 color: #7E191F
         1952                         }
         1953                         .type-label .value.real {
         1954                                 background-color: #F1FCF6;
         1955                                 color: #2F6959
         1956                         }
         1957 </style>
         1958 
         1959 
         1960       ]]></content:encoded>
         1961       <pubDate>Mon, 03 May 2021 00:00:00 +0000</pubDate>
         1962       <author>hello@requestmetrics.com (Request Metrics)</author>
         1963     </item>
         1964     
         1965     
         1966     <item>
         1967       <title>Tutorial: Monitoring Your Core Web Vitals</title>
         1968       <link>https://requestmetrics.com/web-performance/monitoring-core-web-vital</link>
         1969       <guid isPermaLink="true">https://requestmetrics.com/web-performance/monitoring-core-web-vital</guid>
         1970       <description>Web performance used to be easy. You’d time how long a page takes to load, easy.
         1971 
         1972 But the rise of client-side JavaScript has introduced bold new ways for websites to be frustratingly slow. Measuring this new slowness will take new metrics. Google calls them the Core Web Vitals.
         1973 
         1974 Each of the Core Web Vitals measures a different aspect of how a web application responds. This post will take a look at each of these metrics, what they measure, and how to use them. You’ll also take a look at my Rea...</description>
         1975       <media:content url="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/title-2000.png" medium="image" type="image/png" height="350" width="700" />
         1976       <enclosure url="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/title-2000.png" type="image/png" />
         1977       <content:encoded><![CDATA[
         1978         <div><img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/title-2000.png" alt="Tutorial: Monitoring Your Core Web Vitals" class="webfeedsFeaturedVisual" /></div>
         1979         <p>Web performance used to be easy. You’d time how long a page takes to load, easy.</p>
         1980 
         1981 <p>But the rise of client-side JavaScript has introduced bold new ways for websites to be frustratingly slow. Measuring this new slowness will take new metrics. Google calls them the Core Web Vitals.</p>
         1982 
         1983 <p>Each of the Core Web Vitals measures a different aspect of how a web application responds. This post will take a look at each of these metrics, what they measure, and how to use them. You’ll also take a look at my Really Slow Webpage™ and implement a basic performance monitoring script to see just how bad it really is. Finally, you’ll see how to interpret performance data to decide what your performance goals should be.</p>
         1984 
         1985 <!--more-->
         1986 
         1987 <h2 id="what-youll-learn">What you’ll learn</h2>
         1988 <p>In this tutorial you’ll learn about:</p>
         1989 
         1990 <ul>
         1991   <li>Some of the different ways modern websites can have poor performance.</li>
         1992   <li>The new emerging metrics for web performance, the Core Web Vitals, and what they measure.</li>
         1993   <li>How to collect Core Web Vitals in your own application.</li>
         1994 </ul>
         1995 
         1996 <h2 id="contentful-paint">Contentful Paint</h2>
         1997 <p>Once a web browser has downloaded and parsed your content, it will render and paint the content to the screen. For simple web pages, this happens only once: the HTML is downloaded, and the content is shown. But for many modern web applications, content will be broken into many smaller chunks and assembled asynchronously with JavaScript. In cases like this, there can be dozens or even hundreds of paint events.</p>
         1998 
         1999 <p>Each paint event could be important for your application, but generally you care about two of them: the first time content was rendered, and the time the largest amount of content was rendered. These are called the First Contentful Paint and the Largest Contentful Paint.</p>
         2000 
         2001 <h3 id="first-contentful-paint">First Contentful Paint</h3>
         2002 <p>The <a href="https://requestmetrics.com/web-performance/first-contentful-paint-fcp">First Contentful Paint</a> (FCP) is the time between when the user starts navigating to a URL, via a link click or entering the address, and the time the first content is drawn on the screen.</p>
         2003 
         2004 <p>This time represents the first visible indication to the user that their request has been received and that the content is coming. It’s important to show the user something quickly to keep their attention and feel fast.</p>
         2005 
         2006 <p>For example, let’s take a look at the site for a major US retailer, Target.com. Here is the First Contentful Paint today:</p>
         2007 
         2008 <figure>
         2009     <img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/image4.png" style="border:1px solid #CCC" width="505" height="392" alt="First Contentful Paint on Target.com" />
         2010     <figcaption>First Contentful Paint on Target.com</figcaption>
         2011 </figure>
         2012 
         2013 <p>The initial document request contains a basic skeleton of the page, which can be painted to the screen very quickly. In a typical case, about 300ms, which is noticeable, but feels very quick. It doesn’t give the user anything to do yet, but they know that something is coming.</p>
         2014 
         2015 <h3 id="largest-contentful-paint">Largest Contentful Paint</h3>
         2016 <p>The <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint</a> (LCP) is the time since navigation started when the largest amount of content is drawn on the screen, measured by pixel area.</p>
         2017 
         2018 <p>This time is a proxy for when the user thinks the page is substantially complete, and that they can proceed with their task. Hopefully, this has occurred quickly enough that they aren’t distracted yet.</p>
         2019 
         2020 <p>Return to Target.com. As the page continues to load, the Largest Contentful Paint draws the screen out to this:</p>
         2021 
         2022 <figure>
         2023     <img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/image3.png" style="border:1px solid #CCC" width="507" height="396" alt="Largest Contentful Paint on Target.com" />
         2024     <figcaption>Largest Contentful Paint on Target.com</figcaption>
         2025 </figure>
         2026 
         2027 <p>The page is much more complete, showing that it knows about the user, their location, and the discounts Target wants to offer. This data came from various back-end services that returned asynchronously. The page is not quite done, as some images have not yet loaded, but the user would probably start interacting with the page at this point, which is 2 seconds after start.</p>
         2028 
         2029 <p>Wait, isn’t LCP just like the old <code class="language-plaintext highlighter-rouge">onload</code> metric? Not exactly. <code class="language-plaintext highlighter-rouge">onload</code> is a specific event the browser triggers when everything known has been downloaded, parsed, and rendered. However, many websites will continue downloading more content after load, which can trigger more paint events.</p>
         2030 
         2031 <p>Because the LCP value will be updated as larger paint events occur later in the page’s lifetime, it may not be as useful for single-page applications that do a lot of client-side routing and rendering. The Largest Contentful Paint could come many minutes after the page was initially loaded, as large parts of the screen are repainted.</p>
         2032 
         2033 <p>Both the LCP and <code class="language-plaintext highlighter-rouge">onload</code> are still valuable, and you should measure what is most meaningful for how your web application loads.</p>
         2034 
         2035 <h2 id="cumulative-layout-shift">Cumulative Layout Shift</h2>
         2036 <p>The <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Cumulative Layout Shift</a> (CLS) is a measurement of how much elements move around due to late-rendered content. A common example is banner ads loading on a news website. You go to click on a headline, but a new ad gets rendered that pushes all the content down, and you miss-click. That frustrating movement is layout shift.</p>
         2037 
         2038 <p>Unlike other performance metrics, layout shift cannot be measured as time. Instead, each layout shift is given a score based on the percent of the current viewport that changed, and the distance that elements moved. <a href="https://web.dev/cls/#layout-shift-score">Google has more details on how this is calculated</a>.</p>
         2039 
         2040 <p>A large, asynchronous site like Target.com will have lots of layout shifts as the content is rendered. The Cumulative Layout Shift is simply the sum of all the layout shift scores. This score is a measurement of all the times the webpage moved around on the user.</p>
         2041 
         2042 <h2 id="first-input-delay">First Input Delay</h2>
         2043 <p>Once the page has painted something to the screen the user will try to interact by clicking. But the browser might be busy downloading and parsing obscene amounts of JavaScript or other assets. The delay between when the user first interacts and when the browser can trigger the event is called the <a href="https://requestmetrics.com/web-performance/first-input-delay">First Input Delay</a> (FID).</p>
         2044 
         2045 <p>This performance metric is a little harder to understand. It’s not a measurement about your code performance directly. It’s a measurement of when the user thinks they can interact, and how much work the browser is still doing.</p>
         2046 
         2047 <p>Consider Target.com again. After the Largest Contentful Paint event, the page looks ready to use. But the browser is still downloading images and executing JavaScript.</p>
         2048 
         2049 <p>If you click on a coupon, which triggers an <code class="language-plaintext highlighter-rouge">onClick</code> event, the browser has to stop what it’s doing and publish the event to any JavaScript listeners, which does not happen instantly. This delay is the First Input Delay.</p>
         2050 
         2051 <p>FID is a measure of both when the user thinks the page is ready and how much stuff is still being done.</p>
         2052 
         2053 <h2 id="compatibility-and-limitations">Compatibility and limitations</h2>
         2054 <p>Google is the driving force behind the Core Web Vital metrics, and has said the <a href="https://developers.google.com/search/blog/2020/05/evaluating-page-experience">Core Web Vitals will be search ranking indicators starting in 2021</a>. The metrics have been implemented in <a href="https://en.wikipedia.org/wiki/Blink_(browser_engine)">Blink</a>, and so are available in Chrome, Edge, and Opera.</p>
         2055 
         2056 <p>Web Vitals are currently a draft in the W3C, and have not started the <a href="https://wicg.github.io/largest-contentful-paint/#sec-largest-contentful-paint-interface">Standardization Process</a>.  There will continue to be changes as other organizations review and adopt the proposal.</p>
         2057 
         2058 <p>If you decide to start capturing performance data, Web Vital metrics will only represent the behavior of newer Chrome users, which may not be representative of your user base.</p>
         2059 
         2060 <h2 id="lab-performance-vs-field-performance">Lab Performance vs. Field Performance</h2>
         2061 <p>As you build your website, you probably have an intrinsic feeling for how fast it is. Maybe you’ve even run some performance profiling tools like <a href="https://requestmetrics.com/web-performance/the-limitations-of-lighthouse">Chrome Lighthouse</a> to gather metrics about your performance. That’s great! This is called “Lab Performance”. It’s the performance of your website to you in your local controlled environment.</p>
         2062 
         2063 <p>But what is more relevant is how fast your users experience your website in the real world, where browsers are unpredictable and networks are not infallible. Gathering performance metrics from real users is called “Field Performance” because it shows you how your website really works in the field.</p>
         2064 
         2065 <p>In the next section you’ll see how you can gather field performance data from your website.</p>
         2066 
         2067 <h2 id="case-study-the-really-slow-website">Case Study: The Really Slow Website™</h2>
         2068 <p>This tutorial has a demonstration website, and it’s really slow. It’s a single page that loads navigation links and content dynamically to illustrate how a page that loads fast can still feel really slow.</p>
         2069 
         2070 <figure>
         2071     <img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/image2.png" width="700" height="615" alt="The Really Slow Website™" />
         2072     <figcaption>The Really Slow Website™</figcaption>
         2073 </figure>
         2074 
         2075 <p>You’re going to write a performance monitoring script for the website that captures field performance data for the Core Web Vitals: First Contentful Paint, Largest Contentful Paint, Cumulative Layout Shift, and the First Input Delay.</p>
         2076 
         2077 <p>You can use your own website for this exercise if you prefer.</p>
         2078 
         2079 <h3 id="prerequisites">Prerequisites</h3>
         2080 <p>The Really Slow Website™ consists of some static content served from a Node.js service with Express. You’ll need the following tools and resources to implement the performance monitoring code in this tutorial:</p>
         2081 
         2082 <ul>
         2083   <li>Git and a Github account – To clone the example repository.</li>
         2084   <li>Node.js. This example uses the current LTS version, 14.15.0</li>
         2085   <li>Google Chrome. – Version 87 or higher.</li>
         2086   <li>Visual Studio Code – or the code editor of your choice.</li>
         2087 </ul>
         2088 
         2089 <p>You should have a working knowledge of Git, HTML, and JavaScript. See other posts listed in the Additional Resources section if you need a refresher.</p>
         2090 
         2091 <p>If you get stuck along the way, the complete solution is in the <a href="https://github.com/toddhgardner/monitoring-core-web-vitals/tree/complete">complete branch</a> of the <a href="https://github.com/toddhgardner/monitoring-core-web-vitals">companion repository on GitHub</a>.</p>
         2092 
         2093 <h3 id="getting-started">Getting started</h3>
         2094 <p>If you’re going to use my Really Slow Website™, you’ll need to clone the repository from Github and get it running on your computer. Start by executing the following commands in the directory where you’d like to create the project directory:</p>
         2095 
         2096 <figure class="code " id="code-153">
         2097     <div class="code-wrap">
         2098         <pre class="prettyprint lang-bash">
         2099 git clone https://github.com/toddhgardner/monitoring-core-web-vitals.git
         2100 cd monitoring-core-web-vitals
         2101 npm install
         2102 npm start
         2103 </pre>
         2104         <!-- <button type="button" class="btn copy-button yes-js">
         2105             <img src="/assets/images/copy.svg" alt="Copy" />
         2106             <span class="label">Copy</span>
         2107         </button> -->
         2108         
         2109         <figcaption><a href="#code-153">Loading the example project</a></figcaption>
         2110         
         2111     </div>
         2112 </figure>
         2113 
         2114 <p>This will install the dependencies for the website and start the server at http://localhost:3000/. Open that URL in Google Chrome and you should eventually see a page with a title, three awesome navigation elements, and some content text.</p>
         2115 
         2116 <p>If you prefer to use your own website for this exercise, make sure you have it running locally and that you can modify the HTML and add JavaScript files to it easily.</p>
         2117 
         2118 <h3 id="troubleshooting">Troubleshooting</h3>
         2119 <p>Here are some solutions to common problems you might encounter:</p>
         2120 
         2121 <ul>
         2122   <li><code class="language-plaintext highlighter-rouge">Command not found: git</code> – Make sure you have Git installed correctly and added to your PATH.</li>
         2123   <li><code class="language-plaintext highlighter-rouge">Command not found: npm</code> – Make sure you have Node.js installed correctly and added to your path.</li>
         2124   <li>npm errors – Make sure you are running a current version of Node.js. The example is based on the current long-term support version, 14.15.0.</li>
         2125 </ul>
         2126 
         2127 <h2 id="creating-the-performance-monitoring-script">Creating the performance monitoring script</h2>
         2128 <p>Go to the monitoring-core-web-vitals/public directory and create a new file called perf.js and insert the following JavaScript code:</p>
         2129 
         2130 <figure class="code " id="code-226">
         2131     <div class="code-wrap">
         2132         <pre class="prettyprint lang-javascript">
         2133 (function perf() {
         2134 
         2135   var data = {
         2136     url: window.location.href,
         2137     fcp: 0,
         2138     lcp: 0,
         2139     cls: 0,
         2140     fid: 0
         2141   };
         2142 
         2143   console.log(&quot;Starting performance monitoring on &quot; + data.url);
         2144 
         2145 })();
         2146 </pre>
         2147         <!-- <button type="button" class="btn copy-button yes-js">
         2148             <img src="/assets/images/copy.svg" alt="Copy" />
         2149             <span class="label">Copy</span>
         2150         </button> -->
         2151         
         2152         <figcaption><a href="#code-226">Skeleton of the performance script</a></figcaption>
         2153         
         2154     </div>
         2155 </figure>
         2156 
         2157 <p>The code creates an <a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE">IIFE</a>, an immediately invoked functional expression, to isolate this code from the global namespace. It creates an object to hold performance data and writes an initialization message to the console.</p>
         2158 
         2159 <p>Go to index.html in the public directory and add the following <code class="language-plaintext highlighter-rouge">&lt;script&gt;</code> element after the existing <code class="language-plaintext highlighter-rouge">&lt;script&gt;</code> elements:</p>
         2160 
         2161 <figure class="code " id="code-68">
         2162     <div class="code-wrap">
         2163         <pre class="prettyprint lang-html">
         2164 &lt;script src=&quot;perf.js&quot;&gt;&lt;/script&gt;
         2165 </pre>
         2166         <!-- <button type="button" class="btn copy-button yes-js">
         2167             <img src="/assets/images/copy.svg" alt="Copy" />
         2168             <span class="label">Copy</span>
         2169         </button> -->
         2170         
         2171         <figcaption><a href="#code-68">Adding the perf script to your page</a></figcaption>
         2172         
         2173     </div>
         2174 </figure>
         2175 
         2176 <p>Open Chrome and navigate to http://localhost:3000/. Open the Developer Tools by pressing F12. Refresh the page and you should see the following output in the console:</p>
         2177 
         2178 <figure class="code " id="code-80">
         2179     <div class="code-wrap">
         2180         <pre class="prettyprint lang-bash">
         2181 Starting performance monitoring on http://localhost:3000/
         2182 </pre>
         2183         <!-- <button type="button" class="btn copy-button yes-js">
         2184             <img src="/assets/images/copy.svg" alt="Copy" />
         2185             <span class="label">Copy</span>
         2186         </button> -->
         2187         
         2188         <figcaption><a href="#code-80">Server running output</a></figcaption>
         2189         
         2190     </div>
         2191 </figure>
         2192 
         2193 <p>The perf.js script is working!</p>
         2194 
         2195 <h2 id="monitoring-first-contentful-paint">Monitoring First Contentful Paint</h2>
         2196 <p>FCP, like many of the Core Web Vital metrics, is exposed via the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver">PerformanceObserver API</a>. To use it, you create an instance of the observer to listen for paint events, and provide a callback to save the responses.</p>
         2197 
         2198 <p>Go to the perf.js file and add the following code after the <code class="language-plaintext highlighter-rouge">console.log</code> statement:</p>
         2199 
         2200 <figure class="code " id="code-176">
         2201     <div class="code-wrap">
         2202         <pre class="prettyprint lang-javascript">
         2203   var fcpObserver = new PerformanceObserver(function handleFCP(entryList) {
         2204     // TODO Handle FCP Entry
         2205   }).observe({ type: &quot;paint&quot;, buffered: true });
         2206 </pre>
         2207         <!-- <button type="button" class="btn copy-button yes-js">
         2208             <img src="/assets/images/copy.svg" alt="Copy" />
         2209             <span class="label">Copy</span>
         2210         </button> -->
         2211         
         2212         <figcaption><a href="#code-176">FCP Observer function</a></figcaption>
         2213         
         2214     </div>
         2215 </figure>
         2216 
         2217 <p>This creates a <code class="language-plaintext highlighter-rouge">PerformanceObserver</code> that will observe <code class="language-plaintext highlighter-rouge">paint</code> events. The <code class="language-plaintext highlighter-rouge">buffered</code> option allows <code class="language-plaintext highlighter-rouge">fcpObserver</code> to receive events that may have already happened before the observer was created. When <code class="language-plaintext highlighter-rouge">paint</code> actions have completed, the <code class="language-plaintext highlighter-rouge">handleFCP</code> function will be called with a performance entry list.</p>
         2218 
         2219 <p>Fill in the body of the <code class="language-plaintext highlighter-rouge">handleFCP</code> function by replacing the <code class="language-plaintext highlighter-rouge">// TODO</code> comment with:</p>
         2220 
         2221 <figure class="code " id="code-257">
         2222     <div class="code-wrap">
         2223         <pre class="prettyprint lang-javascript">
         2224   var entries = entryList.getEntries() || [];
         2225   entries.forEach(function(entry) {
         2226     if (entry.name === &quot;first-contentful-paint&quot;) {
         2227       data.fcp = entry.startTime;
         2228       console.log(&quot;Recorded FCP Performance: &quot; + data.fcp);
         2229     }
         2230   });
         2231 </pre>
         2232         <!-- <button type="button" class="btn copy-button yes-js">
         2233             <img src="/assets/images/copy.svg" alt="Copy" />
         2234             <span class="label">Copy</span>
         2235         </button> -->
         2236         
         2237         <figcaption><a href="#code-257">FCP Event Handler</a></figcaption>
         2238         
         2239     </div>
         2240 </figure>
         2241 
         2242 <p>The <code class="language-plaintext highlighter-rouge">getEntries()</code> function converts the contents of the <code class="language-plaintext highlighter-rouge">entryList</code> argument to an array, but may return <code class="language-plaintext highlighter-rouge">null</code> in some instances. To protect against that, <code class="language-plaintext highlighter-rouge">entries</code> defaults to an empty array. The function then loops over the entries looking for one with the name <code class="language-plaintext highlighter-rouge">first-contentful-paint</code>, which contains the required value.</p>
         2243 
         2244 <p>Go back to Chrome and refresh the page. You should see the new FCP value written to the console, probably about 2000ms.</p>
         2245 
         2246 <h2 id="monitoring-largest-contentful-paint">Monitoring Largest Contentful Paint</h2>
         2247 <p>Just like FCP, the LCP is exposed via the <code class="language-plaintext highlighter-rouge">PerformanceObserver</code> API with a different entry name. Strangely, the LCP is not a <code class="language-plaintext highlighter-rouge">paint</code> event, but it’s own type, <code class="language-plaintext highlighter-rouge">largest-contentful-paint</code>.</p>
         2248 
         2249 <p>Other than the names, the code is very similar. Go back to perf.js and add the following <code class="language-plaintext highlighter-rouge">lcpObserver</code> below the <code class="language-plaintext highlighter-rouge">fcpObserver</code> you created previously:</p>
         2250 
         2251 <figure class="code " id="code-406">
         2252     <div class="code-wrap">
         2253         <pre class="prettyprint lang-javascript">
         2254   var lcpObserver = new PerformanceObserver(function handleLCP(entryList) {
         2255     var entries = entryList.getEntries() || [];
         2256     entries.forEach(function(entry) {
         2257       if (entry.startTime &gt; data.lcp) {
         2258         data.lcp = entry.startTime;
         2259         console.log(&quot;Recorded LCP Performance: &quot; + data.lcp);
         2260       }
         2261     });
         2262   }).observe({ type: &quot;largest-contentful-paint&quot;, buffered: true });
         2263 </pre>
         2264         <!-- <button type="button" class="btn copy-button yes-js">
         2265             <img src="/assets/images/copy.svg" alt="Copy" />
         2266             <span class="label">Copy</span>
         2267         </button> -->
         2268         
         2269         <figcaption><a href="#code-406">LCP Observer function</a></figcaption>
         2270         
         2271     </div>
         2272 </figure>
         2273 
         2274 <p>The difference between FCP and LCP is that you don’t necessarily know which paint is the largest until all the paint events have occurred. The observer will call the handler for each paint event that is the largest so far. The code checks to make sure that the <code class="language-plaintext highlighter-rouge">startTime</code> of the paint event is after any previously saved value before saving it.</p>
         2275 
         2276 <p>Refresh the page in Chrome. In addition to the previous console messages, you’ll see one or more additional messages about saving the LCP value.</p>
         2277 
         2278 <h2 id="monitoring-cumulative-layout-shift">Monitoring Cumulative Layout Shift</h2>
         2279 <p>The Cumulative Layout Shift is also available with a <code class="language-plaintext highlighter-rouge">PerformanceObserver</code>, but you need to do a little more work. The observer will tell you when layout shifts happen, and what the score was, but you need to add them all together to make it cumulative.</p>
         2280 
         2281 <p>You also need to check to see if the layout shift is in response to a user action. If the user clicks on a button and a form unfolds, that’s not an unexpected layout shift and you don’t count it in CLS.</p>
         2282 
         2283 <p>Fortunately, there’s an API for that.</p>
         2284 
         2285 <p>In perf.js, add the following below the <code class="language-plaintext highlighter-rouge">lcpObserver</code>:</p>
         2286 
         2287 <figure class="code " id="code-387">
         2288     <div class="code-wrap">
         2289         <pre class="prettyprint lang-javascript">
         2290   var clsObserver = new PerformanceObserver(function handleCLS(entryList) {
         2291     var entries = entryList.getEntries() || [];
         2292     entries.forEach(function(entry) {
         2293       if (!entry.hadRecentInput) {
         2294         data.cls += entry.value;
         2295         console.log(&quot;Increased CLS Performance: &quot; + data.cls);
         2296       }
         2297     });
         2298   }).observe({ type: &quot;layout-shift&quot;, buffered: true });
         2299 </pre>
         2300         <!-- <button type="button" class="btn copy-button yes-js">
         2301             <img src="/assets/images/copy.svg" alt="Copy" />
         2302             <span class="label">Copy</span>
         2303         </button> -->
         2304         
         2305         <figcaption><a href="#code-387">CLS Observer function</a></figcaption>
         2306         
         2307     </div>
         2308 </figure>
         2309 
         2310 <p>Refresh the page in Chrome again. You’ll see several additional messages as each layout shift happens during the loading process. In a representative trial the total CLS was about 0.138.</p>
         2311 
         2312 <h2 id="monitoring-first-input-delay">Monitoring First Input Delay</h2>
         2313 <p>As you might expect, as a Core Web Vital, the First Input Delay is exposed with a <code class="language-plaintext highlighter-rouge">PerformanceObserver</code>. And just like the other metrics, there is a quirk in how it is captured. Rather than providing the delay value directly, the <code class="language-plaintext highlighter-rouge">PerformanceObserver</code> API provides an object with multiple timestamps that you need to calculate.</p>
         2314 
         2315 <p>In perf.js, add the following below <code class="language-plaintext highlighter-rouge">clsObserver</code>:</p>
         2316 
         2317 <figure class="code " id="code-365">
         2318     <div class="code-wrap">
         2319         <pre class="prettyprint lang-javascript">
         2320   var fidObserver = new PerformanceObserver(function handleFID(entryList) {
         2321     var entries = entryList.getEntries() || [];
         2322     entries.forEach(function(entry) {
         2323       data.fid = entry.processingStart - entry.startTime;
         2324       console.log(&quot;Recorded FID Performance: &quot; + data.fid);
         2325     });
         2326   }).observe({ type: &quot;first-input&quot;, buffered: true });
         2327 </pre>
         2328         <!-- <button type="button" class="btn copy-button yes-js">
         2329             <img src="/assets/images/copy.svg" alt="Copy" />
         2330             <span class="label">Copy</span>
         2331         </button> -->
         2332         
         2333         <figcaption><a href="#code-365">FID Observer function</a></figcaption>
         2334         
         2335     </div>
         2336 </figure>
         2337 
         2338 <p>The delay in first-input is the difference between when the entry started and when the entry started processing the first event handlers.</p>
         2339 
         2340 <p>Refresh the page in Chrome. You won’t see the FID console message yet though. You need to interact with the page first. Click on the background body somewhere. As soon as you click, you’ll see the FID recorded.</p>
         2341 
         2342 <p>Play around with clicking at different phases of the loading process. When you click before the content is loaded, the FID could beover 800ms. But if you wait until the page is done, the delay is only about 50ms. This illustrates how important it is to give the user indications of when the page is ready, especially if it’s slow.</p>
         2343 
         2344 <h2 id="sending-the-data-with-a-beacon">Sending the Data with a Beacon</h2>
         2345 <p>Now that you have some performance metrics, you need to save them somewhere. A great way to do this is with the <a href="https://requestmetrics.com/building/using-the-beacon-api">Beacon API</a>. The Beacon allows you to send small amounts of data after a page has been unloaded.</p>
         2346 
         2347 <p>This works particularly well for performance data because it guarantees that all the performance metrics are in their final state, rather than picking an arbitrary time to send the data.</p>
         2348 
         2349 <p>To use the Beacon, you wait for a <code class="language-plaintext highlighter-rouge">beforeunload</code> event, then call <code class="language-plaintext highlighter-rouge">navigator.sendBeacon</code> with a URL and data. The Really Slow Website™ example has an endpoint called /vitals that prints out the values into the server console.</p>
         2350 
         2351 <p>Add this below the observers in perf.js:</p>
         2352 
         2353 <figure class="code " id="code-205">
         2354     <div class="code-wrap">
         2355         <pre class="prettyprint lang-javascript">
         2356   window.addEventListener(&quot;beforeunload&quot;, function() {
         2357     navigator.sendBeacon(&quot;/vitals&quot;, JSON.stringify(data));
         2358     console.log(&quot;Sending performance beacon...&quot;, data);
         2359   });
         2360 </pre>
         2361         <!-- <button type="button" class="btn copy-button yes-js">
         2362             <img src="/assets/images/copy.svg" alt="Copy" />
         2363             <span class="label">Copy</span>
         2364         </button> -->
         2365         
         2366         <figcaption><a href="#code-205">Sending data with the Beacon</a></figcaption>
         2367         
         2368     </div>
         2369 </figure>
         2370 
         2371 <p>Go back to the site in Chrome and refresh it a few times. You probably don’t see anything different. Why? Remember that the Beacon sends the data after the page has been unloaded, right before the Dev Tool console gets cleared.</p>
         2372 
         2373 <p>If you want to see the values in Chrome, you can turn on the “Preserve Log” function in the Console or Network tabs. That should allow you to see the call to /vitals being made.</p>
         2374 
         2375 <p>Or, you can pop over to the terminal where you started the website. Each time /vitals was called with your performance data, the data should be sent to the console window.</p>
         2376 
         2377 <h2 id="understanding-the-data">Understanding the Data</h2>
         2378 <p>As you saw with the Really Slow Website™ case study, field performance metrics are hard to gather and interpret. Running a performance script will capture hundreds or thousands of data points that need to be distilled to understand the real performance of your website.</p>
         2379 
         2380 <p>Some of the data you receive will be from users with really fast computers and networks. Some of the data will be on a 3G connection in Siberia with a broken antennae. The truth is somewhere in the middle.</p>
         2381 
         2382 <p>For the Core Web Vitals, <a href="https://web.dev/vitals/">Google has some general advice</a> about the target ranges for the metrics. They recommend that you look at the 75th percentile of your field data, which means what value do 75% of your users do better than.</p>
         2383 
         2384 <figure>
         2385     <img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/image1.png" width="700" height="214" alt="Google's recommended ranges for vital metrics" />
         2386     <figcaption>Google's recommended ranges for vital metrics</figcaption>
         2387 </figure>
         2388 
         2389 <p>But this is just a starting point!</p>
         2390 
         2391 <p>You need to decide for yourself what is appropriate for your website, your audience, and your users. Charting your metrics over time can be helpful to see how performance changes as you release new code. For example, here is a Web Vital histogram and distribution chart from <a href="https://requestmetrics.com">Request Metrics</a>, a web performance tool.</p>
         2392 
         2393 <figure>
         2394     <img src="https://requestmetrics.com/assets/images/webperf/monitoring-core-web-vital/image5.png" width="700" height="112" alt="Web Vital histogram chart from Request Metrics" />
         2395     <figcaption>Web Vital histogram chart from Request Metrics</figcaption>
         2396 </figure>
         2397 
         2398 <p>You may also want to combine the Web Vital data with more traditional metrics like page load, hits, unique users, or page size to give you a more complete picture of field performance. No one metric tells the entire story.</p>
         2399 
         2400 <h2 id="summary">Summary</h2>
         2401 <p>In this post you learned about the new Core Web Vital performance metrics, what they measure, and how to use them. You looked at an example website and wrote a simple script for gathering field performance metrics from real users and how to interpret them.</p>
         2402 
         2403 <p>Web Performance is a complex topic and you should learn more about it. Developers need to lead in making the web fast and accessible for everyone.</p>
         2404 
         2405       ]]></content:encoded>
         2406       <pubDate>Thu, 03 Dec 2020 00:00:00 +0000</pubDate>
         2407       <author>hello@requestmetrics.com (Request Metrics)</author>
         2408     </item>
         2409     
         2410     
         2411     <item>
         2412       <title>How Hacker News Crushed DavidWalshBlog</title>
         2413       <link>https://requestmetrics.com/web-performance/how-hackernews-crushed-davidwalshblog</link>
         2414       <guid isPermaLink="true">https://requestmetrics.com/web-performance/how-hackernews-crushed-davidwalshblog</guid>
         2415       <description>Earlier this month, David’s heartfelt posting about leaving Mozilla made the front page of Hacker News. Traffic increased by 800% to his already-busy website, which slowed and eventually failed under the pressure. Request Metrics monitors performance and uptime for David’s blog, and our metrics tell an interesting story. Here’s what happened, why, and what you can do to prepare your site for traffic surges.
         2416 
         2417 
         2418 
         2419 DavidWalsh.name Technology
         2420 David’s site uses WordPress. It serves most content from...</description>
         2421       <media:content url="https://requestmetrics.com/assets/images/webperf/hackernews/title-2000.png" medium="image" type="image/png" height="350" width="700" />
         2422       <enclosure url="https://requestmetrics.com/assets/images/webperf/hackernews/title-2000.png" type="image/png" />
         2423       <content:encoded><![CDATA[
         2424         <div><img src="https://requestmetrics.com/assets/images/webperf/hackernews/title-2000.png" alt="How Hacker News Crushed DavidWalshBlog" class="webfeedsFeaturedVisual" /></div>
         2425         <p>Earlier this month, <a href="https://davidwalsh.name/leaving-mozilla">David’s heartfelt posting about leaving Mozilla</a> made the front page of Hacker News. Traffic increased by 800% to his already-busy website, which slowed and eventually failed under the pressure. <a href="https://requestmetrics.com/">Request Metrics monitors performance and uptime for David’s blog</a>, and our metrics tell an interesting story. Here’s what happened, why, and what you can do to prepare your site for traffic surges.</p>
         2426 
         2427 <!-- more -->
         2428 
         2429 <h2 id="davidwalshname-technology">DavidWalsh.name Technology</h2>
         2430 <p>David’s site uses WordPress. It serves most content from a MySQL database, which is a well-known performance limitation. To mitigate this, David uses <a href="https://www.cloudflare.com/">Cloudflare</a> to cache the content of the site and reduce the load to his server.</p>
         2431 
         2432 <p>Cloudflare does this by taking control of DNS and routing requests through their edge network before calling your server. When possible, Cloudflare will return cached content rather than needing to call your server at all, which is particularly useful when request volume goes way up. It’s even <em>free</em> for most websites, which is pretty awesome.</p>
         2433 
         2434 <h2 id="monitoring-the-spike">Monitoring the Spike</h2>
         2435 <p>Traffic began to surge to the page around 7:40 AM (local time), and the system handled it in stride. The median page load was acceptable at 4-6 seconds.</p>
         2436 
         2437 <p>By 7:50 AM, traffic hit the limit of the technology, around 100 page views per minute, and the user experience quickly degraded. Median page load times grew to more than 30 seconds. Unable to fulfill the requests, the site went down at around 8:10 and remained offline for about 40 minutes.</p>
         2438 
         2439 <p>Here’s the alert that went off in Request Metrics:</p>
         2440 
         2441 <figure>
         2442   <img src="https://requestmetrics.com/assets/images/webperf/hackernews/perf_alert.png" alt="Performance Alert" width="700" height="269" />
         2443   <figcaption>Performance Alert</figcaption>
         2444 </figure>
         2445 
         2446 <p>If you tried to read his post during that time, you had a frustrating experience. The page took a long time to respond, and if you got through, it was shifting around as asynchronous content was loaded and rendered. We can measure these behaviors as <a href="https://requestmetrics.com/web-performance/largest-contentful-paint">Largest Contentful Paint</a> and <a href="https://requestmetrics.com/web-performance/cumulative-layout-shift">Cumulative Layout Shift</a>, which both degraded quickly as the traffic grew.</p>
         2447 
         2448 <figure>
         2449   <img src="https://requestmetrics.com/assets/images/webperf/hackernews/web_vitals.png" alt="Core Web Vitals" width="700" height="332" />
         2450   <figcaption>Core Web Vitals</figcaption>
         2451 </figure>
         2452 
         2453 <p>Clearly, it was slow. But why? Why couldn’t it serve more than 100 page views per minute? Why didn’t Cloudflare absorb the traffic? Let’s dig deeper into the page and see what’s happening.</p>
         2454 
         2455 <h2 id="page-performance-history">Page Performance History</h2>
         2456 
         2457 <p>The performance report below for David’s Mozilla post shows a 48-hour window around the time he made the front page of HackerNews. The page is more than just the HTML document request; it includes all the static assets, JavaScript execution, and dynamic requests that make up the page.</p>
         2458 
         2459 <figure>
         2460   <img src="https://requestmetrics.com/assets/images/webperf/hackernews/page_report.png" alt="Page Performance Report" width="700" height="427" />
         2461   <figcaption>Page Performance Report</figcaption>
         2462 </figure>
         2463 
         2464 <p>Before the surge of traffic, the page had a median load time of 4-6 seconds. That’s <em>okay</em> but I would have expected a lot faster for a mostly-static site served from Cloudflare.</p>
         2465 
         2466 <p>Opening the site in and checking the document request in network devtools gives us a clue.</p>
         2467 
         2468 <figure>
         2469   <img src="https://requestmetrics.com/assets/images/webperf/hackernews/headers.png" alt="Request Headers" width="384" height="74" />
         2470   <figcaption>Request Headers</figcaption>
         2471 </figure>
         2472 
         2473 <p>The server is returning a <code class="language-plaintext highlighter-rouge">cache-control</code> header that says this content is not cacheable! Cloudflare is honoring that instruction and passing every request through to the server, as denoted by <a href="https://support.cloudflare.com/hc/en-us/articles/200172516-Understanding-Cloudflare-s-CDN#h_bd959d6a-39c0-4786-9bcd-6e6504dcdb97"><code class="language-plaintext highlighter-rouge">cf-cache-status: DYNAMIC</code></a>.</p>
         2474 
         2475 <p>The net effect of this is that Cloudflare has made the site <strong>slower</strong> by introducing an additional hop through their infrastructure, but not caching anything.</p>
         2476 
         2477 <h2 id="api-endpoint-performance">API Endpoint Performance</h2>
         2478 
         2479 <p>The page performance report above also shows that an API endpoint, <code class="language-plaintext highlighter-rouge">/sidebar.php</code> is called on every page load. The performance of this API degraded similarly with the traffic spike, but took 500ms to respond in the best of times.</p>
         2480 
         2481 <figure>
         2482   <img src="https://requestmetrics.com/assets/images/webperf/hackernews/endpoint_report.png" alt="API Endpoint Report" width="700" height="302" />
         2483   <figcaption>API Endpoint Report</figcaption>
         2484 </figure>
         2485 
         2486 <p>Checking this endpoint in devtools, it returns an HTML snippet of what we would expect, the static sidebar content of David’s blog. And it has the exact same <code class="language-plaintext highlighter-rouge">cache-control</code> header problem as the main document.</p>
         2487 
         2488 <p>By rendering the sidebar with an asynchronous uncacheable request, the server was forced to serve at least 2 database-touching requests for every person reading the post. This greatly limited the number of requests the blog was able to handle.</p>
         2489 
         2490 <h2 id="web-performance-lessons">Web Performance Lessons</h2>
         2491 
         2492 <p>Your website is different from this one, but there are some common ideas that we can take away from this performance audit.</p>
         2493 
         2494 <h3 id="1-reduce-dynamic-content">1. Reduce Dynamic Content</h3>
         2495 <p>This site was producing the sidebar content dynamically. It probably doesn’t need to be. It’s the same advertisements, popular tags, and related content to a post for everyone.</p>
         2496 
         2497 <p>Dynamic content is slow. It’s hard to cache and it often has to be fetched asynchronously. Servers simply have to do more work to produce dynamic content, and more work is always slower.</p>
         2498 
         2499 <p>Look for dynamic content and make sure it’s really worth the performance penalty over what could be delivered statically from a cache.</p>
         2500 
         2501 <h3 id="2-test-your-configuration">2. Test Your Configuration</h3>
         2502 <p>This site was set up to be cached by Cloudflare at one point, but over time things changed. Somewhere along the line from a WordPress plugin or hosting upgrade, the <code class="language-plaintext highlighter-rouge">cache-control</code> headers were changed, and the caching was broken.</p>
         2503 
         2504 <p>Software systems are complex and ever-changing. Be sure to test things out once in a while and confirm that everything is working as it should.</p>
         2505 
         2506 <h3 id="3-there-is-no-silver-bullet">3. There Is No Silver Bullet</h3>
         2507 <p>Simply adding Cloudflare to the site did not solve the performance issues, nor should it be expected to. Caching and edge networks are amazing, but your site needs to be configured to use them correctly.</p>
         2508 
         2509 <p>Performance isn’t something you buy or bolt on later. It’s a principle you hold while building and operating a system. <a href="https://requestmetrics.com/">Performance monitoring tools like Request Metrics</a> can help you focus and improve your performance over time.</p>
         2510 
         2511 
         2512       ]]></content:encoded>
         2513       <pubDate>Wed, 25 Nov 2020 04:00:00 +0000</pubDate>
         2514       <author>hello@requestmetrics.com (Request Metrics)</author>
         2515     </item>
         2516     
         2517 
         2518   </channel>
         2519 </rss>