Render-blocking requests in Lighthouse: zo los je ze op
Lighthouse 13 toont elk stylesheet en script waarop je pagina wacht voor de first paint, ook de snelle. Wanneer dat je bezoekers tijd kost en welke CSS-fixes werken.
Kosten render-blocking requests je score?
Lighthouse toont elk stylesheet en script waarop je pagina wachtte voor de first paint in de insight Render-blocking requests ("Requests blokkeren de eerste render van de pagina, wat LCP kan vertragen") – vóór Lighthouse 13 heette dit "Eliminate render-blocking resources". De insight telt niet mee voor je score, maar zolang de pagina op zijn CSS en scripts wacht blijft het scherm wit. Dat vertraagt FCP en LCP, vooral op een telefoon. Trim en combineer je CSS en defer je scripts.
Table of Contents!
Wat checkt de insight Render-blocking requests?
De beschrijving in Lighthouse zegt: "Requests blokkeren de eerste render van de pagina, wat LCP kan vertragen. Door ze te deferren of te inlinen haal je deze netwerkrequests uit het kritieke pad." Inlinen is niet de fix: zie hieronder. De tabel heeft drie kolommen: URL, Transfer Size en Duration. Transfer Size is het bestand zoals het verzonden is, gecomprimeerd. Duration is de schatting van Lighthouse van hoe lang het request duurt op een trage telefoon, inclusief een nieuwe verbinding naar een ander domein.
Tijdens het laden van een pagina markeert Chrome elk request als render-blocking of niet en schrijft dat in zijn trace. Lighthouse leest de trace uit en toont elk request in het main frame dat door Chrome als blokkerend is gemarkeerd en dat klaar was voor de first paint. In de praktijk zijn dat de stylesheets en synchrone scripts in de <head>, plus de bestanden die ze via @import binnenhalen. Een stylesheet in de body telt alleen mee op Chrome's hoogste prioriteit, een script op hoge prioriteit; na de eerste afbeelding zakken beide omlaag en worden ze overgeslagen. Er is geen drempelwaarde: één bestand op de lijst laat de insight al falen.
Sommige bestanden zie je hier nooit terug, ook al vertragen ze de pagina: async scripts, bestanden die een script aan de pagina toevoegt (gtm.js, gtag.js), inline styles en scripts, alles in een iframe, en fonts.

De insight op een WooCommerce-productpagina, Lighthouse 13.5.0, gesimuleerde throttling zoals in PageSpeed Insights.
Wat maakt een bestand render-blocking?
Render-blocking betekent dat de browser niets rendert tot dat bestand binnen is: elk stylesheet in de <head> moet binnen zijn en elk synchroon script daar moet downloaden en draaien.
- Een script na een stylesheet wacht op dat stylesheet. Chrome voert een synchroon script pas uit als de CSS erboven binnen is, en de parser wacht op het script. Beide bestanden kunnen tegelijk downloaden, maar je betaalt alsnog eerst voor de CSS en daarna voor het script.
- Een
@importstart laat. De preload scanner van Chrome vindt elke link in de HTML meteen, maar een@importin een CSS-bestand pas als dat bestand binnen is. Op de productpagina hieronder startte het geïmporteerde theme-stylesheet op 1,28 s, tegenover 0,59 s als het vanuit de HTML werd gelinkt. - Een ander domein kost een verbinding. Een stylesheet op fonts.googleapis.com of een CDN heeft DNS, TCP en TLS nodig voordat de eerste byte binnenkomt.
| In Chrome | Houdt de first paint tegen? |
|---|---|
<link rel="stylesheet"> in de head | Ja |
hetzelfde met fetchpriority="low" | Ja |
hetzelfde met media="print" | Nee, wordt wel gedownload |
rel="preload" plus een stylesheet-link naar hetzelfde bestand | Ja |
@import in een blokkerend stylesheet | Ja, na zijn parent |
<script src> in de head | Ja |
async | Nee, maar kan wel voor de paint draaien |
defer, type="module" | Nee |
| een stylesheet in de body | Alleen de content eronder |
Hebben render-blocking requests invloed op FCP en LCP?
Ik heb het gemeten op een WooCommerce-productpagina met negen stylesheets en twee scripts in de head, op een telefoon via Slow 4G. In de gefixte versie is de CSS opgeschoond en gecombineerd tot één stylesheet van 6,6 kB en zijn beide scripts deferred. Eenmaal geladen ziet het er exact hetzelfde uit.
FCP
Het origineel had de first paint op 2,12 s, de gefixte versie op 1,39 s.

