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: "123abc987654"
361
362 # Browser sends in the validation request headers:
363 If-None-Match: "123abc987654"
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 <img src="picture-1200.jpg"
784 srcset="picture-600.jpg 600w,
785 picture-900.jpg 900w,
786 picture-1200.jpg 1200w"
787 sizes="(max-width: 900px) 100vw, 1200px"
788 alt="my awesome picture" height="900" width="1200" />
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 <img src="picture-1200.jpg"
812 srcset="picture-600.jpg 600w,
813 picture-900.jpg 900w,
814 picture-1200.jpg 1200w"
815 sizes="(max-width: 600px) 0, 600px"
816 alt="my awesome picture" height="900" width="1200" />
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 <img src="data:image/png;base64; iVBORw0KGgoAAAANSUhEUgAAAAUA
873 AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
874 9TXL0Y4OHwAAAABJRU5ErkJggg=="
875 alt="my awesome picture" />
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,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAABGdBTUEAALGPC/xhBQAABA5pQ0NQa0NHQ29sb3JTcGFjZUdlbmVyaWNSR0IAADiNjVVdaBxVFD6bubMrJM6D1Kamkg7+NZS0bFLRhNro/mWzbdwsk2y0QZDJ7N2daSYz4/ykaSk+FEEQwajgk+D/W8EnIWqr7YstorRQogSDKPjQ+keh0hcJ67kzs7uTuGu9y9z55pzvfufec+7eC5C4LFuW3iUCLBquLeXT4rPH5sTEOnTBfdANfdAtK46VKpUmARvjwr/a7e8gxt7X9rf3/2frrlBHAYjdhdisOMoi4mUA/hXFsl2ABEH7yAnXYvgJxDtsnCDiEsO1AFcYng/wss+ZkTKIX0UsKKqM/sTbiAfnI/ZaBAdz8NuOPDWorSkiy0XJNquaTiPTvYP7f7ZF3WvE24NPj7MwfRTfA7j2lypyluGHEJ9V5Nx0iK8uabPFEP9luWkJ8SMAXbu8hXIK8T7EY1V7vBzodKmqN9HAK6fUmWcQ34N4dcE8ysbuRPy1MV+cCnV+UpwM5g8eAODiKi2wevcjHrBNaSqIy41XaDbH8oj4uOYWZgJ97i1naTrX0DmlZopBLO6L4/IRVqc+xFepnpdC/V8ttxTGJT2GXpwMdMgwdfz1+nZXnZkI4pI5FwsajCUvVrXxQsh/V7UnpBBftnR/j+LcyE3bk8oBn7+fGuVQkx+T7Vw+xBWYjclAwYR57BUwYBNEkCAPaXxbYKOnChroaKHopWih+NXg7N/CKfn+ALdUav7I6+jRMEKm/yPw0KrC72hVI7wMfnloq3XQCWZwI9QxSS9JkoP4HCKT5DAZIaMgkifJU2SMZNE6Sg41x5Yic2TzudHUeQEjUp83i7yL6HdBxv5nZJjgtM/FSp83ENjP2M9rypXXbl46fW5Xi7tGVp+71nPpdCRnGmotdMja1J1yz//CX+fXsF/nN1oM/gd+A3/r21a3Nes0zFYKfbpvW8RH8z1OZD6lLVVsYbOjolk1VvoCH8sAfbl4uwhnBlv85PfJP5JryfeSHyZ/497kPuHOc59yn3HfgMhd4C5yX3JfcR9zn0dq1HnvNGvur6OxCuZpl1Hcn0Ja2C08KGSFPcLDwmRLT+gVhoQJYS96djerE40XXbsGx7BvZKt9rIAXqXPsbqyz1uE/VEaWBid8puPvMwNObuOEI0k/GSKFbbt6hO31pnZ+Sz3ar4HGc/FsPAVifF98ND4UP8Jwgxnfi75R7PHUcumyyw7ijGmdtLWa6orDyeTjYgqvMioWDOXAoCjruui7HNGmDrWXaOUAsHsyOMJvSf79F9t5pWVznwY4/Cc791q2OQ/grAPQ+2jLNoBn473vAKw+pnj2UngnxGLfAjjVg8PBV08az6sf6/VbeG4l3gDYfL1e//v9en3zA9TfALig/wP/JXgLxWPWywAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAeGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAAqACAAQAAAABAAAAZKADAAQAAAABAAAAZAAAAACTSTvwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoZXuEHAAAU7ElEQVR42u1cCXhTVdo+//P4O+P8PzACLlDa0r2lQJvua9I0SZu0Sbo3LUk3ZKdQaKHSFoQijgwijILIoKIzOC7jMrjCqIigrIIoi8Aoyg+ytzDsAgX+99yT3NykBZKbpJRnyHOfPLeXy8297/m+93u/73znkut3Pw5/yF0I7oJ1F6y7YN0F6y5YHvtcu3bt6tWr+L4L1q2R4ve7OGSkKyDVeuLEvn/t/fXiRR6yu2B18GG4vLpk0SBC6oeXffHJiq6MV5cAa+e336j8SGFiWDIhz82ZdeXKZe6f2v7TwbrGPletH+aJi+Y8kRlExhh0wOvp5kac0QXti3QOQG34XLlyta2tPX/jIL5PnWwdkatSB//XqMKsJEKWLXmOZzSGbZvth16q06EknQBTB8e5p7cznJOtLcNzVblRvkO1aan/S75e/yUOXr506dbD0NEY3ElgMXPg/zxx7Ni2zRs/fPvNlxbMm9fcNKu+prl2zOOTx8+d3rBk/px3/vaXLz5ZeeTQLyAvIFUiiyxKDh+aLTt39iz77wiUrS0nDh08cGD/zwf3/3z00C//PnXyyuXLdvTnaeVBPIETb01nz5xGgJvdNKk8Mykr9HdKb6LqT9RBJCusW/aA+7PCuquDCagdxzP8iXZgzykjK0YVasD0OD87vOf8x6etWP7OH6dOrq00ALtSCuKA4pRwoyIGNvjoyPJnnpjxwVtv7N65/cL583ao3RlgMSq5ePHCR+/+fXiOMr0PwWMXpww0KmLLMhKBQvsNx02q+NK0KH2kF84ckh4D48IRfWRf+UMEsObF+gMmg3QwjpdIIwypgwoTQ+GwmtD7FP2IJuSeMcXZf3n+2b27dvIwdej+XQss5gXfb/+2pqwAcQ1PXqWVAQs8/5D06FJ5FBCxbBLrvjxqiDwa5wDQIXLzEZxgUsYDSnzjON3oRWKMbKNH4gBoOQU6oSAhBKOSGUiaa8duXreWIeV2l3QnWGxUN321Fj6VHxc0VCcvTg6HXRQlDaBIpUU5v0lKZZJbnJAmKZFJgF2lJgWjAtdW+pAZE0fv2bnD7XgRt1oVvbO506dEE6KP6ANuAgHNnFRdXaLnnEsUXnIHzwFkkcUpg/BDsLJIQnSDep8+dYqXJl3OshhYiFPbv9myZ9eOo4cP7d2148Vn5lZoUvAkcDRRxuXQhouDy8YatI/VjJw+YdS0ccPf+utStzujmzlLQKvXPvv4A8QsBDuDNIJDSuJRsHD9R3TyBU/O+HbLJvy6J2ieuNGs+Dtbv+ZzhKc4QuARMCuP2pQQL/igOuTeoqSweTOn7vpuG58huMu43AMWzwsQltCcIPjc6P5V2TJEKzhgJyDFyB6xEhwP5oKa0w7sveDJZtyP3R3eTrA4g7rC9j/98D0EPnXIPbhjbCxUOcTQboXMpIyrUCdDWEBJFCaGrFz+jrtckriIFJMLJ44dnd1Yl3IvgR1BGbHw1LkYdeCV5dyA4a6ebKhFtuQ6XuLB4rXyN5s2lKkS4HqVWVLoA6gel6yJClSbrYOrtT+ho3NwJ7gfsEGGH0G29MPu7130R+IiUu+9+RpEILyvKlvK0ZNIpKxPzolMgzQSMRTfNL/hjdR8Ze4EWaT5HJzACVfLf+9AfwEvJEw5kV7fbdnsin0RV5BaunBeyn8TZBtgKFeInIVL7uEhx6IqVPHDslJG58jH5SsnFGXUFqvrDJpJJVl0M2iwjyMTCjPwr6P0aUM1SSZFLIdvBDUlpE3tRouRA2Jl9oAeO7ZtFY0XcZ6nzEi98KenkP1ZlIFELEx0K5ZG4ApD1UnVeQrAMcWoayzLaeK2RuumbzTpBX9aT3h0iHZCUeZIrRSoFVsga++SGNHCpLCixNAD+38ShxcRx+jL33g15TcE5u1KvBvCPQP8CHYEY2kw6dnDYwd4Ob41WECsL80em5uONLuYymBbIsO+TFKhTtEOeqC20nDxwgURZWsiwgH37d1TEB8MswdVGTkXEIEXBh8wmRQxcKjGdhg1mCgEjmztUasfoh2enQqPZuNhZ19DtWnS7uS1lxaLyISIc3bFXX3J/Dk9CBlbonv2DzMK4oOM6bHikKrIiIctUD8y6uBKdo89xaSbYmKombcpZgTpDgN0ivWg3g6y6rx0nviFKoyWd5Rx2oG9fv7xB2eNy2k3xPfWTetnTqr+ce/u5+f+AVoZ3OkUu3PeF1mmjIMJMIMyE1B5jr0bmszfDTbA6YXfQkzN53NbU3nu+Hyl+cbkQuOKBM8qfcnC2TOdNS7xOguZBIIL3NA52rKcidA2rSIPTwvIJharxxeoxuUp8HggL/qoZTlCvDoCS2cFy+q2VrBgqrjIaH2amb9s9QQSo/y4wMMHDzhlXGLAunKF5jd/e3ExxoepZMfBoumuNAI0jOfBNyIgaAtXgFcWc8IK++XKOIRF3rmEhmaHlNWg+I07nw+jU8tzH9EkG2iByIbp4Q2KfuSDt99wKiwScSLr3Nmz44bkguY5y3I6favKTGQxy8DpWCY7jenRRnl0CQMuNQIh0kpGtvTUAccLkJpcmk0lmE42IjsVoMO4bEMQLasi5UBYnDV5vJBe3A9WG2dWWzeuVwcRMKW48ouB0+W85gZGsK8iTiKVK2OBGo4XpQ7GAzdy/thgAYL61w3UA1NnE4sygQuwZmk8LmJjVjDt9Gg2xiCvYXrFydYWx3MgpwmejcHzc59EwkVzZrEKix9qQFMsjYwMeDigb/cgr98nhnkXpTKWodpyckkW5KgVDpN+anmOcOPZjZ0AF8Z/LFPE5qcMzk8ejDEw8uVs7prFyQOnVg8zKeN0EX3UweRf3+/iLcAjOutkSwvGBOMDY3axusB0aWDfHjHBfQtSBusTwgf5PSgd5FdGiYxGrpoCFQ8HdiaVZI/NzxhbkMF/jyvMpPGUC6mQbLhaTtLAcN/egX27+/fpNtjvQYyE0WL+EPHpfclfFy/ctnmjIXXQYzUj2TyugxwvBqxNX65ReFGzcr0EimHPSx4EazKlR5vSY0x0movSFiskAKzxHFgstA3VpQ8IC/YL9PcLCqDfgQH+QQG+Af7KxCjwFLQCGAohIjqob3K4L/P06KA+Yd49LW4IXk/WhP5Pc101o13WEea4eiDOiix8Fs+bnd6HFmRKZBJ3FJ6iuIBo66RpVoXBWLyuJAtIefn1Dw0NCgoJZFtwSCD+/I1Xv0JF0mOVeVAeTCjAMPHNuC8zOpjLFilYsKzcaN/xxrwzp087Re0iwTp75syoQk1ebEBZhlgflHc4/Sf4V27OFdyMcMb0PfQqPK5/oH8IkAoO5LfA4MCwsOC+fv0zkqKhEmBf5qxe8BPUoy2WBd5ALl2emXj86BERuTRx1gd379yeGUBo1VjutvKeTSUPVAUaTo2AvBDyOthqcHgonA72FWgBC5aFP+/39jGokkH2OBnaDSgzQceHEd5UweuwsvxYf0uu42GwIOSkv4cPptJMQhRe1rqdNNJ2Y9U+iUkROzpHbpWjnG4AFka1tKePD3gK1sQhFYSd3r4+MRHhgLLRomCBF9BhlzJPWVssl5v3j9OE3LuTm/vxYNWBueG8mVMzA53OB82Bj+ax0aBhbCO1UshOyGuIeHwPz0qBqqrOTZ9QlMno3IKUns/4cLAyOy18QPBD/X1BVQFBAQ/6+qYnSGoNWdb0iFNbEBy4fpkylrPWaGDEiyz4hNKbfL3+Kw+CxZC69OuvE8qL8mL8MT7OEhZTCbAazkxy+bIMH/gby9on0no+y2FHppXnTjRkZSZHP+DrC4KHrdGiIC/ETFa8WFGwzqABcMg3GZfRWQx1svwhsnbVJ54Eiyms1hak7CwRFQEWd7tReAA82KO24ru9ImcPz1dm+IMMUMiI6oLMaRW5DBrLCZbs2rYoiP0yyla0haBCnSLrRVat+NCDYLHrHv7lILQcJZfUwU6LLEbeMgkcrVHgNVaAjMLUT1iuMj85q09wFQUd0+71XBVsXIEKRtRgNUP7AcDAVKjiWd6DwZb1JCvfe9fjlnWqtSU3yrtEGjFUKxOBF8e7ESCsRmuOIqxJ6W2LfHp+h6mHicWaUJ9eNUWZ0yvzARMAah5aMEwrw0HOvoRg2aTWNA2ygFWpSZXdTz569y2Pl5XBXPv27lm/5nOAVZgYalLGO92OII3g02M7vAQ2pRe6IZ8/w+m0SRFhPr1hm2A9bKNzFd4P/NaoSpxWkUdrre2qqQws/BPInklTClZP8vE/PA8Wu/p3WzanP0wrWc62XNFqMqxSnWQlY9ONCnsdcxZcT58c6denW/wA39hQb/8+3UsU8UDNWikUgiUo2rAsinfDTz5Y3hmWRct+LzyP30O6Iy4gYpCFFGOLlN52qsLiU0YrXjCiCUVq8HSVJrWOK7cKA4UQaAYWrBghhd0qBUudktabrP7nx52hs/ADzXVjNSH30bDobG5oEeiI5cJC1Y0sS3hQGDdhX49V5AEmlgzZVgftz8c5NQUqNn1LpUNmEhLbDWtWdwZYrS0n4IBFyeEmZZyI3JDRFsSnBSz9FJNjYAmKpazEjphoa1C2YAksa0yOnEuwaY8veBbp2ndbv/YsWNZFSf1Z6V3kTD0GGZJdWNKztQudHUbCh7eFUm/ny+05ju3TMjxnWawInhPl/ePePSKaRG5DbsggA+naqCHTjfjrJvt2lmgzIcZ0GZuONCliWHpoUsUXJQ0oU8UfO3LYs4k0c8P5YnNDIc3DKeAaTVwUs5kltHElfYPYfeZ9k0qyRmilyKvN1cQ0CRwiR+KNjO38uXMezw0vXDg/tkRnmdSRiC7RsDQNw15fmg3UrPxldAUsnVVtMMviFANzQPbTGGNNyD1/nFpv1+Ti7nSHc+//+2lfbpSP0xOrHUp5WWRlRkJVJp125F3SPmVx0iV57cZbFjdbIeFXbQAspQ95/eUl10Wtx3YULNY4+tXnnyr6cewuc7VZlGku1rcGZ2myNy47Yro5WFZdJgRrYlGmufBtLgFGI4KDQzZ+uYZ/Ig8l0tSylv35OYRCS3OtG6rvvINAN3bkjM65pLDXAVcDW1nn7mlJPgb5LL6PHj7kwf4si7lea64dqxv8MKew3NkpC+My50A2WYvQxJwHy6QflpVcbF6vALCoWeHmZ06qFjFV4QRYbBBOtrY8opOznix3dhbLWTUicly+0k6pOiBW9Q12XUc27B7DdwGWSCMq1MkKL7KC6/T2YJskm7Dds3O7PqIP08HubsOm/IVnqxcyval9M4j+JmBNsU1xxnHTYrgmXRSrky+aMwtjXJw8gM3riFu96YRlffbR+wglZaoETywvYWkQp+xzbiC7bhENhaEQxxFnaWehMk4f2beuqgQqtCh5wNMzGq+7sKifOEJYzL1fWjBP5Su+v8FBsYq00SpWrb1XOkfAEraHMM2MoUX4Wzj7cdw/kmc2A+asvHICLDYO8MTpNaOyw3tyYHlm9YRlKhSP2sSVE4Rp9g3qXzYGaImDeoQL1sxUzs3lrFu9qv28ugfBaj1xHOxekBDiejOII+TFNc/Y42UHkE2p3sJWTawgw+WtrB/TqIhpOX6ckYmLy8OIgwpr766d2oG92TJmT6+5MdCO09jJXG+udabHtjG3ncXpBRXkODa9CieADz7d3HTdTUs0HbWsNZ+uTHuAIPp2wiovM16K2EkGTZOlZCwQBzxwNoV2NvE1IjuVaSvYVKUmVdqDrPti1XU3vanlVmBdM48JtHtabzGlZPHLeLlpPk586QEZHJP5prDgx+vPUfo0ZII42WCW7BKTKr4wMXSoVvbvk62dZFnsNy5evFBbVaKP9PIgu99gUhZitUqdWFusHqmTgbZthYWen48o5fqWrC00MgnGVf4wWbpwvjCgexYsZr17du3I8Ce0x6iTV1paxD0X2jhhkadoss7lmIXCSK3UQFf/CEIqNzEh60XYC7nc9QIRh8B64+UXqA9qXKqOuuKS1m4kmWRCYQaPl7C0IGyBM9Jae5w6iOz4ZksngWVuBrn068TKYnHNIJ5wTOzUAC+uiwTpEVs/Z7dsDkixpUUH9/983X3vLSO3zHKQEmpC76PvipF1/oLnjvGCgYPC6gyayowErhBql35JwBj58UHDc5WnWOd2p4HFVlK4q4blJryizItc29mUpdE2SR/Rd/IwI0JTZ7gh+4GLFy5MKCvMiw3wtBYV2Ul/A0unb6AK6zarvob1s1zzNFisLLNj29bs8PuN7B1FXQmsm29Muz/zxHTX88Fbg8ULk1cWPevRSoPnwFL1Jy8tmOdGkXVDsJgPnjl9eoxBmxfj13UIy0F1RmdxvMnrS0XO4ogBa8uGdfjJMudbi247WLhnRT/yj9eXmZ/FTe85IjdZSbHoqScUXm5bSdGZ3M9akj98583rnn5/FgOr5cTxMlV8ccpAcd0yt/fFKtBZAIt1rLV5FCzmg6v/+bG0m6sNILcNLFWC0od89tH7HgeLfWZOqs4O7yl+gc5/AljMw/fv+0Ef6WVOBu8osxKC9amnwWKXfvOVF+ms152lGNpxFmt29xRYjK3OnztXXapHItrVUhznpIM3ef/vr11366u/SfsUh75oNICYxC4W7xKilNNZb7/6ikVnuUmUtlnens3Lq3nNTeqgOy/FsUt3QCPL/vycR9KdtrYrDLID+38qSgwtkUa4t1Wm88HK8COLn54tnHNxA1ibtqxje+w9IG8te5nrr5XeidQuBEsdTOZOn+LmqsOExmFV4wu/30PfRHz2zOkR+Rn5cYFdVF7d/EWAtsW/7IG9po0fwSzALRwPxKkbrtu4JlHn/eXGLzasXZ3y29s2MeFIj6DldYDmGumQjm+SW2UfRVfZnzt7xi1gsSuYOevwkV8qq/NMWUmlUklXqzFY3ghI35hSzr0OcHh2ShmdkohkXc8dgMVW2WeYV9m7mEszpXboyDHC5m/wvX7t56k96AR9lzIrNpWPnRFaKXtbFL84lVs4Hm3o6BXXVCHKo3Ik/X7cu9tFsNo4m7p06XJV3XTCk9/c6Q26wQ91qSDIFnNWZMSz9lzh8mm6nK48d5JBU86ZmJ0/mheO+5AtG9a54oY8OE8tWkricwizsV3bv9WE/Y5rgYzuOkiBmB7RJDNrsl+4YplkrR+ircxIaD/NwxZhrlj+tutgvbdyFYnVy0pH/z8ciXUA+l1cTwAAAABJRU5ErkJggg==" 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("[data-src]"));
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("data-src");
920 if (src) { el.setAttribute("src", 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 <img src="picture-1200.jpg"
948 loading="lazy" class="lazy" />
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 <img src="picture-1200.jpg"
972 loading="lazy" class="lazy"
973 width="1200" height="900" />
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><! --></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 <link href="https://fonts.googleapis.com/..." rel="stylesheet"
1100 media="only screen and (min-width: 600px)">
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) => {
1207 console.log(entryList.getEntriesByName("first-contentful-paint"));
1208 }).observe({ type: "paint", 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 === 'hidden' ? 0 : Infinity;
1265
1266 document.addEventListener('visibilitychange', (event) => {
1267 hiddenTime = Math.min(hiddenTime, event.timeStamp);
1268 }, { once: true });
1269
1270 new PerformanceObserver(entryList => {
1271 entryList.getEntriesByName("first-contentful-paint").forEach((entry) => {
1272 if (entry.startTime < hiddenTime) {
1273 // This entry occurred before the page was hidden
1274 console.log(entry);
1275 }
1276 };
1277 }).observe({ type: "paint", 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 => {
1298 console.log(entryList.getEntriesByName("first-contentful-paint"));
1299 })
1300 // Some browsers throw when 'type' is passed:
1301 .observe({ type: "paint", 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("Starting performance monitoring on " + 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"><script></code> element after the existing <code class="language-plaintext highlighter-rouge"><script></code> elements:</p>
2160
2161 <figure class="code " id="code-68">
2162 <div class="code-wrap">
2163 <pre class="prettyprint lang-html">
2164 <script src="perf.js"></script>
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: "paint", 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 === "first-contentful-paint") {
2227 data.fcp = entry.startTime;
2228 console.log("Recorded FCP Performance: " + 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 > data.lcp) {
2258 data.lcp = entry.startTime;
2259 console.log("Recorded LCP Performance: " + data.lcp);
2260 }
2261 });
2262 }).observe({ type: "largest-contentful-paint", 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("Increased CLS Performance: " + data.cls);
2296 }
2297 });
2298 }).observe({ type: "layout-shift", 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("Recorded FID Performance: " + data.fid);
2325 });
2326 }).observe({ type: "first-input", 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("beforeunload", function() {
2357 navigator.sendBeacon("/vitals", JSON.stringify(data));
2358 console.log("Sending performance beacon...", 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>