Critical request chains: the blocking head resources counted
Blocking stylesheets and scripts in head, plus every inline CSS @import a browser fetches. squirrel counts each as a link in the chain before first paint.
What a critical request chain is
A critical request chain is a sequence of requests that must complete in order before the page can render. The HTML arrives, the parser finds a stylesheet, the stylesheet arrives, and inside it an @import names another file that has to be fetched before anything can paint. Each link costs a full round trip, and round trips are the part of the load that a faster connection does not fix.
Depth matters more than count. Four independent blocking resources fetch in parallel; four chained ones fetch one after another. @import is the worst offender because the browser cannot even discover the second URL until the first file is parsed.
What squirrel checks
The rule runs on every crawled page and emits one check:
critical-request-chains, at info status with the messageNo <head> element foundwhen the page has no head.critical-request-chains, warning with the messageN critical request chain(s) foundand listing up to ten members as items. A stylesheet readsCSS: <href>, a scriptJS: <src>, and the page’s@imports are folded into one entry readingN CSS @import(s) found (each adds a network round-trip). The details record the blocking CSS count, the blocking JS count, the import count, the number of preloads and any overflow.critical-request-chains, passing with the messageNo critical request chains detected, with the preload count in the details.
Three things are counted, all from <head> except the imports. A blocking stylesheet is a link[rel="stylesheet"] that is neither media="print" nor disabled. A blocking script is a classic script[src] with a non-empty src and neither async nor defer. A type="module" script is deferred by default, and a classic script with nomodule or a script whose type is not JavaScript never runs in a modern browser, so none of those count. In every <style> element on the page, each @import a browser would fetch is counted, and the total contributes exactly one entry to the list rather than one entry each. Comments, strings and url() are read as single tokens, the way a browser reads them, so an @import inside a comment (/* @import url(a.css); */), a string or a url() is not an import. Neither is one that comes after any other rule, or one inside a block such as @media: a browser ignores those and never fetches them. Only @charset and, before the first import, @layer statements may come before an import. An @import with no URL is dropped too.
The count is a count of blocking resources rather than a measured chain depth, since a static read of the HTML cannot see what a stylesheet fetches once parsed.
Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.
How to fix it
<style>/* styles the first screen needs */</style>
<link rel="stylesheet" href="/rest.css" media="print" onload="this.media='all'" />
<script src="/app.js" defer></script>Replace every @import with a <link>, which lets the browser fetch in parallel instead of serially. Add defer to scripts and inline the styles the first screen needs.
The limit worth knowing is type="module", which this rule treats as non-blocking. A module script is deferred by default, so that is correct for parsing, but its dependency graph is itself a chain the rule cannot see, which makes the count a floor.
| Rule ID | perf/critical-request-chains |
| Category | Performance |
| Scope | Per-page |
| Severity | warning |
| Weight | 5/10 |
Enable / disable
Disable this rule
[rules]
disable = ["perf/critical-request-chains"]Disable all Performance rules
[rules]
disable = ["perf/*"]Enable only this rule
[rules]
enable = ["perf/critical-request-chains"]enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.
Related rules
- perf/render-blocking: the same resources counted with a different threshold.
- perf/font-delivery: the third-party font stylesheet that often heads a chain.
- perf/preconnect: connection setup for the origins in that chain.
- perf/lcp-hints: the paint these requests are delaying.
Performance findings ship in every audit next to the SEO, security and agent experience rules. See Core Web Vitals for how an agent works through a report.
References
Check your site
Run squirrel audit https://example.com and open the Performance section of the report. Every blocking resource is listed with its URL. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.