|
|
requestmetrics.com.rss.xml - sfeed_tests - sfeed tests and RSS and Atom files |
|
|
 |
git clone git://git.codemadness.org/sfeed_tests (git://git.codemadness.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
README |
|
|
 |
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> |
|