Performance · Explainer

Render-blocking resources

Before a browser can paint anything, it has to finish everything in the head that says "wait for me". A handful of tags there decides whether your page appears in half a second or three. This is the cheapest performance work most sites never do.

What blocking actually means

The browser is not being slow. It is obeying instructions to wait, and those instructions are in your markup.

A browser parses HTML top to bottom. When it reaches an ordinary <script src> in the head, it stops parsing, fetches the file, runs it, and only then continues, because the script might rewrite the document beneath it. Stylesheets block differently: parsing continues, but the browser will not paint until the CSS has arrived, because painting first would flash unstyled text and then reflow it. In both cases the page is ready in memory and deliberately withheld from the screen. Every blocking file in the head adds its full round trip to the time before anything appears.

Why this shows up in your Core Web Vitals

Blocking resources push out the exact moment Largest Contentful Paint measures.

LCP records when the largest element in the viewport finishes rendering. If painting cannot start until four stylesheets and two scripts have downloaded, LCP cannot be earlier than that, no matter how well optimised the image itself is. Teams often compress the hero image and see the score barely move, because the image was never the constraint: the wait before painting was.

This is also why lab and field numbers diverge. On a fast office connection six blocking files cost little. On a mid-range phone on mobile data each one is a fresh round trip, and the same page takes multiples longer. Core Web Vitals explained covers what the thresholds are; this page covers the most common reason a site misses them.

Fixing scripts: defer, async, or move

Three attributes cover almost every script on a normal page. Choosing between them takes seconds once the difference is clear.

defer

Downloads in parallel with parsing and runs after the HTML is parsed, in document order. This is the right default for almost every script a page loads: application code, widgets, anything that reads the DOM.

Order is preserved, so a script that depends on one earlier in the page still works. If you are unsure which attribute to use, defer is the safe answer.

async

Downloads in parallel and runs the moment it arrives, interrupting parsing. Order is not guaranteed, so it suits only genuinely independent scripts such as an analytics beacon.

Using async on code with dependencies produces intermittent failures that appear on slow connections and vanish on fast ones, which makes them unusually hard to reproduce.

Move it out of the head

A script with neither attribute placed just before the closing body tag no longer blocks the initial paint, because the parser has already reached the end of the document.

This is the oldest fix and still works, but defer is generally better: it starts the download immediately instead of waiting until the parser arrives, so the file is usually ready sooner.

Third-party tags

Tag managers, chat widgets and A/B testing snippets are frequently installed as plain blocking scripts in the head because the vendor instructions say to.

These are often the largest blocking cost on the page and the least examined. Load them with defer or async unless the vendor gives a concrete reason they must run first, and measure what each one costs before accepting it.

Fixing CSS without breaking the page

Stylesheets cannot simply be deferred, so the approach is different.

Deferring all CSS causes a flash of unstyled content, which is worse than a short wait and damages Cumulative Layout Shift as the page reflows. The working approach is to split it: inline the small amount of CSS needed to render what appears in the viewport first, and load the rest without blocking, commonly by setting media="print" and switching it to all once loaded.

Before reaching for that, check the simpler wins. Many sites block on stylesheets they barely use: an icon font loaded for two icons, a full UI framework for one component, or a print stylesheet that should be media="print" already and therefore never blocking. Removing an unused blocking file is faster and safer than engineering around it, and it is usually the larger saving.

Render-blocking questions, answered

What exactly makes a resource render-blocking?

A resource in the head that the browser must fetch and process before it can safely paint. A plain <script src> without async or defer stops HTML parsing outright, because the script could rewrite what follows it. A stylesheet lets parsing continue but blocks painting, because showing unstyled content and then reflowing it once CSS arrives would look worse than the wait.

Does fixing this actually move my LCP score?

Yes, for scripts directly: removing a blocking script lets the browser reach and paint the rest of the page sooner, often including the LCP element itself. CSS needs a different technique since a stylesheet cannot simply be deferred without a flash of unstyled content; inlining critical CSS and loading the rest asynchronously is the equivalent fix.

Is critical CSS worth setting up?

Where LCP is capped by stylesheet download time, yes. It inlines only the styles needed for what's visible on load, so the browser never waits on an external file to paint that content. It adds a build step and needs regenerating when above-the-fold markup changes, so weigh that against the size of the win on a given page.

Can I just add defer to every script and be done?

Not blindly. defer is the safe default for most application code, but a script that intentionally must run before paint, such as a theme class-setter that prevents a flash of the wrong theme, needs to stay blocking on purpose. Check what a script depends on before moving it.

Why are third-party tags usually the worst offenders?

Because they're installed exactly as the vendor's snippet says, which is typically a plain blocking script in the head, and their size and behaviour are outside your control. A tag manager or chat widget is frequently the single largest blocking resource on the page. Load it with defer or async unless the vendor has a specific reason it must run first.

Does render blocking affect INP too, or just LCP?

Mainly LCP and first paint, since blocking is about delaying the page from appearing at all. INP is measured once the page is interactive and is driven more by long JavaScript tasks on the main thread during and after interactions. A heavy blocking script can hurt both: it delays paint, and once it runs, a large synchronous script can also tie up the thread. See Core Web Vitals explained for the INP thresholds and fixes.

See what is blocking your first paint

Free to start. Flags render-blocking scripts and stylesheets in the head, page by page, alongside PageSpeed results.

Start my free audit