layout: true class: center, middle, bright --- background-image: url(resources/goats_in_the_mist.jpg) # Resource Hints ##Clueing the browser in ??? Hi, My name is Yoav Weiss and I work for Akamai on making the Web platform faster. --- background-image: url(resources/wilderness.jpg) ??? Now, I live in the French country-side. My family and I have a decent chunk of land which, historically, I did a very poor job at maintaining. So, before blackberry bushes eat up everything, we decided to clean all that up, and in order to prevent regression in the future, we decided to ... --- background-image: url(resources/car_selfie.jpg) ??? ...bring in goats to take care of that area for me! --- background-image: url(resources/goats_in_new_home.jpg) ??? They settled in, and started eating. Unfortunately, a few days after they came in... --- background-image: url(resources/wolf_attacked_the_goats.png) ??? this happened. Eventually it turned out it wasn't a wolf, just a bad, bad dog (which literally makes me "the boy who cried wolf") --- background-image: url(resources/hospital.jpg) ??? One on them didn't make it, and the survivors undergone a healing period, during which one of them had to wear a t-shirt in order to protect the wounds. which was sad, but also provided us opportunities to take photos like these --- background-image: url(resources/looking_after_goats.jpg) --- background-image: url(resources/eating.jpg) ??? But now that ugly part is behind us. We're now pretty happy about the arrangement and they keep their end of the bargain and keep their turf clean. --- background-image: url(resources/stereo.jpg) ??? The thing about goats is that they are an incredibly intelligent animals and would do pretty much anything to reach something that they want (in this case, she really wanted to put on some music) --- background-image: url(resources/run.jpg) ??? They learn very quickly and once they have a certain routine, they'd prepare for it and usually embrace what's coming --- background-image: url(resources/will_not_move.jpg) ??? But when something new happens, they're kinda at a loss, and will fight you even if the outcome is for their own good (like when we started to bring them over to hurd in a new place. --- background-image: url(resources/in_the_hen_house.jpg) ??? So the other day, one of them managed to sneak into the hen house and eat a ton of chicken food. Now that's extremely dangerous, since if they eat too much grains, they can bloat up and die. Litterally. you don't want that. So, I quickly went looking up what goats should and shouldn't eat, when I stumbled upon this sentence ---
??? That sentence made me realize something. Goats *are* browsers And there are many parallels between goats and browsers --- background-image: url(resources/closeup_at_door.jpg) ??? Both are fairly intelligent in dealing with situations they already encountered, and figuring out ways to optimize oftly encountered situations --- background-image: url(resources/goat_in_car.jpg) ??? But both are clueless when it comes to new situations. A goat facing a new situation will not know how to prepare for it, no matter how much us humans will try to communicate what will happen to her. For example, a goat that is about to go on an hour long ride for the first time in her life will not... evacuate herself before getting on the car, no matter how much you want it to. --- background-image: url(resources/confused.jpg) ??? Very much like this goat encountering glass windows for the first time, a browser going to a new domain for the first time doesn't know what it is up against. It may have some heuristics about opening more than one connection, but not much more than that. It has no clue which 3rd party domains it will need to connect to, which resources would be required and so on and so forth. --- background-image: url(resources/we_know_better.jpg) ## We know better! ??? The thing is, in both cases, humans know better. browsers may not know what's coming, but quite often, we as Web developers and even the Web server serving the site, can have a pretty decent clue about what the browser is going to need. --- background-image: url(resources/communicate.jpg) ## How do we communicate that? ??? So, we can't communicate what's coming to goats. Believe me, I tried But browsers can be a different story. How can we communicate what we know to the browsers, so that they can prepare for what's coming and significantly improve the page loading performance? --- background-image: url(resources/link_prefetching.png) ## Proprietary extensions! ??? Well, many years ago browser vendors have realized that this is an issue in which Web developers can help out the browsers, and added a bunch of non-standard extensions that enable us to tell the browser what's about to happen. The problem was that these extensions had some compatibility issues between browsers, some parts of them weren't well defined, and it begged for much needed standardization. --- background-image: url(resources/resource_hints.png) ## Standardization! ??? Enter Ilya Grigorik, who standardized and rationalized these various ad-hoc hints under the roof of Resource Hints and the preload spec, as part of the WebPerfWG. So let's go over these various hints and see what they do. --- background-image: url(resources/dns-prefetch.jpg) # DNS Prefetch ??? The first hint we would discuss is dns-prefetch, which has been around for a long while. What does dns-prefetch do? Well, it tell the browser that a certain host would be required later on (either as part of the current page or the next), so that the browser would perform the DNS query ahead of time and outside of that host's resource critical path. --- background-image: url(resources/dns-prefetch_diagram_before.jpg) --- background-image: url(resources/dns_prefetch_after.jpg) --- ## Scheme doesn't matter ??? Since this hint just tells the browser to issue a DNS request and cache the reply, the browser doesn't care about the scheme of the URL in question. You can give it a scehme, but in general it will be ignored. --- ## CORS agnostic ??? Similarly, th browser doesn't care about the CORS mode of the resource fetched from this host, since it won't establish any sockets. (We'll get back to that later) --- ```html
``` --- ```http Link: /example.com>; rel=dns-prefetch``` --- class: nocenter
pr attribute
```html
``` ??? It can have a `pr` attribute which indicates the probability that this resource would actually be needed. That part is not yet implemented anywhere, but it's in the spec, so it might be one day. --- ## Very low cost ??? And the cost of DNS requests and responses is very low since both are fairly small, so dns-prefetch links is something you can feel comfortable adding even if you're not 100% certain that the user will end up using them. (Obviousely you shouldn't add ones that there's very low probability that the user will need) --- ## When? ??? The main use case for dns prefetch is when you have low ccertainty that the host will be used, or when you don't yet know the scheme or CORS mode that will be used. It can be used for hosts both on the current page navigation or for hosts that are likely to be used in the next page navigation. But why shouldn't it be used for hosts where you know the scheme and CORS mode? Because for those, we have a better option. --- background-image: url(resources/preconnect.jpg) # Preconnect ??? The next hint is preconnect. It does what it says on the tin. It preconnects to the destination host, taking the connection time outside of the critical loading (and potentially rendering) path. Wasn't part of the traditional hints - you can think of it as dns-prefetch on steroids --- background-image: url(resources/preconnect_before_backup.jpg) --- background-image: url(resources/preconnect_reallife.jpg) --- background-image: url(resources/preconnect_after_1.jpg) --- ## Scheme matters ??? So here, unlike dns-prefetch, the browser is performing a full connection. That includes DNS, TCP connection and TLS connection on top of that if needed. So, it's fairly obvious why a scheme is needed in case that the destination host's scheme is different than the current --- ## CORS ??? Now CORS. Who here knows what CORS or Cross Origin Resource Sharing is? In a few words, CORS is a way in which the browser can know it can do certain things with certain types of resources. --- class: nocenter ```html
``` & ```http Access-Control-Allow-Origin: *``` ??? For example, images can be fetched across different origins, but the web site can't really examine what it got (using canvas), unless the request included a `crossorigin` attribute and the server included an `Access-Control-Allow-Origin` header. And fonts by default are CORS enabled, meaning that the request for them doesn't include user credentials (e.g. cookies) and the server response must include an `Access-Control-Allow-Origin` header, in order for the to work. The thing is, the browser uses sockets from different pools in order to fetch CORS-enabled vs no CORS resources, and mixing connections between these pools would result in privacy concerns. Therefore, if we know we'd need to fetch a CORS-enabled resource: --- ## crossorigin ??? We would need to add the crossorigin attribute to the preconnect hint, so that the right kind of connections would be established --- class: nocenter ```html
``` --- class: nocenter, wordwrap ```http Link:
; rel=preconnect Link:
; rel=preconnect; pr=0.8 Link:
;rel=preconnect;crossorigin ``` --- ## Low cost ??? The cost of using preconnect is not very high if you get it wrong, since no resources are involved. Still, using it when you're not sure the connection will be used is slightly riskier than dns-prefetch, especially for TLS where the connection establishment may also include downloading certificates which can be a few KB each, so might add up, and contend with your critical resources, if you open a lot of them. OTOH, even though TLS connections are "costlier", the benefits from them are also significantly better, since TLS connections take significantly longer, and moving that phase outside of the critical path helps a lot. --- ## When? ??? The main use-case for preconnect is when you know the host, scheme and CORS state of the resource to be fetched, and you have a fairly high certainty that the host will in fact be used, but you don't know the exact resource that the browser is going to need. You could also use it if you know the host will contain non-critical resources that you don't want to compete with critical content. So, you just preconnect to the host, taking the DNS request-response, TCP handshake and TLS handshake out of the way. And like we said, using it when you're not sure the connection will be used is slightly riskier than dns-prefetch, especially for TLS where the connection establishment may also include downloading certificates which can add up in terms of KBs. --- background-image: url(resources/eating_trees.jpg) # Preload ??? Moving on to the item, preload. What preload does is indicate the browser about a specific resource that the browser will need further down during the loading of current page, so that the browser can start downloading it according to its priority ASAP. --- background-image: url(resources/preload_backup.jpg) --- background-image: url(resources/preload_after_1.jpg) --- ## Not a hint ??? Preload is not like all other Resource Hints. It is different, it is special, his mother told him so. Unlike the other hints we discuss here, preload has MUST semantics. When a supporting browser encounters any of the other hints, it can decide to ignore it, or to "downgrade" it. If the browsers is stressed in resources, it can decide to turn preconnect to dns-prefetch for example. Not so with Preload. When supported, the browser MUST fetch the resource according to the priority specified. Because of that, `preload` is not part of the Resource Hints spec, but has its own spec. Because he's a special snowflake. --- ##
subresource
??? As part as the earlier, proprietary incarnation of Resource Hints, there was also a hint called `subresource`. It was destined to do what preload does, but failed miserably. The reason it failed is that it didn't have priority information. Because it didn't have priority information, it was tagged with a fairly low priority. While that may seem like a minor detail, priority information goes a long way in the world of ruthless competition that is Web page loading. Modern pages have a *lot* of resources, and a resource that is of lower priority *will* get loaded last. That means that in most real-life scenarios, the browser discovered the "subresource" resource, added it to the download queue, never sent it out because there were always higher priority resources, and eventually the browser would discover the element that needs this resource, bump up the resource's priority in the queue and send it on its way to the server, at exactly the same time it would have left if "subresource" wasn't defined at all. So, "subresource" is scrapped, and "preload" replaces it. It was only ever supported in Chrome, and I'm hoping to deprecate and remove it soon. --- ## as * script * stylesheet * font * image ??? The main difference between `preload` and `subresource` is the `as` attribute, that enables the developer to say what the resource that they're downloading is, and therefore the browser can prioritize the resource accordingly. The 'as' attribute value can be a set of fixed values that map to resource types. It is not defined as MIME types for various spec related reasons. --- class: nocenter ```html
``` ??? How does preload look like? Not very different from the hints before it, but you can notice that `href` is pointing at a full URL now, not just a host. The priority is defined using the `as` attribute, which can be a set of predefined resource types that you see here. It can also contain the crossorigin attribute, if you're preloading an image or another resource that requires that. --- class: nocenter, wordwrap ```http Link:
; rel=preload; as=script Link:
; rel=preload; as=image Link:
; rel=preload; as=font; crossorigin Link:
; rel=preload; as=stylesheet Link:
; rel=preload; as=image; crossorigin ``` ??? Also in Link header form. Not yet supported in the experimental Chrome implementation, but it will be. --- ## High cost ??? Since here we're downloading the entire resource, and most probably contending with discovered resources while doing that, the cost of adding the wrong `preload` hint is high. When you're adding a preload hint, you want to be sure that the resource will in fact be used, and it is important to have the browser discover it early. Ideally, you would use that for resources that are critical for the page's rendering but are invisible to the preloader. --- ## What's a preloader? ??? "What's a preloader?". Well, how does a page load works? The browser get the HTML from the network, tokenizes it, parses these tokens, creates DOM elements, and the DOM elements trigger loading of subresources. That's all nice and dandy, until blocking scripts come in and (as their name suggests), block the parsing until they finish loading and running. (for various reasons I won't go into here. I did a whole talk about that in last year's Velocity) So, in the early days, blocking scripts meant that no resources were fetched until the blocking script finished downloading. That wasn't so great. Browsers, in order to overcome that, created preloaders. What preloaders do, is go over the tokens (which keep piling up, since the HTML keeps coming down the pipes), and figure out from the tokens which resources are most likely to be needed later on in the page's load cycle, and add them to the download queue. That means that all your resources that are part of the HTML, will get discovered by the preloader and their download will kick in early. OTOH, all resources that are outside of the main HTML, so in JS, CSS, iframes or HTML imports, will not get picked up by the preloader, and their download will start later, making them more likely to become a bottleneck to one of your critical metrics. That is, unless you would add a hint telling about them to the browser. --- ##When? ??? So, as we said, you want to use `preload` for resources in the critical path that aren't easily discovered by the preloader: dynamically loaded blocking scripts (or non-blocking scripts that draw important bits of the UI), imported stylesheets, ES6 modules, HTML imports, fonts. Everything that's impacting initial rendering, and the browser needs to download an external resource in order to discover it. --- background-image: url(resources/prefetch.jpg) # Prefetch ??? Up next: prefetch. Prefetch has been around for a while, and is one of the parts that the Resource Hints retroactively defines, rather than invent. Prefetch is in many ways similar to preload, with a couple of significant exceptions --- ## Hint semantics ??? The first is that we're back to hint sementics. Browsers can downgrade prefetch to preconnect, dns-prefetch or ignore it completely. --- ## Next navigation ??? The second is that prefetch indicates that a certain resource would be required for the *next* navigation. Therefore, the resource fetch necessarily has lower priority than anything on the current page. Also, it's only natural that we don't really know what the next navigation would be. So, this hint is speculative by nature. --- class: nocenter ```html
``` ??? You can see from the syntax that there's not a whole lot to it. a prefetch relation, the resource's URL, and a crossorigin attribute when it's relevant. --- class: nocenter, wordwrap ```http Link:
; rel=prefetch ``` ??? And again the Link header form. That part is implemented only in Firefox at the moment. --- ## When? ??? So as I said, prefetch is something you want to use when you have pretty high certainty that a resource would be required in the next navigation. Maybe you're in a funnel and know that unless the user bails out, he has only one option. Maybe you know that all links from the current page lead to a page that needs a certain CSS, JS or font. So, in these cases or similar, you'd want to add a prefetch link, so that when the browser finished downloading the current page, and the user is reading it, the browser would prepare to download the required resources for the next navigation in the background. --- ## Cost ??? The cost of prefetching an unneeded resource is pretty high, but lower than preloading the wrong resource. Basically, the user will pay the bandwidth and battery costs of the extra download, but it shouldn't interfere with resources for the current page. OTOH, it might interfere with resource downloads for the next navigation since prefetch's navigation survives across navigations. --- background-image: url(resources/prerender.jpg) # Prerender ??? Which brings us to our final hint: Prerender. That's the ultimate hint, basically telling the browser "download this page in the background, with all its subresources and all, and have it ready when the user navigates to it" --- class: nocenter ```html
``` ??? Again, basically the prerender relation, and the URL. Like other hints, it includes a `pr` attribute to indicate probability. --- class: nocenter, wordwrap ```http Link:
; rel=prerender;pr=0.8 ``` ??? In theory, also in header form, but that's not currently supported anywhere --- ## When? ??? You would use prerender mainly in situations where you have a very high probability of where the user is going, so if you're in a signup form's step 1 out of 3, you may want to prerender step 2. Or if you're on page 2 of an article split across multiple pages to increase ad exposure, you may want to prerender page 3 to diminish the UX damage of the split. --- ## Visibility ??? That last use case may cause some of you to scratch their heads. If I'm prerendering these pages, I'm not sure that the user actually sees them. How can I then count page views for my ads? How can I know that the users bailed at step 1 of my signup form because it's really bad? That's one of the reasons that the page visibility API exists. It enables you to check if the current page is in fact visible and count ads, send analytics and all that jazz only when the user is actually seeing the page. It can also be used to prevent expensive animations and video playback from starting before the user actually sees the page. But if I'm not prerendering my pages, I shouldn't worry about visibility, right? --- ## Others already prerender for you! ??? Wrong! Google and others already have mechanisms in place where if you're the first results and there's a good likelihood that the user would pick you, they prerender your site, just in case. If you don't account for visibility there, your analytics team and ad billing folks are going to have a bad time. --- ## Dynamic adding of hints ??? What some of these sites do is to start prerendering the destination site if they see the user hover over the link or even if they detect mouse movement in the link's direction (indicating the user is likely to click it). That works for mouse, but not as much for touch. They then add the link tags dynamically, using JS, and the browsers should kick off a the page prerendering in a background tab. Now dynamic adding of hints works in Chrome, but doesn't in Firefox (but I hope it would be fixed soon). And, where it works, it can also work for any of the supported hints. --- ## Cost ??? Obviousely prerender has huge costs if you get it wrong. So this is something you should do only when you're really sure about the user's next steps, either because of scenario or because of dynamic mouse behavior. --- #Implementation | hint | Chromium | Firefox | IE/EDGE | Safari | | :-----------: |:--------:|:-------:|:-------:|:------:| | dns-prefetch | link+tag+dynamic|link+tag | tag | tag | | preconnect | link+tag+dynamic|link+tag | - | - | | preload | in progress | - | - | - | | prefetch | tag+dynamic | link+tag | tag >10 | - | | prerender | tag+dynamic | - | tag > 11 | - | ??? Implementation are underway in Firefox, for at least some of these hints. I'm implementing in Blink, and Microsoft are also excited. And large parts are already shipped in the various browsers. So let's go over each one of the hints. Dns-prefetch implemented in all browsers, IE9 got it wrong, Link headers implemented in Chrome, Firefox and Opera Prefetch is implemented in all modern browsers outside of Safari, in its link tag form. As I said, the Link header form is only implemented in Firefox at the moment. --- # Costs vs. benefits | hint | cost if wrong | benefit if right | | :-----------: |:-------------:| :----------------:| | dns-prefetch | very low | low | | preconnect | low | medium | | preload | high | high | | prefetch | mid-high | high (next nav) | | prerender | huge | huge (next nav) | --- class: top background-image: url(resources/suprise.jpg) # HTTP2 push ??? This is not strictly related to Resource Hints but I've heard people compare HTTP2 push to preload, so let's discuss it for a few minutes. --- ## What is HTTP2 push? ??? HTTP2 push is part of the HTTP2 protocol that enables us to send resources to the browsers before the browser actually requests them. That enables us to send down for example JS and CSS resources, because we know the browser will need them soon enough. So in some ways this is a mechanism that competes with Preload and Prefetch. Instead of telling the browser what it would need and letting it request it, we're just giving it what it needs. --- ## push > preload * Saves an RTT * Current browser support ??? So push is better than preload in some scenarios. One of its biggest advantages is that it saves an RTT, enabling us to send down content without waiting for the browser to get the hint and to act upon it. Otherwise, currently H2 push is supported among browsers that support H2 (which is mostly all of them). That's also a big deal. --- ## push < preload * 1st party resources * Ignores cache state * HTTPS only ??? In other areas, push is doing worse than preload. One major drawback is that you can only push resources that are covered by your current H2 connection's certificate. In other words, you can only push 1st party content. With preload, you can tell the browser that a 3rd party resources will be required, and the browser will act on it. The second is that when implementing H2 push, you have to maintain the cache state of the browser yourself. You have to remember what that user already downloaded and keep track of that over time, in order to know what to push and what not to push. When using Preload OTOH, the browser does all that hard work for you. The third is a practical (and hopefully temporary) limitation, but until all sites move to HTTPS, not everyone can deploy H2, and therefore, not everyone can use H2 push. --- # Client Hints ??? And finally, to sort out a common confusion: Resource Hints and Client Hints are two different things. A lot of people get them mixed up, probably because of the name similarities and the fact that a lot of the same people are involved in both. Resource Hints is everything we discussed up until now: ways for developers to tell the browsers what it would need. Client Hints is quite the opposite. It's a way for the browsers to tell the server side "this is my environment, please adapt the content to that". So Client Hints is about adding headers to requests, indicating the screen's resolution, the viewport width, the width of the requested image resource (when the browser has it), the user's preference in terms of data savings, and probably even more hints about the user's environment in the future. So both standards are complementary and are part of the same strategy: "Let's let the client-side and the server-side have a dialog, in order to get a better experience for our users" --- # Takeaways --- class: top background-image: url(resources/awesome1.jpg) # Goats are awesome! --- # Browsers need your help! ??? Browsers are awesome too, but need your help to be even awesomer --- # The tools are (almost) here --- background-image: url(resources/awesome.jpg) # Thank you! [@yoavweiss](https://twitter.com/yoavweiss) [http://yoavweiss.github.io/hints_velocity_nyc](http://yoavweiss.github.io/hints_velocity_nyc)