Network dependency tree in Lighthouse: read it and fix it

Files the browser only finds inside other files make your page wait for each of them in turn. How to read the tree and the preconnect tables, and which chains are worth fixing.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-10-29

What is the Network dependency tree in PageSpeed Insights?

The Network dependency tree in Lighthouse ("Avoid chaining critical requests" before Lighthouse 13) shows the files your page needs first and which file each one waited for: every file found inside another file adds a wait. Host your fonts on your own domain, link stylesheets in the HTML instead of @import, and preconnect only where a critical file comes from a domain you can't avoid.

How do you read the Network dependency tree?

Lighthouse 13 and PageSpeed Insights describe the notice as: "Avoid chaining critical requests by reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load". It replaced "Avoid chaining critical requests" (earlier "Minimize critical requests depth") and "Preconnect to required origins", which is why two preconnect tables sit under the tree.

The report below comes from the home page of a dental practice I built like a WordPress site: the child theme's stylesheet imports the parent's, which imports Google Fonts, and the icons come from Font Awesome on a CDN.

Lighthouse 13.6 report, Network dependency tree of a dental practice home page: Maximum critical path latency 4,176 ms; under the HTML, all.min.css on cdnjs.cloudflare.com with fa-solid-900.woff2 (155.18 KiB, 4,176 ms) in orange, and the child theme style.css, the parent style.css, the Google Fonts css2 stylesheet, each indented under the one before, and three woff2 files on fonts.gstatic.com side by side under the css2 stylesheet

Lighthouse 13.6 on mobile, DevTools throttling (Slow 4G, 4x CPU).

  • An indented row is a file the browser found only inside the file one level up, so it waited for that file. Rows at the same level download side by side, like the three font files under Google's css2 stylesheet.
  • The time and the KiB on a row: when that file had arrived, counted from the start of the page load, and its size.
  • Maximum critical path latency is the time of the slowest chain, in orange. On the dental page that is the 155 KiB icon font two levels deep: it arrives after the Google fonts four levels down.
  • LCP and Unscored: the notice has no weight in your Performance score, but its stylesheets hold up the first paint, and LCP can never come before it.

PageSpeed Insights records the load without throttling: its tree shows much shorter times than this screenshot.

Why does a chain of files delay the page?

On the dental practice home page from the report above, the visitor first looks at a white screen, then at text in a fallback font that changes when the web fonts arrive, with empty circles where the icons go.

The browser reads ahead through the HTML (the preload scanner) and requests every file it finds there at once. It finds a file named inside another file only once that other file has arrived: a font in a stylesheet, a stylesheet behind an @import, a script that a script adds. Each level adds a round trip to the server before the next request can start.

A file on another domain waits longer: the browser first sets up a connection (DNS, TCP and TLS), which on a phone network takes several times as long as a request on an open connection. On the dental page the Google fonts sit four levels under the HTML, behind two @imports, the last two on new domains (fonts.googleapis.com, fonts.gstatic.com). Render-blocking requests explains why a stylesheet holds the first paint.

How much faster is the page with a flat tree?

After the fix, the dental page paints a second earlier and shows its own fonts almost two seconds earlier. I measured three versions. Before has the two @imports. Links has the same files linked in the HTML, as WordPress prints them with wp_enqueue_style. After has the parent and child stylesheets in one file, the fonts on the site's own domain, each with a preload, and the icons as inline SVG.

BeforeLinksAfter
Levels421
Slowest
chain
4.183.251.21
First
paint
2.361.431.35
All
fonts
3.572.611.72
Icons4.193.241.35
LCP2.361.731.96

Times in seconds, on a throttled phone (Slow 4G, 4x slower CPU). Levels and slowest chain (Maximum critical path latency in the report) from Lighthouse 13.6 with the same throttling.

Phone screen two seconds into loading the dental practice home page before the fix: still completely white

Before

The same moment after the fix: the logo, the heading Your family dentist in Riverside and the text in their own fonts, the Book an appointment button with its calendar icon, the photo of the treatment room and a treatment card with its icon

