Fixing Render-Blocking Resources
When a speed report says "eliminate render-blocking resources", it means the browser has been told to wait for certain files before it draws anything at all. Visitors see a blank screen, and First Contentful Paint and Largest Contentful Paint both slip.
The list usually mixes stylesheets, scripts, web font CSS and third-party files, each with a different owner and a different fix. This guide is the triage stage: it shows you how to see every blocker, work out what each one really costs, remove the cheap delays, and send each file to the right detailed fix.
- What counts as render-blocking, and what does not
- Why a few small files can cost seconds
- Triage: reading the report and the waterfall
- Choosing the fix for each blocking file
- The fix, step by step
- Resource hints and early hints, used carefully
- Platform notes
- Edge cases and trade-offs
- Stopping blockers from creeping back
- Questions and answers
What counts as render-blocking, and what does not
Browsers try to show something as soon as they can, but they will not paint until they have the styles that apply to the page and have run any script that might change the page before it is shown. Anything that the browser must fetch and process before that first paint is on the critical rendering path. Files on that path are what speed tools call render-blocking.
The label gets applied loosely, so it helps to know exactly which kinds of file block and which only look as if they do.
| Resource | Blocks first paint? | Notes |
|---|---|---|
<link rel="stylesheet"> in the head with no media or a matching
one | Yes | The browser waits so it does not show unstyled content. |
Stylesheet with a non-matching media value, such as print | No | Still downloaded, at low priority. |
A file pulled in by @import inside a blocking stylesheet | Yes | Found late, so it adds a second round trip. |
Classic <script src> in the head without defer or async | Yes | Stops the HTML parser until downloaded and run. |
Script with defer, async or type="module" | No | Downloads in parallel and runs later. |
| Web font CSS from a font service | Yes | It is a stylesheet, often on another host. |
| The font files themselves | Not the paint | They can hide text for a while, which is a separate problem. |
| Images, video, iframes | No | They slow LCP and use bandwidth but do not stop the first paint. |
Inline <style> and inline <script> | No extra request | They still cost parsing time, and a large inline script still runs before paint. |
Chromium-based browsers also support an explicit blocking="render" attribute, which lets
a page deliberately make a script or style block. You will rarely want it, but if you see it in a template,
it is doing exactly what it says.
Why a few small files can cost seconds
People are often surprised that a 10 KB stylesheet appears near the top of the report. File size is only one part of the cost. Before a blocking file can be used, the browser may need to:
- Look up the host name if the file is on a domain the page has not used yet.
- Open a connection, which on HTTPS means a TCP handshake and a TLS negotiation, or a QUIC handshake on HTTP/3.
- Wait for the server to respond, which for a third-party service is outside your control.
- Download the bytes, competing for bandwidth with every other file in flight.
- Parse and apply it, or in the case of a script, compile and run it on the main thread.
On a fast office connection all of that happens in a blink. On a phone with a weak mobile signal, each round trip can be long, and the steps for a new host happen one after another. That is why a single font stylesheet on a separate domain can hold the first paint back further than a larger stylesheet on your own domain that reuses the existing connection.
Chains multiply the delay
The worst case is a chain: a blocking file that, once loaded, asks for another blocking file. A stylesheet that imports a second stylesheet, which then imports a font service's CSS, makes the browser wait for three sequential trips before it can paint. Lighthouse used to show these as "critical request chains", and newer versions fold the same idea into its network dependency insight. The analyzer's waterfall shows them as bars that start only when the previous bar finishes.
Scripts add processing time
A blocking script is expensive twice over. The HTML parser stops while it downloads, and then the main thread is busy while it runs. A large library in the head can therefore cost more after it arrives than it took to download, especially on mid-range phones.
Triage: reading the report and the waterfall
Start by running the Page Speed Analyzer on each main template. It measures the page with real-user and lab data and names the requests and files holding it back, and its network waterfall shows the order in which they load. Look for the requests that finish before the first paint marker; those are your candidates.
Cross-check with Lighthouse
PageSpeed Insights and Lighthouse list render-blocking files with a transfer size and an estimated saving. Recent versions show this as a render-blocking requests insight; older versions call it "Eliminate render-blocking resources". The savings figure is modelled, so use it to rank files, not as a promise.
Confirm in DevTools
- Open the Performance panel, turn on network and CPU throttling, and record a reload.
- In the network track, select each early request. The summary shows whether Chrome treated it as render-blocking.
- Find the FCP marker on the timings track. Everything blocking that ends just before it is holding up the paint.
To get a quick list of everything that finished before first paint, you can run this in the Console after a fresh, uncached load:
const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime;
performance.getEntriesByType('resource')
.filter(r => r.responseEnd < fcp && /css|script|link/.test(r.initiatorType))
.map(r => ({ file: r.name.split('?')[0], host: new URL(r.name).host,
ms: Math.round(r.responseEnd - r.startTime) }));
This lists requests that finished before the first paint. Not every one of them blocked it, but every blocker will be in the list, and the host column shows at a glance which ones need a new connection.
Use a filmstrip
A filmstrip from WebPageTest, or the screenshots strip in the DevTools Performance panel, shows the moment something first appears on screen. That moment, often called start render, is what visitors feel. Keep the filmstrip; you will compare against it at the end.
Choosing the fix for each blocking file
Once you have the list, every blocking file falls into one of a few groups. The right fix depends on the group, not on how large the file is.
| What the file is | First question | Usual fix | Detailed guide |
|---|---|---|---|
| Your main stylesheet | How much of it does the first screen need? | Inline the critical rules, load the rest without blocking | Render-blocking CSS |
| A plugin, app or widget stylesheet | Is the feature on this page at all? | Load it only on pages that use it | Render-blocking CSS |
| Font service CSS | Could the fonts be served from your own domain? | Self-host, or preconnect and trim the families and weights requested | This page, steps 4 and 5 |
| Your own script in the head | Does anything need it before the page is shown? | Add
defer, or move it out of the head | Render-blocking JavaScript |
| A third-party script in the head | Who asked for it, and is it still used? | Remove
it, or load it with async or after load | Third-party scripts |
| A very large stylesheet or bundle | Why is it so large? | Split, purge unused rules or code | Heavy CSS, JS bundles |
This guide handles the parts that apply across all of them: removing what is not needed, cutting connection
time and breaking chains. The two linked guides for CSS and JavaScript cover the file-level techniques,
such as generating critical CSS or choosing between defer and async, in full.
The fix, step by step
Step 1
Record a baseline with a filmstrip
Pick your busiest templates, such as the home page, a service or product page and an article, and test each one on a throttled mobile profile with the cache disabled. Save the analyzer result, the Lighthouse list of blocking files and a filmstrip showing when content first appears. Write down the start render time and First Contentful Paint for each template. Without this record you cannot tell later whether a change helped, made no difference or quietly made the page worse.
Step 2
List each blocker with its type, host and owner
Build a simple table with one row per blocking file. Note whether it is a stylesheet, a script or font CSS, which host it comes from, its size and roughly how long it took. Then add an owner column: the theme, a named plugin or app, your developer, a marketing tag or a font service. The owner column matters most, because it tells you who can change the file and who must agree before anything is removed.
Step 3
Delete blockers that nothing on the page uses
Go through the table and ask of each file whether the page would look or behave differently without it. Old slider libraries, icon sets for a single icon, a second copy of jQuery, a widget from a trial that ended and a stylesheet for a form that only appears on the contact page are common finds. Remove them from the templates, or restrict them to the pages that genuinely use them. Removing a blocker is always faster than optimising it.
Step 4
Cut the connection cost for other hosts
For each blocking file on another host, decide whether it can move to your own domain. Font
files and small libraries usually can, and self-hosting removes a lookup, a handshake and a separate
server from the critical path. Where a file must stay on its host, add <link rel="preconnect">
for that origin near the top of the head, using the crossorigin attribute for fonts. Keep
preconnects to the two or three hosts that matter, because each one opens a real connection.
Step 5
Break request chains so blockers are found early
Look for any blocking file that only starts downloading after another one finishes.
Replace @import rules with direct <link> tags or combine the files at
build time. If a font service stylesheet is loaded from inside your main stylesheet, move its link into
the HTML head. If a script in the head injects another blocking script, reference the second file directly.
The aim is for every file the first paint needs to be visible in the HTML itself.
Step 6
Choose the delivery fix for each remaining file
What is left should be files the page genuinely needs. For your main stylesheet,
inline the small set of rules the first screen uses and load the full file without blocking. For scripts,
add defer unless something truly must run before the page is shown. For third-party tags,
load them asynchronously or after the page has rendered. Make one change at a time on a staging copy,
and check the page still looks and works right after each change.
Step 7
Retest and compare start render, not just the score
Run the same tests with the same settings you used for the baseline and put the filmstrips side by side. The number that matters most here is when content first appears on screen, followed by First Contentful Paint and Largest Contentful Paint. The Lighthouse score can move for unrelated reasons, so do not judge the work on it alone. After the change has been live for a few weeks, check real-user data to confirm visitors see the same improvement.
Resource hints and early hints, used carefully
Some blocking files cannot be avoided: most sites need at least a small stylesheet before the first paint. For those, the goal is to start the download as early as possible.
preconnect
preconnect tells the browser to open a connection to another host before it finds a file
there. It helps when a blocking file must stay on a separate domain. It wastes effort if the host is
not used early, and browsers close unused connections after a short time.
<link rel="preconnect" href="https://fonts.example-cdn.com" crossorigin>
preload
preload fetches a specific file early at high priority. It is useful for a blocking file
the browser would otherwise find late, such as a font referenced from CSS or a stylesheet requested by
a script. It does not make a blocking file non-blocking, and preloading several large files can push
back the ones that matter. Treat each preload as a decision you can justify.
103 Early Hints
Some servers and CDNs can send an informational 103 Early Hints response while the page
itself is still being generated. It can carry preconnect and preload links,
so the browser starts on your critical stylesheet before the HTML arrives. Support is strongest in Chromium-based
browsers, and it helps most when the server takes a while to build pages. Check your host or CDN documentation
for whether it is available and how to switch it on.
Platform notes
WordPress
Blockers usually come from the theme and from plugins that enqueue their styles and scripts on every page. Optimisation plugins can defer scripts, inline critical CSS and combine files, but switch features on one at a time and check menus, forms and sliders after each. Asset manager plugins, or a few lines in your child theme, can stop a plugin loading its files on pages where it is not used. The WordPress speed guide covers this in more depth.
Shopify
You cannot edit the scripts Shopify injects through content_for_header, but you control
the theme's own tags, and many blockers are app code left in theme.liquid. Duplicate the
theme before editing. The Shopify
speed guide covers app code and theme weight.
Custom and static sites
You have full control, so the fixes are direct edits to the head template. Build tools can generate critical CSS and add hashes to file names so full stylesheets can be cached for a long time.
Frameworks
Next.js, Nuxt, Astro and similar frameworks generally load their own scripts without blocking and can inline critical CSS. The blockers that remain are usually global stylesheets, font imports and third-party scripts added by hand to the root layout. The Next.js speed guide covers framework-specific options.
Edge cases and trade-offs
- Some blocking is correct. A small inline or linked critical stylesheet should block, because showing unstyled content and then snapping into place is worse. The goal is a short critical path, not zero blockers.
- Consent banners and privacy tools. A consent script may need to run before tracking fires. That does not mean it has to block painting; many consent tools can load asynchronously as long as tags wait for the consent signal. Check the vendor's guidance and test that consent is still respected.
- A/B testing and page-hiding snippets. Some testing tools deliberately hide the page until their script has run. That is a render-blocking cost by design, and it is covered in the render-blocking JavaScript guide.
- Font swapping. Once font CSS no longer blocks, text may appear first in a fallback
font and then change. Use
font-display: swaporoptionaland a fallback with similar metrics to keep the change small, and check it does not cause layout shift. - Single-page apps. If the HTML is an empty shell and everything is drawn by JavaScript, removing blockers helps less than rendering the first screen on the server. That is a bigger change, but it is often the real fix.
- Cached repeat visits. Returning visitors may never see the delay, so do not rely on your own browsing to judge the problem.
Stopping blockers from creeping back
Render-blocking files return whenever someone installs a plugin or app, pastes a tag into the head, or a theme update changes how assets load. A few habits keep the critical path short:
- Keep the owner table. The list from step 2 becomes your record. Add to it whenever a file is added to the head, with the name of whoever asked for it.
- Agree a head budget. For example: one blocking stylesheet from your own domain, no blocking scripts, and no blocking files from other hosts. Anything that breaks the budget needs a reason.
- Test after every install. Run the analyzer on the affected templates whenever a plugin, app or tag is added, and compare against your baseline.
- Review quarterly. Walk through the head of each main template every few months and remove what is no longer needed.
For more background on how speed fits together, the Page Speed Analyzer guide explains the reports, and the LCP guide shows how render-blocking time feeds into the main content element's load time.
1. What does eliminate render-blocking resources actually mean?
It means the page links files, usually stylesheets and scripts in the head, that the browser must download and process before it can draw anything. Until they arrive, visitors see a blank screen. Eliminating them rarely means deleting every one. It means removing the ones the page does not need, loading scripts so they do not stop the parser, and keeping only a small amount of essential CSS on the critical path so the first screen can appear quickly.
2. Are images render-blocking?
No. Images do not stop the browser from painting the rest of the page. They can still slow down Largest Contentful Paint if the main image is large, discovered late or lazy-loaded, and they compete for bandwidth with blocking files early in the load. So images matter for speed, but they belong to a different part of the problem. If a report lists images as blocking, check the tool, because it is probably describing something else, such as LCP delay.
3. Why does my font stylesheet show up as render-blocking?
A font service usually gives you a link to a stylesheet that describes the font files. That stylesheet is a normal CSS file linked in the head, so the browser waits for it before painting. It is often on a separate domain, which adds a lookup and a new connection before any bytes arrive. Self-hosting the font files and their small CSS on your own domain removes that delay. If you keep the service, add a preconnect hint and request fewer weights.
4. Is it safe to defer every script on my site?
Not blindly. Deferring changes when a script runs, and code that expects a library to be available straight away can break, for example an inline snippet that calls jQuery before the deferred copy has loaded. Menus, sliders, forms and checkout flows are where problems show up. Defer scripts one at a time on a staging copy, test the pages that use them, and keep any script that genuinely has to run first as small as possible.
5. Will removing render-blocking resources improve my Core Web Vitals?
It usually helps First Contentful Paint and often Largest Contentful Paint, because both depend on how soon the browser can start painting. Whether it moves your Core Web Vitals assessment depends on what else is slow. If your server responds slowly or your main image is huge, those may still dominate. Real-user data takes time to update, since it reflects visits over roughly the previous 28 days, so allow a few weeks before judging the change.
6. Why does the report still show render-blocking files after I fixed them?
There are a few common reasons. A cache or CDN may still be serving the old HTML, so purge it and retest. An optimisation plugin may only apply its changes to visitors who are not logged in, so test in a private window. Some files are legitimately blocking, such as a small critical stylesheet, and tools will keep listing them. Finally, check the source of the flagged file: it may be injected by a platform or app you did not edit.
7. Should I use preload for all my CSS and JavaScript files?
No. Preload raises the priority of a file so the browser fetches it early, which helps a small number of critical files that would otherwise be found late. If you preload many files, they all compete for the same bandwidth and the important ones arrive later, not sooner. Preload also does not stop a stylesheet from blocking the paint. Use it for one or two files you can clearly justify, and check the waterfall afterwards to confirm it helped.
The Page Speed Analyzer measures your page with real-user and lab data, shows the network waterfall, and names the specific files and scripts holding the page back, so you can see which blockers to deal with first.
Open Page Speed Fixes โ