Voor

Na
Beide versies op 1,4 s op de gethrottlede telefoon: het origineel is nog wit.
LCP
LCP kan niet vóór de first paint vallen. Hier is het LCP-element de productfoto, en die kan pas verschijnen als de CSS binnen is: met de fix ging LCP van 2,25 s naar 1,91 s. In een LCP-breakdown is die wachttijd zichtbaar als element render delay. Lighthouse laat dit niet zien: met een afbeelding als LCP-element schat het de LCP-winst altijd op 0 ms. Denk dus niet dat er geen effect is op LCP.
Hoeveel sites hebben hier last van en kun je dit gerust negeren?
Bijna allemaal. State of CWV onderzoekt de homepages van 114.526 sites met CrUX-data: 92,8% heeft minstens één render-blocking request, 91,2% een blokkerend stylesheet en 62,5% een blokkerend script. Waar ze voorkomen is de mediaan 8 requests en 110 kB, en één op de vijf sites heeft er meer dan 20. (State of CWV telt stylesheets en synchrone scripts in de head, wat vrijwel overeenkomt met de lijst van Lighthouse.)
Sites met veel van dit soort requests presteren slechter in the field. 84,0% van de sites zonder blokkerende requests heeft een goede FCP op telefoons, tegenover 54,1% van de sites met meer dan 20. Maar de server verklaart het grootste deel van die daling: over datzelfde bereik verdubbelt de mediane TTFB van 538 ms naar 1.114 ms. Uitgesplitst naar serversnelheid:

Sites met een goede FCP op telefoons naar aantal render-blocking requests en serverresponstijd. State of CWV, oktober 2026, CrUX p75 op telefoons, een goede FCP is 1,8 s of minder.
- Snelle server (TTFB onder 600 ms): 94 tot 97% heeft een goede FCP, ongeacht het aantal. De vertraging is er nog steeds: van first byte naar first paint duurt het met 21 of meer blokkerende requests zo'n 150 tot 200 ms langer dan zonder.
- Trage server (TTFB van 900 ms of meer): een goede FCP daalt van ongeveer de helft van de sites (47 tot 53%) bij maximaal twee blokkerende requests naar 30% bij 21 of meer. Bij een TTFB van 900 ms of meer blijft er minder dan 0,9 s over tot de grens van 1,8 s voor een goede FCP. Elk extra request waarop de paint moet wachten, kan FCP dus over de grens duwen.
WordPress-sites hebben er meer: 57% heeft 16 of meer blokkerende requests (mediaan 19, 163 kB). Bij dezelfde serversnelheid kost dat op een snelle host 3 procentpunt aan goede FCP vergeleken met één tot vijf requests, en op een trage host zo'n 10 procentpunt.
Dat is een correlatie, maar het bepaalt wel de volgorde: heb je een trage TTFB, pak dan eerst de server aan. Op een snelle server is die 150 tot 200 ms nog steeds de moeite waard om mee te pakken als het om je eigen CSS gaat. Je kunt de melding laten zitten als:
- Lighthouse slechts één of twee kleine stylesheets van jezelf toont, elk kleiner dan zo'n 15 kB;
- het vermelde bestand een consent tool, een A/B-test snippet of een platformbestand is dat je niet kunt aanpassen (zie hieronder).
Hoe herken je render-blocking requests in je field data?
Lighthouse laadt je pagina één keer, koud. Na hun eerste pagina hebben bezoekers de CSS in hun cache, dus in the field zie je de impact vooral op de eerste pagina die een bezoeker laadt. Kijk in CoreDash op mobiel naar twee dingen:
- FCP naast TTFB. Render-blocking bestanden kosten tijd tussen de first byte en de first paint. Een template waar dat gat groter is dan bij de rest, laadt er meer.
- LCP render delay. Zoek naar templates waar render delay een groot deel van de LCP vormt. Blokkerende CSS is één van de oorzaken van die wachttijd; DevTools (volgende sectie) laat zien of die pagina er heeft.
CrUX toont dezelfde metrics op p75 voor je origin of een URL, maar vertelt je niet welk template traag is.