After

Two seconds into the load on the same throttled phone.

Links already gives most of the first-paint gain, because the two @import levels are gone, while the fonts and icons still wait for their own domains. LCP, the hero photo, comes 0.4 s earlier after the fix. Links has the earliest LCP because there the photo has the bandwidth almost to itself; in After the preloaded fonts share it.

To see whether real visitors wait for the chain, split LCP on mobile into its parts in CoreDash: late stylesheets and fonts show up as a long render delay, the wait after the LCP image (or, for text, the HTML) has arrived.

What do Preconnected origins and Preconnect candidates mean?

A preconnect (<link rel="preconnect">) opens the connection to a domain before the browser needs a file from there. Preconnected origins lists the domains your page preconnects to; Preconnect candidates lists domains whose file still had to set up a connection, with the time a preconnect could save (Est LCP savings).

Preconnected origins with five domains: fonts.googleapis.com, fonts.gstatic.com, cdnjs.cloudflare.com with the warning Unused preconnect. Check that the crossorigin attribute is used properly, maps.googleapis.com and www.youtube.com each with Unused preconnect. Only use preconnect for origins that the page is likely to request; Preconnect candidates: cdnjs.cloudflare.com, Est LCP savings 320 ms

The same dental page with a large, lazy-loaded hero photo, Lighthouse 13.6 with DevTools throttling.

  • "Unused preconnect. Only use preconnect for origins that the page is likely to request." The page loaded nothing from that domain, here maps.googleapis.com and www.youtube.com. Remove it once you have checked that no other template, such as the checkout, uses it.
  • "Unused preconnect. Check that the crossorigin attribute is used properly." A font needs a connection opened with crossorigin, a stylesheet or script one without it. cdnjs.cloudflare.com sends the dental page Font Awesome's stylesheet and its font, so the stylesheet used the preconnect and the font opened a second connection. Host the font yourself; only when you can't, add a second preconnect to that domain with crossorigin.
  • "More than 4 preconnect connections were found." Each preconnect sets up a connection next to the files the page needs first. Host what you can on your own domain, and keep a preconnect only for a critical file on a domain you can't avoid.
  • A domain under Preconnect candidates. Move that file to your own domain; when you can't and the file is critical, preconnect to it.

If you can't host Google Fonts yourself yet, link its stylesheet in the HTML and preconnect to fonts.gstatic.com, with crossorigin; the browser connects to fonts.googleapis.com as soon as it reads the link. That still leaves a chain.

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

Plugin preconnect settings don't always get crossorigin right: LiteSpeed Cache's DNS Preconnect prints it on no domain, and Perfmatters only where you tick its box. Check your page source: only font domains need it.

Why is the tree still red after the fix?

Lighthouse turns the notice red as soon as the HTML loads one stylesheet, so a page without a chain stays red. That is the one red you can keep: the HTML and the files it names itself. Every row indented under another file is a chain to fix, also when it looks harmless:

  • Fonts under your own stylesheet. They come from your domain but still wait for the stylesheet: preload the fonts the first screen shows.
  • Files a service adds. Cloudflare's Email Obfuscation (email-decode.min.js) and Web Analytics beacon, or guest.vary.php from LiteSpeed Cache's Guest Mode. Switch the feature off.

A known PageSpeed Insights bug sometimes puts other sites' requests in the tree: run the test again.

Network dependency tree after the fix, still red: Maximum critical path latency 1,211 ms; only the HTML and acme-smile/style.css, one level, both on www.acme-dental.test; No origins were preconnected; No additional origins are good candidates for preconnecting

The dental page with every chain gone, Lighthouse 13.6 with DevTools throttling.

How do you fix the Network dependency tree?

Start at the slowest chain, the one Lighthouse marks in orange, at its deepest row. For every file in it, ask: can this file be named in the HTML, or come from my own domain?

Google Fonts and other web fonts

