1
At a glance the headline numbers for Ad networks impact
Ad networks ranked by performance impact.
6
Providers ranked
After min-sites filter
17,864
Sites in sample
Combined across all items
0.06
Best CLS (p75)
Google AdSense
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
2
The ranking sorted by CLS at p75, fastest first
| # | Provider | CLS p75 | Passing | Sites | |
|---|---|---|---|---|---|
| 1 | Google AdSense | 0.06 | 84.1% | 12,952 | |
| 2 | Google Publisher Tag/GAM | 0.10 | 75.4% | 2,840 | |
| 3 | Prebid.js | 0.12 | 71.8% | 841 | |
| 4 | Amazon Publisher Services | 0.13 | 68.9% | 299 | |
| 5 | Criteo | 0.13 | 72.3% | 450 | |
| 6 | Taboola | 0.14 | 68.3% | 482 |
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
3
Passing CLS per provider which group passes the CLS most often
ProviderSitesPassing CLSp75
Google AdSense 6.8%
12,952
0.06
Outbrain 0%
25
0.07
Index Exchange 0%
45
0.09
Google Publisher Tag/GAM 1.5%
2,840
0.10
Criteo 0.2%
450
0.13
Prebid.js 0.4%
841
0.12
Amazon Publisher Services 0.2%
299
0.13
Taboola 0.3%
482
0.14
Media.net 0%
21
0.92
Good
Needs Improvement
Poor
Sorted best-passing first · median colored by its own rating · pass = good CLS (0.1 at p75) · one value per site
84% of Google AdSense sites pass CLS. Taboola trails 16 points behind, leaving 32% of its sites failing. computed
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
4
All five vitals at once the whole category without toggling - cell is the pass rate, small number the p75
LCP
INP
CLS
FCP
TTFB
Google Publisher Tag/GAM 1.5%
822.2s
83177ms
750.10
821.6s
68925ms
Amazon Publisher Services 0.2%
812.2s
75197ms
690.13
821.6s
77745ms
Criteo 0.2%
712.6s
73208ms
720.13
712.0s
541.2s
Outbrain 0%
832.0s
87145ms
830.07
911.3s
78609ms
Index Exchange 0%
862.2s
93152ms
790.09
841.5s
79709ms
Google AdSense 6.8%
782.4s
84173ms
840.06
721.9s
531.3s
Prebid.js 0.4%
842.2s
83171ms
720.12
841.6s
73843ms
Taboola 0.3%
752.5s
77194ms
680.14
731.9s
66967ms
Media.net 0%
353.7s
92178ms
350.92
292.6s
131.6s
60%95%+ passing
Cell: pass rate, small number = p75 · faded rows: under 100 sites
One row per provider, one column per vital - the cell is the share of sites passing, the small number the p75. No toggling needed to see where the category actually differs.
Google AdSense leads on CLS: 84% of its sites pass. Taboola trails at 68%. computed
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
Related signals
JS libraries (25) →
Email marketing (8) →
Font services (4) →
CDN (16) →
Chrome field data from 189,915 sites, representing millions of real page loads · How we measured
Chrome field data from 189,915 sites, representing millions of real page loads. How we measured.
Live queries (3) — admin only
Query #1:
35.0 ms
SELECT 1 AS ok FROM site_providers LIMIT 0;
Query #2:
375.5 ms
SELECT CAST(p.key AS VARCHAR) AS e, COUNT(*) AS sites,
COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL) AS all_lcp_n,
quantile_disc(s.crux."all".lcp, 0.5) AS all_lcp_median,
quantile_disc(s.crux."all".lcp, 0.75) AS all_lcp_p75,
COUNT(*) FILTER (WHERE s.crux."all".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_good,
COUNT(*) FILTER (WHERE s.crux."all".lcp > 2500 AND s.crux."all".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_ni,
COUNT(*) FILTER (WHERE s.crux."all".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_poor,
COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL) AS all_inp_n,
quantile_disc(s.crux."all".inp, 0.5) AS all_inp_median,
quantile_disc(s.crux."all".inp, 0.75) AS all_inp_p75,
COUNT(*) FILTER (WHERE s.crux."all".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_good,
COUNT(*) FILTER (WHERE s.crux."all".inp > 200 AND s.crux."all".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_ni,
COUNT(*) FILTER (WHERE s.crux."all".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_poor,
COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL) AS all_cls_n,
quantile_disc(s.crux."all".cls, 0.5) AS all_cls_median,
quantile_disc(s.crux."all".cls, 0.75) AS all_cls_p75,
COUNT(*) FILTER (WHERE s.crux."all".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_good,
COUNT(*) FILTER (WHERE s.crux."all".cls > 0.1 AND s.crux."all".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_ni,
COUNT(*) FILTER (WHERE s.crux."all".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_poor,
COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL) AS all_fcp_n,
quantile_disc(s.crux."all".fcp, 0.5) AS all_fcp_median,
quantile_disc(s.crux."all".fcp, 0.75) AS all_fcp_p75,
COUNT(*) FILTER (WHERE s.crux."all".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_good,
COUNT(*) FILTER (WHERE s.crux."all".fcp > 1800 AND s.crux."all".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_ni,
COUNT(*) FILTER (WHERE s.crux."all".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_poor,
COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL) AS all_ttfb_n,
quantile_disc(s.crux."all".ttfb, 0.5) AS all_ttfb_median,
quantile_disc(s.crux."all".ttfb, 0.75) AS all_ttfb_p75,
COUNT(*) FILTER (WHERE s.crux."all".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_good,
COUNT(*) FILTER (WHERE s.crux."all".ttfb > 800 AND s.crux."all".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_ni,
COUNT(*) FILTER (WHERE s.crux."all".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_poor,
COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL) AS phone_lcp_n,
quantile_disc(s.crux."phone".lcp, 0.5) AS phone_lcp_median,
quantile_disc(s.crux."phone".lcp, 0.75) AS phone_lcp_p75,
COUNT(*) FILTER (WHERE s.crux."phone".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_good,
COUNT(*) FILTER (WHERE s.crux."phone".lcp > 2500 AND s.crux."phone".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_ni,
COUNT(*) FILTER (WHERE s.crux."phone".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_poor,
COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL) AS phone_inp_n,
quantile_disc(s.crux."phone".inp, 0.5) AS phone_inp_median,
quantile_disc(s.crux."phone".inp, 0.75) AS phone_inp_p75,
COUNT(*) FILTER (WHERE s.crux."phone".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_good,
COUNT(*) FILTER (WHERE s.crux."phone".inp > 200 AND s.crux."phone".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_ni,
COUNT(*) FILTER (WHERE s.crux."phone".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_poor,
COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL) AS phone_cls_n,
quantile_disc(s.crux."phone".cls, 0.5) AS phone_cls_median,
quantile_disc(s.crux."phone".cls, 0.75) AS phone_cls_p75,
COUNT(*) FILTER (WHERE s.crux."phone".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_good,
COUNT(*) FILTER (WHERE s.crux."phone".cls > 0.1 AND s.crux."phone".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_ni,
COUNT(*) FILTER (WHERE s.crux."phone".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_poor,
COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL) AS phone_fcp_n,
quantile_disc(s.crux."phone".fcp, 0.5) AS phone_fcp_median,
quantile_disc(s.crux."phone".fcp, 0.75) AS phone_fcp_p75,
COUNT(*) FILTER (WHERE s.crux."phone".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_good,
COUNT(*) FILTER (WHERE s.crux."phone".fcp > 1800 AND s.crux."phone".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_ni,
COUNT(*) FILTER (WHERE s.crux."phone".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_poor,
COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL) AS phone_ttfb_n,
quantile_disc(s.crux."phone".ttfb, 0.5) AS phone_ttfb_median,
quantile_disc(s.crux."phone".ttfb, 0.75) AS phone_ttfb_p75,
COUNT(*) FILTER (WHERE s.crux."phone".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_good,
COUNT(*) FILTER (WHERE s.crux."phone".ttfb > 800 AND s.crux."phone".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_ni,
COUNT(*) FILTER (WHERE s.crux."phone".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_poor,
COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL) AS desktop_lcp_n,
quantile_disc(s.crux."desktop".lcp, 0.5) AS desktop_lcp_median,
quantile_disc(s.crux."desktop".lcp, 0.75) AS desktop_lcp_p75,
COUNT(*) FILTER (WHERE s.crux."desktop".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_good,
COUNT(*) FILTER (WHERE s.crux."desktop".lcp > 2500 AND s.crux."desktop".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_ni,
COUNT(*) FILTER (WHERE s.crux."desktop".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_poor,
COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL) AS desktop_inp_n,
quantile_disc(s.crux."desktop".inp, 0.5) AS desktop_inp_median,
quantile_disc(s.crux."desktop".inp, 0.75) AS desktop_inp_p75,
COUNT(*) FILTER (WHERE s.crux."desktop".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_good,
COUNT(*) FILTER (WHERE s.crux."desktop".inp > 200 AND s.crux."desktop".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_ni,
COUNT(*) FILTER (WHERE s.crux."desktop".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_poor,
COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL) AS desktop_cls_n,
quantile_disc(s.crux."desktop".cls, 0.5) AS desktop_cls_median,
quantile_disc(s.crux."desktop".cls, 0.75) AS desktop_cls_p75,
COUNT(*) FILTER (WHERE s.crux."desktop".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_good,
COUNT(*) FILTER (WHERE s.crux."desktop".cls > 0.1 AND s.crux."desktop".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_ni,
COUNT(*) FILTER (WHERE s.crux."desktop".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_poor,
COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL) AS desktop_fcp_n,
quantile_disc(s.crux."desktop".fcp, 0.5) AS desktop_fcp_median,
quantile_disc(s.crux."desktop".fcp, 0.75) AS desktop_fcp_p75,
COUNT(*) FILTER (WHERE s.crux."desktop".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_good,
COUNT(*) FILTER (WHERE s.crux."desktop".fcp > 1800 AND s.crux."desktop".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_ni,
COUNT(*) FILTER (WHERE s.crux."desktop".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_poor,
COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL) AS desktop_ttfb_n,
quantile_disc(s.crux."desktop".ttfb, 0.5) AS desktop_ttfb_median,
quantile_disc(s.crux."desktop".ttfb, 0.75) AS desktop_ttfb_p75,
COUNT(*) FILTER (WHERE s.crux."desktop".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_good,
COUNT(*) FILTER (WHERE s.crux."desktop".ttfb > 800 AND s.crux."desktop".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_ni,
COUNT(*) FILTER (WHERE s.crux."desktop".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_poor
FROM site_providers p
JOIN sites s ON s.origin = p.origin
WHERE p.category = 'ad_networks'
AND p.key IS NOT NULL AND CAST(p.key AS VARCHAR) <> ''
GROUP BY p.key;
Query #3:
28.7 ms
SELECT COUNT(*) AS n FROM sites;
JSON file lookups (5)
20260625/meta.json
0.03 ms
20260625/types.json
0.51 ms
20260625/loaf-scripts.json
0.29 ms
20260625/menu.json
0.11 ms
20260625/providers.json
3.17 ms