CoreDash op een voorbeeldshop, acme-pagespeed.com, mobiel, p75: de product- en categoriepagina's zijn traag.
Hoe vind je de blokkerende bestanden in DevTools?
Open de pagina in Chrome, ga naar het Performance-paneel, zet het netwerk op Slow 4G en de CPU op 4x slowdown, en neem een reload op. In de Insights-sidebar staat Render-blocking requests. Klik erop en DevTools omkadert elk blokkerend request in de Network-track.

Elke balk loopt vanaf het moment dat het request startte tot de laatste byte binnenkwam. Zoek naar het request dat als laatste eindigt vóór de FCP-marker: daar heeft de paint op gewacht. Een CSS-request dat pas start zodra een ander eindigt, is een @import. Blokkeert er niets, dan toont de sidebar "No render-blocking requests for this navigation".
Hoe los je render-blocking requests op?
De URL in de Lighthouse-tabel vertelt je waar elk bestand vandaan komt, en dat bepaalt waar je het aanpast.
Je eigen CSS en scripts
Streef in je eigen templates naar deze head: één klein, opgeschoond stylesheet en elk script deferred. Dan wacht de first paint op nog maar dat ene bestand.
<link rel="stylesheet" href="/css/main.min.css">
<script src="/js/jquery.min.js" defer></script>
<script src="/js/gallery.min.js" defer></script> Hoe je dat aanpakt lees je verderop, te beginnen bij Trim en combineer de CSS.
WordPress
Bestanden onder /wp-content/plugins/ horen bij een plugin, /wp-content/themes/ bij je theme en /wp-includes/ bij WordPress zelf.
Schrijf je zelf geen code, gebruik dan een optimalisatie-plugin met deze instellingen:
- Zet het deferren van scripts aan: Load JavaScript deferred in WP Rocket, Defer JavaScript in Perfmatters, Load JS Deferred in LiteSpeed Cache. Een deferred script houdt de first paint niet meer tegen. Met jQuery moet je oppassen, zie hieronder.
- Laat Load CSS Asynchronously en critical CSS uitstaan, wat de oude WordPress-tip onder deze insight ook beweert over plugins die "inline critical assets" toepassen. Critical CSS werkt wel, maar de rest van de CSS arriveert na de first paint. De pagina stylet zichzelf daardoor twee keer en verschuift vaak. Wat inlinen daarbovenop kost, lees je onder Trim en combineer de CSS.
- Laat Remove Unused CSS uitstaan (Generate UCSS in LiteSpeed Cache). Dit bouwt aparte CSS voor elke URL (WP Rocket zet het inline in de HTML) en bewaart alleen de regels die het vindt tijdens het laden van de pagina. Een element dat later door een script verandert (een geopend menu, een slider), kan daardoor zijn styles kwijtraken. WP Rocket heeft een complete handleiding voor de weergaveproblemen die dit veroorzaakt. Dat levert meer gedoe op dan het waard is: trim en combineer de stylesheets zelf. Het kost meer werk, maar verdient zichzelf terug.
Veel plugins laden hun CSS op elke pagina, ook op pagina's waar ze niets doen: Contact Form 7 op pagina's zonder formulier, WooCommerce buiten de shop. Elk stylesheet dat WordPress inlaadt heeft een id zoals contact-form-7-css: het deel vóór -css is de handle waarmee je het verwijdert waar het niet nodig is:
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'contact' ) ) {
wp_dequeue_style( 'contact-form-7' );
}
}, 20 ); WooCommerce heeft een eigen filter voor zijn drie stylesheets: woocommerce_enqueue_styles. Voor je eigen scripts kan WordPress vanaf versie 6.3 defer toevoegen:
wp_enqueue_script( 'acme-gallery', get_stylesheet_directory_uri() . '/js/gallery.min.js', array( 'jquery' ), '2.3.0', array( 'strategy' => 'defer' ) ); WordPress laat die defer zonder waarschuwing weer vallen als een script dat afhankelijk is van het jouwe nog blokkeert, of als er daarna een inline script volgt. Controleer de script-tag in de broncode van de pagina.
Defer jQuery alleen met een tool die ook de inline jQuery(...)-calls afhandelt. WordPress registreert jQuery zonder defer, waardoor het de head blokkeert zodra er iets van afhangt. Handmatig defer toevoegen sloopt elke inline jQuery(...)-call die ervoor draait. WP Rocket's Load JavaScript deferred zet defer op jQuery en wrapt die inline calls. Perfmatters' Defer JavaScript laat jQuery met rust tot je Include jQuery aanzet; doe je dat, klik dan door de pagina (menu, gallery, winkelmand) om te controleren of er niets stuk is.
Een child theme waarvan de style.css begint met @import url("../storefront/style.css") bouwt een @import-keten op. Verwijder die regel en laad het parent-stylesheet in met wp_enqueue_style.
Shopify
Apps voegen hun bestanden toe via app embeds (Online Store, Edit theme, App embeds). Het scriptbestand van een embed laadt async (tenzij de Liquid-code een eigen script-tag schrijft), het stylesheet blokkeert. Zet embeds uit die je niet gebruikt. En heb je een app verwijderd, zoek dan in theme.liquid en de snippets naar achtergebleven code.
Themes laden hun CSS met het stylesheet_tag-filter in theme.liquid, wat een blokkerende link oplevert. Voor dat ene basis-stylesheet is dat prima. Het script_tag-filter schrijft een script zonder defer, dus schrijf die tag zelf:
<!-- blokkeert de first paint -->
{{ 'gallery.js' | asset_url | script_tag }}
<!-- blokkeert niet -->
<script src="{{ 'gallery.js' | asset_url }}" defer></script> Shopify verstuurt al 103 Early Hints voor maximaal 10 render-blocking bestanden in de head, dus die bestanden starten sowieso vroeg. Wat je nog wel kunt beïnvloeden, is hoeveel het er zijn en hoe groot. Reken erop dat het stylesheet voor accelerated checkout op de lijst blijft staan (zie hieronder).
Next.js
In de App Router wordt elke CSS-import een stylesheet-link bovenin de head, en React voert geen paint uit tot alles geladen is. Next.js voegt ze samen in een paar chunks, dus de fix is minder CSS. Verplaats CSS die slechts door enkele routes wordt gebruikt uit de globale CSS naar CSS Modules: globale CSS die in de root layout is geïmporteerd gaat naar elke route, een CSS Module alleen naar de routes die hem importeren. Meer over hoe Next.js zijn CSS-bestanden opbouwt.
Next.js laadt zijn eigen scripts async of deferred. Een blokkerend script op een Next.js-site is er eentje die iemand handmatig aan de layout heeft toegevoegd. Laad deze in met next/script, wat het script standaard pas inlaadt zodra Next.js begint met het hydraten van de pagina:
import Script from 'next/script'
<Script src="https://example.com/widget.js" strategy="afterInteractive" /> Google Fonts en andere domeinen
De Google Fonts-link is een blokkerend stylesheet op fonts.googleapis.com, en de fontbestanden komen van een tweede domein: fonts.gstatic.com. Lighthouse gaf dat bestand van 0,5 kB de langste Duration: de nieuwe verbinding telt mee. Host de fonts zelf en zet de @font-face-regels in je eigen stylesheet; dan zijn het request en beide verbindingen verdwenen. In Next.js doet next/font dit al automatisch.
Consent tools, A/B-tests en andere bestanden die je met rust laat
- Een consent tool met auto-blocking (Cookiebot, OneTrust's OtAutoBlock, de Usercentrics-autoblocker): deze moet vóór elk ander script draaien om ze tegen te houden, en is dus bewust synchroon ontworpen. Het alternatief is manual mode, waarbij je elk cookie-script zelf markeert. Of dat het werk waard is, is vooral een juridische vraag. Een preconnect naar het domein van de tool start de verbinding in elk geval vroeg.
- Een A/B-test snippet (Optimizely, AB Tasty, Convert): bewust synchroon, zodat bezoekers het origineel niet kort zien flitsen vóór de variant. Verwijder de snippet als er geen test draait.
- Shopify's content_for_header: elk theme heeft dit nodig en je kunt het niet aanpassen. Bij alle vijf de shops die voor deze pagina zijn gecheckt, voegde het een blokkerend stylesheet toe voor accelerated checkout: accelerated-checkout-backwards-compat.css. Laat dit gewoon staan.
Trim en combineer de CSS
Beperk de render-blocking CSS tot een paar stylesheets van elk minder dan zo'n 15 kB gecomprimeerd. 15 kB is grofweg wat een nieuwe verbinding in zijn eerste round trip binnenhaalt.

Zes render-blocking stylesheets op Slow 4G in het DevTools Network-paneel. Groen is het wachten op de first byte, wat voor elk bestand gelijk is. Blauw is de download, die oploopt met de bestandsgrootte.
- Combineer de piepkleine bestanden die je theme en plugins toevoegen. Elk stylesheet wacht een volledige round trip voordat zijn first byte binnenkomt – op Slow 4G zo'n 0,6 s, ongeacht de grootte. Een bestand van 0,3 kB betaalt die volledige wachttijd voor vrijwel niets.
- Trim of splits een groot bestand. Na diezelfde wachttijd was het bestand van 14 kB al 0,4 s later binnen. Het bestand van 60 kB had nog een seconde extra nodig om te downloaden, en de pagina bleef wit tot het binnen was.
- Verwijder de regels die de pagina niet gebruikt. Het Coverage-tabblad in DevTools toont per bestand hoeveel van de CSS de pagina nooit gebruikt. Bij de doorsnee site is dat 89% (State of CWV, Chrome's coverage van de homepage).
- Gooi niet alle CSS in één groot bestand. Alle CSS, ongetrimd, in één bestand van 68 kB gaf een latere paint dan de negen losse bestanden. Het grote bestand deelde de bandbreedte met de deferred scripts en de hero image, terwijl bij negen bestanden de render-blocking requests het grootste deel kregen.
- Ga de CSS niet inlinen. Inline CSS komt in de HTML terecht, en de HTML is het eerste bestand dat de browser nodig heeft: al het andere wacht erop. Inlinen maakt dat ene bestand waar alles op wacht dus groter en trager. Belangrijker nog: de browser kan de HTML meestal niet cachen. Elk paginabezoek downloadt die CSS dus opnieuw, terwijl een los stylesheet na de eerste pagina gecachet is. Van de sites met een TTFB onder 900 ms en één tot vijf blokkerende requests heeft 95,9% van de sites zonder inline CSS een goede FCP, tegenover 90,8% van de sites die meer dan 300 kB inlinen (State of CWV).
- Splits "de rest" van de CSS niet af om later te laden. De oude
media="print"-truc werkt alleen voor CSS die nooit op het eerste scherm staat, en dat weet je zelden zeker voor elke pagina en elk schermformaat: gok je verkeerd, dan rendert de pagina eerst zonder styling.
Vervang elke @import
Geef elk geïmporteerd bestand een eigen link in de HTML, of voeg het samen met het parent-bestand. Het verwijderen van de @import op de productpagina bracht de first paint 137 ms naar voren zodra de scripts deferred waren. Terwijl de scripts nog blokkeerden scheelde het 33 ms: jQuery kwam ongeveer tegelijk binnen met het geïmporteerde stylesheet, waardoor de paint sowieso op jQuery wachtte.
Defer de scripts
Voeg defer toe aan elk script in de head dat niet nodig is voor de first paint. Module scripts zijn standaard al deferred. Async haalt een script ook van de lijst, maar kan alsnog vóór de first paint draaien en de main thread bezet houden; defer is dus de betere standaard. Scripts vormen vandaag de dag het kleinste deel van deze insight: het deferren van beide scripts leverde 24 ms op, omdat de CSS sowieso pas later klaar was. Meer hierover in async vs defer, head vs footer JavaScript en 14 methoden om JavaScript te deferren.
Start het hoofd-stylesheet vroeg met 103 Early Hints
Met 103 Early Hints vertelt de server de browser welke bestanden alvast opgehaald kunnen worden terwijl hij de HTML nog aan het opbouwen is. Geef een hint voor je hoofd-stylesheet:
HTTP/2 103
link: </css/main.min.css>; rel=preload; as=style Dit loont als je server tijd nodig heeft om de pagina op te bouwen. Het stylesheet downloadt in die tussentijd, dus de winst is hooguit de denktijd van je server. Een pagina die vanuit een CDN-cache wordt geserveerd wint er weinig mee. Cloudflare heeft er een schakelaar voor (Speed, Settings, Content Optimization) en bouwt de hints op basis van de Link-headers die je server meestuurt. Apache heeft vanaf versie 2.4.58 H2EarlyHint.
Check daarna wel je LCP. Als de CSS vroeg binnen is, starten de fonts die erin staan ook vroeg: op de productpagina snoepte het Font Awesome-font van 156 kB bandbreedte af van de productfoto, waardoor LCP later viel. Lighthouse houdt het gehintte bestand overigens gewoon op de lijst, met 0,0 KiB. Meer over 103 Early Hints, en over waarom een gewone preload van een stylesheet dat je ook al linkt geen zin heeft.
Sla fetchpriority="low" op een stylesheet over
Een veelgehoorde tip is om CSS die het eerste scherm niet nodig heeft fetchpriority="low" mee te geven. Dat is een anti-pattern. In de head verlaagt Chrome dat stylesheet met één stapje, van hoogste naar hoge prioriteit, en wacht er voor de first paint nog steeds op. Lighthouse bepaalt in zijn simulatie op basis van prioriteit wat blokkeert, en behandelt het bestand daardoor alsof het weg is: geen Duration en een snellere gesimuleerde FCP.
Ik heb dit op vier stylesheets getest. Lighthouse met gesimuleerde throttling liet de FCP dalen van 2,0 s naar 1,60 s en verhoogde de score van 96 naar 97. In Chrome verschoof de first paint van 2.122 ms naar 2.114 ms, dus je bezoekers wachten nog precies even lang.

Lighthouse, gesimuleerde throttling

Chrome op de telefoon, dezelfde pagina
Op het moment dat Lighthouse als FCP geeft, toont Chrome nog steeds een wit scherm.
Hoe controleer je of de fix heeft gewerkt?
Draai Lighthouse opnieuw op mobiel. Met de fix ging de score van 96 naar 100 en de gesimuleerde FCP van 2,0 s naar 1,1 s. De insight ging van rood met twaalf bestanden naar oranje met nog maar één: het stylesheet van 6,6 kB.

Voor

Na
Lighthouse 13.5.0, mobiel, gesimuleerde throttling.
Draai de test drie keer en neem het middelste resultaat: op dezelfde pagina verschilde de gesimuleerde FCP tussen runs tot wel 1,5 s.
CoreDash toont de FCP en LCP render delay vanaf de eerstvolgende bezoeken. CrUX en Search Console tonen de afgelopen 28 dagen, dus twee weken na de fix bestaat de helft van wat ze laten zien nog uit de oude site.
Wil je weten of blokkerende CSS jouw mobiele FCP of LCP in het rood houdt? In een Core Web Vitals power hour openen we samen je field data en een trace van je pagina, en ga je weg met de bestanden om te trimmen, combineren of deferren.
CoreDash heeft MCP ingebouwd.
Hang 'm aan Claude of een andere AI agent. Vraag waarom je INP vorige dinsdag piekte.
Zo werkt het