A browser cannot paint until it knows how things will look. That single fact explains render-blocking resources, and most of what follows is about changing the order in which the browser learns things.

Advertisement: Kinsta managed WordPress hosting, free for the first 30 days

Advertisement. We earn a commission if you sign up through this link, at no extra cost to you.

The tools are: getting less CSS in the way, getting JavaScript out of the way, and telling the browser in advance what it will need. They are frequently applied in the wrong order and occasionally applied to problems they do not solve.

What actually blocks rendering

CSS is render-blocking by design. The browser will not paint until it has parsed every stylesheet in the head, because painting first would mean showing unstyled content and then redrawing it. This is correct behavior — the cost is that a slow stylesheet holds up everything.

JavaScript in the head without async or defer is parser-blocking. HTML parsing stops entirely while the script is fetched and executed, because the script might write into the document. Nothing further in the page is even discovered until it finishes.

Fonts block text, not layout. With the default font-display, text stays invisible for up to three seconds waiting for a font file.

What does not block rendering, despite frequent claims: images, scripts already marked async or defer, and stylesheets with a non-matching media attribute.

To find yours, open PageSpeed Insights and read “Eliminate render-blocking resources”, or open the Network panel in DevTools and look at what sits before the first paint marker.

JavaScript: async, defer, module

Three attributes, and choosing between them is simpler than it is usually made to sound.

<script src="analytics.js" async></script>
<script src="app.js" defer></script>
<script src="main.js" type="module"></script>

defer — downloads alongside parsing, executes after the HTML is parsed, and preserves order between scripts. This is the right default for anything that touches the page or depends on another script.

async — downloads alongside parsing, executes the moment it arrives, in whatever order they finish. Correct for genuinely independent things: analytics, tag managers, error trackers. Wrong for anything with a dependency, because the order is not guaranteed.

type=”module” — deferred automatically. No extra attribute needed.

The old advice to move all scripts to the bottom of the body still works, but defer is better: the download starts early and only the execution waits.

In WordPress, scripts are enqueued rather than written into templates, so this is set at registration:

wp_script_add_data('my-script', 'strategy', 'defer');

Since WordPress 6.3 there is proper support for defer and async strategies in the enqueue API, so you no longer need the filter hacks that older guides describe. Performance plugins expose the same thing as a checkbox per script, which is usually where this actually gets done.

Critical CSS

The idea: identify the CSS needed to render what is visible before scrolling, inline that in the head, and load the rest asynchronously. The browser can paint immediately from the inlined rules and the full stylesheet arrives without holding anything up.

<style>/* inlined critical rules */</style>
<link rel="preload" href="full.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="full.css"></noscript>

Generating the critical set by hand is impractical, and the tooling is mature: the critical and penthouse npm packages for a build pipeline, and in WordPress, LiteSpeed Cache, WP Rocket, Autoptimize and Perfmatters all generate and inline it for you.

Two warnings from experience. Critical CSS is per template, not per site — the home page and a blog post have different visible content, and one shared critical set produces a flash of wrong styling on one of them. And it goes stale: change the design, regenerate it, or you are inlining rules for a layout that no longer exists.

Honest assessment: this is the highest-effort item on this page and it delivers real gains on heavy sites. On a light site with one modest stylesheet, the improvement can be within measurement noise. Do the cheaper things first and see whether you still need it.

Minification, honestly

Minification strips whitespace, comments and long variable names. It is universally recommended and its benefit is routinely overstated, for one reason that rarely gets mentioned: your server is already compressing these files.

Gzip and Brotli both do extremely well on repetitive text, which is exactly what unminified CSS and JavaScript are. Once Brotli has run, minification typically adds a few percent — real, but not the transformation the guides imply.

So: turn it on, because it is a checkbox in every caching plugin and it costs nothing. Do not expect it to fix a slow site, and if it is the main item on your performance plan, the plan needs more in it.

The related habit worth more than minification: removing code you are not using. The Coverage panel in Chrome DevTools shows what percentage of each file was actually executed on a page load. Seeing that 85 percent of a stylesheet went unused is a more productive finding than any minifier.

Resource hints

Four hints, each solving a different problem, and the difference between them matters.

preconnect

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

Performs the DNS lookup, TCP handshake and TLS negotiation for a third-party origin early, so the eventual request skips all of it. Typically saves 100 to 500 milliseconds on a cross-origin request, more on mobile.

Use it for origins you know you will need in the first moments: your font provider, your image CDN, your analytics endpoint. Limit it to four to six — each open connection has a cost, and preconnecting to everything is worse than preconnecting to nothing.

The crossorigin attribute is required for fonts and easy to forget. Without it the browser opens a second connection and you have paid the cost twice.

dns-prefetch

<link rel="dns-prefetch" href="https://example-cdn.com">

DNS resolution only. Much cheaper than preconnect, much smaller benefit. The sensible pattern is preconnect for your two or three critical origins and dns-prefetch for the rest.

preload

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">

Tells the browser to fetch something at high priority now, because it will be needed shortly. The classic use is a resource the parser cannot discover early — a font referenced inside a stylesheet, or a hero image set as a CSS background.

The as attribute is not optional. Without it the browser cannot assign the right priority and may fetch the file twice.

Preload is a claim of importance and it is zero-sum. Preloading ten things reorders nothing, because everything cannot be first. Two or three per page is the useful range.

prefetch

<link rel="prefetch" href="/next-page.html">

Fetches at the lowest priority for a future navigation. Useful where the next step is predictable — page two of a paginated flow, the checkout after the cart. Wasteful when guessed badly, since you are spending someone’s data on a page they may never open.

The order to do this in

  1. Audit what is loading. The Coverage panel and the Network waterfall. You will usually find something you forgot was there.
  2. Delete what you do not need. The fastest resource is the one not requested. An unused slider library beats any amount of clever loading.
  3. Add defer or async to every remaining script. Cheap, safe, immediate.
  4. Preconnect to your two or three critical third-party origins. One line each.
  5. Preload the genuinely critical fewthe LCP image, the primary font.
  6. Turn on minification and compression in your caching plugin.
  7. Then consider critical CSS, if the numbers still say you need it.

Steps two and three account for most of the available improvement on most sites. Step seven gets most of the attention.

The short version

CSS blocks painting, undeferred JavaScript blocks parsing. Defer everything that touches the page, async only what is truly independent, and delete what you are not using — that last one beats every technique here. Preconnect to a handful of origins, preload two or three resources, and treat minification as a checkbox rather than a strategy. Critical CSS is real and it is the last thing to reach for, not the first.

Working through this list on your own site and would rather not? That is what an Expert Web Audit is for.

Advertisement: Kinsta managed WordPress hosting

Advertisement. We earn a commission if you sign up through this link, at no extra cost to you.

Leave a Reply

Your email address will not be published. Required fields are marked *