Without code, switch on the setting that hosts Google Fonts on your domain: Self-host Google Fonts in WP Rocket or FlyingPress, Local Google Fonts in Perfmatters, Load Google Fonts Locally in Elementor (Settings, Performance), or the OMGF plugin. WP Rocket's setting misses a Google Fonts @import in the theme's stylesheet, so if fonts.googleapis.com is still in the tree afterwards, remove the @import (next section). An option that loads Google Fonts "asynchronously" with a font loader script, as in LiteSpeed Cache and Autoptimize, adds a level to the chain: host the fonts yourself instead.

By hand, put the @font-face rules in your own stylesheet, with the woff2 files in a fonts folder next to it:

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/poppins-v24-latin-700.woff2") format("woff2");
}

A font can only be named inside a stylesheet, so you can't flatten this chain. Preload every font the first screen shows, so it starts with the HTML:

<link rel="preload" href="/wp-content/themes/acme-smile/fonts/poppins-v24-latin-700.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/wp-content/themes/acme-smile/fonts/poppins-v24-latin-600.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/wp-content/themes/acme-smile/fonts/open-sans-v44-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/wp-content/themes/acme-smile/style.css?ver=2.5.0">

The preload needs crossorigin, also on your own domain, or the font downloads twice. Each preload shares the bandwidth with the stylesheet, so fonts lower on the page get none.

On Next.js, next/font already hosts and preloads the fonts: replace a hand-written @import url(https://fonts.googleapis.com/...) in a CSS file with next/font.

WordPress themes with @import

In a child theme, the @import of the parent stylesheet at the top of style.css adds a level: remove it and load the parent with wp_enqueue_style, as shown on Render-blocking requests. A Google Fonts @import you delete from the parent theme comes back with its next update: look for a font setting in the theme, or ask its developer to enqueue the fonts.

Icon fonts

Font Awesome and Elementor's icons load a stylesheet that loads a font holding every icon. In Elementor, switch on Inline Font Icons: it replaces the stylesheet and the font with inline SVG. Elsewhere, replace the icons the page uses with inline SVG, or host Font Awesome on your own domain and preload its font.

Shopify

Pick your fonts in the theme editor (Theme settings, Typography) instead of pasting Google Fonts code into theme.liquid: the theme then serves them from your store's own domain, and Dawn and the themes built on it preload them. App embeds that add a stylesheet show up in the tree: switch them off one at a time and run Lighthouse again. Shopify sends its own preconnects in the page's headers, to its CDN and to the first three other domains your blocking files come from, and Lighthouse counts them in its limit of four. Remove an old cdn.shopify.com preconnect from theme.liquid and add your own only for a domain your tree still needs; each app embed you switch off can remove one of Shopify's. If "More than 4 preconnect connections" stays when only Shopify's own are left, leave it.

Files a script adds, and services you can't change

A stylesheet that a script adds waits for that script: if the first screen needs it, put its link in the HTML; if not, defer the script. A consent tool that has to run first stays blocking; when it loads files from a second domain you can't avoid, preconnect to that domain. When your server takes time to build the HTML, 103 Early Hints sends the preconnect and the font preload before the HTML itself.

How do you check that the tree got shorter?

Run Lighthouse again and compare the levels and the slowest chain first, since Maximum critical path latency moves a little from run to run. The Initiator column in the Network panel of Chrome DevTools shows which file requested each file. For real visitors, watch FCP and LCP on mobile in CoreDash from the next visits on; Search Console takes 28 days.

Does your longest chain come from a theme or page builder you can't edit, or an app that loads its own fonts? In a Core Web Vitals power hour we go through your tree together, file by file, and decide which files move to your own domain, which go into the HTML and which domain still needs a preconnect.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

 

Real time data. Not 28 day averages.

CoreDash segments every metric by route, device, browser, and connection type.

Explore CoreDash
Network dependency tree in Lighthouse: read it and fix it Core Web Vitals Network dependency tree in Lighthouse: read it and fix it