Render-Blocking Resources: How to Find and Fix Them
A site can have a fast server, a lean HTML document, and a CDN in every region - and still show a blank white screen for two full seconds, because one stylesheet or script in the <head> is making the browser wait before it draws anything at all. That's render blocking, and it's one of the few performance problems that's genuinely fixable in an afternoon.
What "Render-Blocking" Actually Means
A browser parses HTML top to bottom, building the page as it goes. When it hits a <link rel="stylesheet">, it stops and won't paint anything until that CSS is downloaded and parsed - because it needs the full stylesheet to know how to lay out the elements it's about to draw. A synchronous <script> tag is worse: it halts HTML parsing entirely until the script downloads, executes, and hands control back, since that script could theoretically use document.write() to change everything below it.
Neither of these is inherently wrong - a page genuinely does need its critical CSS before it can paint correctly. The problem is when render-blocking resources are large, slow to fetch, or simply unnecessary for what's visible in the first viewport.
How to Find Them
Lighthouse's "Eliminate render-blocking resources" audit lists every stylesheet and synchronous script loaded before first paint, along with the milliseconds each one is estimated to cost. That's the fastest starting point. For a deeper look, open your browser's network panel, filter to CSS and JS, and check which requests finish before the first paint marker in the waterfall - anything upstream of that line is a candidate.
The Coverage panel in Chrome DevTools (Cmd/Ctrl+Shift+P → "Show Coverage") is worth running too - it shows what percentage of each loaded CSS and JS file was actually used on the page. A render-blocking stylesheet that's 90% unused bytes is a much higher-value fix than one that's small and mostly applied.
Fixing CSS Blockers
- Inline critical CSS. Extract just the styles needed for above-the-fold content and inline them in a
<style>tag in the <head>, then load the full stylesheet asynchronously. This trades a render-blocking network request for zero-latency inline styles for what actually matters first. - Split by media query. A stylesheet loaded with
media="print"or a specific breakpoint doesn't block rendering on devices where that media query doesn't apply - the browser still downloads it, but at a low priority that doesn't hold up paint. - Preload + swap. Load a non-critical stylesheet with
rel="preload"and anonloadhandler that flips it torel="stylesheet"once loaded - fetches early without blocking the initial render. - Cut what isn't used.A framework's default CSS bundle often ships far more than a given page needs. Purging unused rules shrinks the file the browser has to parse before it can paint, independent of any loading strategy.
Fixing JS Blockers
- Add
defer.The script downloads in parallel with HTML parsing and runs only after parsing finishes, in document order. The correct default for almost any script that isn't needed before first paint. - Add
asyncfor independent scripts. Downloads in parallel and runs as soon as it's ready, out of order relative to other scripts - fine for something like an analytics snippet that doesn't depend on anything else on the page. - Move scripts to the end of the bodyas a fallback for older code that can't use
defer- it means the parser has already built the DOM above it before the script can block anything. - Code-split.A single large JS bundle blocks on all of it even if only a fraction is needed for the first render. Splitting by route or by above/below-the-fold usage means the browser only has to wait on the part that's actually blocking.
When It's Not Worth Fixing
A few hundred bytes of inlined critical CSS blocking render for a few milliseconds isn't a problem - it's the mechanism working as intended. The audit is flagging a cost, not issuing a verdict; a resource that's small and fast to fetch can stay exactly where it is. Spend the effort on the ones actually showing up as hundreds of milliseconds in the waterfall, not on chasing a zero-item list in Lighthouse for its own sake.
Our Core Web Vitals test flags render-blocking resources alongside the rest of your Largest Contentful Paint breakdown, so you can see exactly how much of your LCP time is genuinely attributable to a blocking file versus everything else in the chain. See our Core Web Vitals guide for the rest of that chain.
See your full audit score right now
Run a free instant audit and get an AI-prioritized fix list for performance, SEO, and accessibility.
Run a free audit