I was chatting with DebugBear’s Matt Zeunert and, in the process, he casually mentioned this thing called Tight Mode when describing how browsers fetch and prioritize resources. I wanted to nod along like I knew what he was talking about but ultimately had to ask: What the heck is “Tight” mode?
What I got back were two artifacts, one of them being the following video of Akamai web performance expert Robin Marx speaking at We Love Speed in France a few weeks ago:
The other artifact is a Google document originally published by Patrick Meenan in 2015 but updated somewhat recently in November 2023. Patrick’s blog has been inactive since 2014, so I’ll simply drop a link to the Google document for you to review.
That’s all I have and what I can find on the web about this thing called Tight Mode that appears to have so much influence on the way the web works. Robin acknowledged the lack of information about it in his presentation, and the amount of first-person research in his talk is noteworthy and worth calling out because it attempts to describe and illustrate how different browsers fetch different resources with different prioritization. Given the dearth of material on the topic, I decided to share what I was able to take away from Robin’s research and Patrick’s updated article.
It’s The First of Two Phases
The fact that Patrick’s original publication date falls in 2015 makes it no surprise that we’re talking about something roughly 10 years old at this point. The 2023 update to the publication is already fairly old in “web years,” yet Tight Mode is still nowhere when I try looking it up.
So, how do we define Tight Mode? This is how Patrick explains it:
“Chrome loads resources in 2 phases. “Tight mode” is the initial phase and constraints [sic] loading lower-priority resources until the body is attached to the document (essentially, after all blocking scripts in the head have been executed).”
— Patrick Meenan
OK, so we have this two-part process that Chrome uses to fetch resources from the network and the first part is focused on anything that isn’t a “lower-priority resource.” We have ways of telling browsers which resources we think are low priority in the form of the Fetch Priority API and lazy-loading techniques that asynchronously load resources when they enter the viewport on scroll — all of which Robin covers in his presentation. But Tight Mode has its own way of determining what resources to load first.
Tight Mode discriminates resources, taking anything and everything marked as High and Medium priority. Everything else is constrained and left on the outside, looking in until the body is firmly attached to the document, signaling that blocking scripts have been executed. It’s at that point that resources marked with Low priority are allowed in the door during the second phase of loading.
There’s a big caveat to that, but we’ll get there. The important thing to note is that…
Chrome And Safari Enforce Tight Mode
Yes, both Chrome and Safari have some working form of Tight Mode running in the background. That last image illustrates Chrome’s Tight Mode. Let’s look at Safari’s next and compare the two.
Look at that! Safari discriminates High-priority resources in its initial fetch, just like Chrome, but we get wildly different loading behavior between the two browsers. Notice how Safari appears to exclude the first five PNG images marked with Medium priority where Chrome allows them. In other words, Safari makes all Medium- and Low-priority resources wait in line until all High-priority items are done loading, even though we’re working with the exact same HTML. You might say that Safari’s behavior makes the most sense, as you can see in that last image that Chrome seemingly excludes some High-priority resources out of Tight Mode. There’s clearly some tomfoolery happening there that we’ll get to.
Where’s Firefox in all this? It doesn’t take any extra tightening measures when evaluating the priority of the resources on a page. We might consider this the “classic” waterfall approach to fetching and loading resources.
Chrome And Safari Trigger Tight Mode Differently
Robin makes this clear as day in his talk. Chrome and Safari are both Tight Mode proponents, yet trigger it under differing circumstances that we can outline like this:
Chrome | Safari | |
---|---|---|
Tight Mode triggered | While blocking JS in the <head> is busy. |
While blocking JS or CSS anywhere is busy. |
Notice that Chrome only looks at the document <head>
when prioritizing resources, and only when it involves JavaScript. Safari, meanwhile, also looks at JavaScript, but CSS as well, and anywhere those things might be located in the document — regardless of whether it’s in the <head>
or <body>
. That helps explain why Chrome excludes images marked as High priority in Figure 2 from its Tight Mode implementation — it only cares about JavaScript in this context.
So, even if Chrome encounters a script file with fetchpriority="high"
in the document body, the file is not considered a “High” priority and it will be loaded after the rest of the items. Safari, meanwhile, honors fetchpriority
anywhere in the document. This helps explain why Chrome leaves two scripts on the table, so to speak, in Figure 2, while Safari appears to load them during Tight Mode.
That’s not to say Safari isn’t doing anything weird in its process. Given the following markup:
<head>
<!-- two high-priority scripts -->
<script src="https://smashingmagazine.com/2025/01/tight-mode-why-browsers-produce-different-performance-results/script-1.js"></script>
<script src="https://smashingmagazine.com/2025/01/tight-mode-why-browsers-produce-different-performance-results/script-1.js"></script>
<!-- two low-priority scripts -->
<script src="script-3.js" defer></script>
<script src="script-4.js" defer></script>
</head>
<body>
<!-- five low-priority scripts -->
<img src="image-1.jpg">
<img src="image-2.jpg">
<img src="image-3.jpg">
<img src="image-4.jpg">
<img src="image-5.jpg">
</body>
…you might expect that Safari would delay the two Low-priority scripts in the <head>
until the five images in the <body>
are downloaded. But that’s not the case. Instead, Safari loads those two scripts during its version of Tight Mode.
Chrome And Safari Exceptions
I mentioned earlier that Low-priority resources are loaded in during the second phase of loading after Tight Mode has been completed. But I also mentioned that there’s a big caveat to that behavior. Let’s touch on that now.
According to Patrick’s article, we know that Tight Mode is “the initial phase and constraints loading lower-priority resources until the body is attached to the document (essentially, after all blocking scripts in the head have been executed).” But there’s a second part to that definition that I left out:
“In tight mode, low-priority resources are only loaded if there are less than two in-flight requests at the time that they are discovered.”
A-ha! So, there is a way for low-priority resources to load in Tight Mode. It’s when there are less than two “in-flight” requests happening when they’re detected.
Wait, what does “in-flight” even mean?
That’s what’s meant by less than two High- or Medium-priority items being requested. Robin demonstrates this by comparing Chrome to Safari under the same conditions, where there are only two High-priority scripts and ten regular images in the mix:
<head>
<!-- two high-priority scripts -->
<script src="https://smashingmagazine.com/2025/01/tight-mode-why-browsers-produce-different-performance-results/script-1.js"></script>
<script src="https://smashingmagazine.com/2025/01/tight-mode-why-browsers-produce-different-performance-results/script-1.js"></script>
</head>
<body>
<!-- ten low-priority images -->
<img src="image-1.jpg">
<img src="image-2.jpg">
<img src="image-3.jpg">
<img src="image-4.jpg">
<img src="image-5.jpg">
<!-- rest of images -->
<img src="image-10.jpg">
</body>
Let’s look at what Safari does first because it’s the most straightforward approach:
Nothing tricky about that, right? The two High-priority scripts are downloaded first and the 10 images flow in right after. Now let’s look at Chrome:
We have the two High-priority scripts loaded first, as expected. But then Chrome decides to let in the first five images with Medium priority, then excludes the last five images with Low priority. What. The. Heck.
The reason is a noble one: Chrome wants to load the first five images because, presumably, the Largest Contentful Paint (LCP) is often going to be one of those images and Chrome is hedging bets that the web will be faster overall if it automatically handles some of that logic. Again, it’s a noble line of reasoning, even if it isn’t going to be 100% accurate. It does muddy the waters, though, and makes understanding Tight Mode a lot harder when we see Medium- and Low-priority items treated as High-priority citizens.
Even muddier is that Chrome appears to only accept up to two Medium-priority resources in this discriminatory process. The rest are marked with Low priority.
That’s what we mean by “less than two in-flight requests.” If Chrome sees that only one or two items are entering Tight Mode, then it automatically prioritizes up to the first five non-critical images as an LCP optimization effort.
Truth be told, Safari does something similar, but in a different context. Instead of accepting Low-priority items when there are less than two in-flight requests, Safari accepts both Medium and Low priority in Tight Mode and from anywhere in the document regardless of whether they are located in the <head>
or not. The exception is any asynchronous or deferred script because, as we saw earlier, those get loaded right away anyway.
How To Manipulate Tight Mode
This might make for a great follow-up article, but this is where I’ll refer you directly to Robin’s video because his first-person research is worth consuming directly. But here’s the gist:
- We have these high-level features that can help influence priority, including resource hints (i.e.,
preload
andpreconnect
), the Fetch Priority API, and lazy-loading techniques. - We can indicate
fetchpriority=
"
high
"
andfetchpriority="low"
on items.
<img src="https://smashingmagazine.com/2025/01/tight-mode-why-browsers-produce-different-performance-results/lcp-image.jpg" fetchpriority="high">
<link rel="preload" href="defer.js" as="script" fetchpriority="low"
- Using
fetchpriority="high"
is one way we can get items lower in the source included in Tight Mode. Usingfetchpriority="low
is one way we can get items higher in the source excluded from Tight Mode. - For Chrome, this works on images, asynchronous/deferred scripts, and scripts located at the bottom of the
<body>
. - For Safari, this only works on images.
Again, watch Robin’s talk for the full story starting around the 28:32 marker.
That’s Tight… Mode
It’s bonkers to me that there is so little information about Tight Mode floating around the web. I would expect something like this to be well-documented somewhere, certainly over at Chrome Developers or somewhere similar, but all we have is a lightweight Google Doc and a thorough presentation to paint a picture of how two of the three major browsers fetch and prioritize resources. Let me know if you have additional information that you’ve either published or found — I’d love to include them in the discussion.
(yk)