Website problems
Render-blocking resources
A render-blocking resource is a file the browser must download and process before it can paint anything. Stylesheets block rendering by default, because painting with unstyled content and then restyling would produce a visible flash. A `<script>` without `async` or `defer` blocks both parsing and rendering while it downloads and executes. The consequence is a page that has arrived over the network and still shows a blank screen — which is why total page weight and perceived speed are different measurements.
What blocks and what does not
A stylesheet in `<head>` blocks rendering until it is fetched and parsed. This is by design: the browser needs the styles to paint correctly.
A classic `<script src>` in `<head>` with no attribute blocks parsing at the point it appears, then downloads, then executes, then parsing resumes. Everything after it in the document waits.
`<script defer>` downloads in parallel and executes after parsing completes, in document order. It does not block.
`<script async>` downloads in parallel and executes as soon as it arrives, which can still interrupt parsing but does not hold up the start of it. Order is not guaranteed.
A stylesheet with a `media` attribute that does not match — `media="print"` — is fetched at low priority and does not block.
Fonts do not block rendering, but they can block text from appearing, which looks identical to the reader.
Why weight and blocking are different problems
A page of two megabytes that paints in 800 milliseconds feels fast. A page of two hundred kilobytes that paints in four seconds feels broken.
What determines the difference is the critical path: the sequence of requests that must complete before the first paint. Everything outside that path can arrive later without anyone noticing.
So the useful question is not how much the page weighs, but what is in the way. Three blocking scripts in the head can be smaller than the images below the fold and cost far more.
This is also why compressing files helps less than expected on a blocked page: a smaller blocking file still blocks.
How to check it yourself
In the browser's performance panel, record a load and read the waterfall. The requests before the first paint marker are the critical path, and the blocking ones are those that start early and finish late.
In the Network panel, sort by start time and look at `<head>` resources. A stylesheet or script fetched from a third-party host is a DNS lookup, a connection and a TLS handshake before the file even starts.
In the console: `[...document.querySelectorAll('head script[src]')].filter(s => !s.async && !s.defer)` returns the classic blocking scripts.
Throttle to a slow connection and reload. Blocking is invisible on a fast link and unmistakable on a slow one.
How to unblock, in order of safety
Add `defer` to scripts that do not need to run before the document is parsed, which is almost all of them. This is the single safest change and usually the largest win.
Move analytics and tag managers out of the blocking path. Very few need to execute before the page appears, and the ones that claim to usually do not.
Split the stylesheet. Inline the styles needed for the visible area and load the rest with a non-blocking pattern, so the first paint does not wait for styles that apply below the fold.
Give print stylesheets `media="print"` so they stop blocking, which is a one-attribute change.
Preconnect to the origins the critical resources come from, so the connection cost is paid in parallel rather than in sequence.
What to avoid: moving scripts to the end of the body as a substitute for `defer`. It helps, and `defer` expresses the intent and downloads earlier.
The third-party version of the problem
A blocking script from your own origin costs one request on a connection that is already open. A blocking script from a third-party host costs DNS, TCP, TLS and then the request.
On a slow mobile connection that is frequently several hundred milliseconds before a single byte of the file arrives, and the page shows nothing throughout.
A tag manager makes this worse by loading further scripts from further origins, each with its own connection cost, all before the page paints if the manager itself is blocking.
The remedy is the same — defer it — and the reason to prioritise it is that the cost is higher per script than for anything you host yourself.
How VeriFixScan detects it
`performance.render_blocking` reads the `<head>` of each crawled page and reports the stylesheets and synchronous scripts that block the first paint, with the URL of each resource.
`performance.css` and `performance.javascript` report the size and count of those resources, which is what tells you whether the fix is to defer them or to reduce them.
`performance.third_party_resources` separates the ones served from other origins, where the connection cost makes each one more expensive.
`performance.preconnect` and `performance.preload` report the hints already in place, which decide whether unblocking is a matter of attributes or of connection setup.
Frequently asked questions
- What is the difference between async and defer?
- Both download in parallel without blocking parsing. defer executes after parsing completes, in document order. async executes as soon as it arrives, in no guaranteed order. defer is the safer default for scripts that depend on the document or on each other.
- Do stylesheets have to block rendering?
- By default yes, because painting unstyled content and restyling produces a flash. Splitting out the styles needed for the visible area, and loading the rest without blocking, is the standard way to reduce it.
- Is moving scripts to the end of the body the same as defer?
- Close, and weaker. A script at the end of the body is discovered late, so it starts downloading late. defer downloads immediately and executes at the same point.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website