Font source
Where fonts come from: self-hosted, Google Fonts, or other third parties.
At a glance the headline numbers for Font source
Where fonts come from: self-hosted, Google Fonts, or other third parties.
53.1% of web fonts are self-hosted. Google still serves 22.9%.
The font source mix who uses what, and how fast each group loads
Font source. On the fleet: 53.1% self hosted, 22.9% google, 20.2% third party.
Self hosted leads by count (53.1%) and by bytes (63.7%). computed
Passing LCP per bucket every category and count level at once - color is the pass rate
Each row is a category, each column its own count bucket (few on the left, many on the right); the cell is the share of those sites passing LCP.
Google swings the hardest: 86% of sites pass LCP with few, 76% with many. computed
Few vs many - does quantity cost LCP? the pass rate with few vs many of each category
Per category: the pass rate among pages with FEW of it (hollow ring) against pages with MANY (solid dot), worst trend first. Thin buckets are excluded from the endpoints.
More Google costs the most: the LCP pass rate falls from 86% with few to 76% with many. computed
Why this matters for the Core Web Vitals, and where to start fixing it
Where fonts come from decides how many connections sit on the render path. Google Fonts costs two extra domains: one serves the CSS, another serves the font files. Both need DNS, TCP and TLS before text can settle. Self-hosted fonts ride the connection that is already open and follow your cache rules.
The old argument for the shared Google cache is dead: browsers partition their caches per site, so nobody arrives with your font already cached. Self-hosting is strictly better, and it is a one-time move.
Chrome field data from 189,915 sites, representing millions of real page loads. How we measured.
Live queries (2) — admin only
SELECT 1 AS ok FROM site_metric_bags LIMIT 0;
WITH flat AS (
SELECT b.cat, b.n, b.size, s.crux."desktop".lcp AS cwv_val
FROM site_metric_bags b
JOIN sites s ON s.origin = b.origin
WHERE b.path = 'fonts.source'
AND b.cat IS NOT NULL
),
totals AS (
SELECT cat,
SUM(n) AS fleet_n,
SUM(size) AS fleet_size,
COUNT(*) FILTER (WHERE n IS NOT NULL) AS sample,
COUNT(*) FILTER (WHERE n > 0) AS with_any,
quantile_disc(n, 0.95) AS p95,
COUNT(*) AS n_rows
FROM flat
GROUP BY cat
HAVING COUNT(*) >= (SELECT COUNT(DISTINCT origin) FROM site_metric_bags WHERE path = 'fonts.source') * 0.005
),
widths AS (
SELECT *,
GREATEST(1, CAST(round(COALESCE(p95, 0)) AS INTEGER)) AS cap,
LEAST(GREATEST(1, CAST(round(COALESCE(p95, 0)) AS INTEGER)), 12 - 1) AS regular_buckets
FROM totals
),
widths2 AS (
SELECT *,
GREATEST(1, CAST(ceil(cap * 1.0 / regular_buckets) AS INTEGER)) AS width
FROM widths
),
binned AS (
SELECT f.cat, f.cwv_val, w.fleet_n, w.fleet_size, w.sample, w.with_any, w.width, w.regular_buckets,
CASE WHEN f.n IS NULL THEN NULL
ELSE LEAST(CAST(FLOOR(f.n * 1.0 / w.width) AS INTEGER), w.regular_buckets)
END AS bucket_idx
FROM flat f
JOIN widths2 w ON f.cat = w.cat
)
SELECT cat, fleet_n, fleet_size, sample, with_any, width, regular_buckets,
bucket_idx,
COUNT(*) AS n,
quantile_disc(cwv_val, 0.5) AS median,
COUNT(*) FILTER (WHERE cwv_val IS NOT NULL) AS total_cwv,
COUNT(*) FILTER (WHERE cwv_val IS NOT NULL AND cwv_val <= 2500) AS good
FROM binned
WHERE bucket_idx IS NOT NULL
GROUP BY cat, fleet_n, fleet_size, sample, with_any, width, regular_buckets, bucket_idx
ORDER BY cat, bucket_idx;