Web fonts are one of the sneakiest performance problems on WordPress sites. Your page looks fine in testing, but real visitors see text popping in late, shifting the layout, or, worst of all, a blank space where your headline should be. Google’s PageSpeed Insights flags this under “Ensure text remains visible during webfont load” and “Preload key requests,” and it quietly damages your Cumulative Layout Shift (CLS) score.
The fix has three parts, and they work best together: self-host your fonts, preload the critical ones, and set font-display correctly. This guide walks through all three, with paths for OMGF, LiteSpeed Cache, WP Rocket, and manual code.
The short version: stop loading fonts from Google’s servers on every page view. Host them on your own server, preload only the weights you actually use above the fold, and add
font-display: swapso text never goes invisible.
Why Fonts Hurt Your Site (The 60-Second Version)
When a browser encounters text styled with a web font, it has to fetch that font file before it can render the text in that font. What happens while it waits depends on the font-display setting:
- FOIT (Flash of Invisible Text): the browser hides the text until the font arrives. Visitors see blank spaces. This is the default behavior with no
font-displayset, and the worst outcome. - FOUT (Flash of Unstyled Text): the browser shows a fallback font first, then swaps when the web font arrives. Better than invisible text, but the swap can shift layout.
- Layout shift: if the fallback font and the web font have different metrics, lines reflow when the swap happens. That movement is measured as CLS, one of Google’s Core Web Vitals.
On top of that, most WordPress sites load fonts from Google Fonts’ servers, which means extra DNS lookups, extra connections, and a render-blocking stylesheet request before the font file itself even starts downloading. That chain is slow, especially on mobile networks.
Step 1: Self-Host Your Fonts
Self-hosting means downloading the font files and serving them from your own server (or CDN) instead of Google’s. The benefits:
- Fewer connections: no DNS lookup or TLS handshake to fonts.googleapis.com and fonts.gstatic.com.
- Full control: you choose exactly which weights and formats to serve, and you can set long browser caching headers on them.
- Privacy bonus: no request to Google’s servers on every page view, which some privacy regulations appreciate.
You only need WOFF2 format. Every modern browser supports it, and it’s the smallest. You also only need the weights you actually use. If your design uses Regular (400) and Bold (700), don’t ship Thin, Light, Medium, SemiBold and Black “just in case.” Each weight is a separate file.
The OMGF path (easiest)
OMGF (Optimize My Google Fonts) is a free plugin that automates self-hosting: it detects the Google Fonts your site loads, downloads the WOFF2 files to your server, and rewrites the references. Install it, run the detection, and your Google Fonts requests become local requests. It also handles font-display and preloading options in its settings.
The LiteSpeed Cache path
If you run LiteSpeed Cache, its Page Optimization settings include font options: it can host Google Fonts locally and apply font-display: swap automatically. Check under Page Optimization → Tuning/Fonts depending on your version. This keeps everything in one plugin if you’re already using LiteSpeed Cache.
The manual path
Download the WOFF2 files (Google Fonts lets you download font families directly, or use a tool like google-webfonts-helper), upload them to /wp-content/uploads/fonts/ or your theme’s fonts directory, and reference them with @font-face in your CSS:
@font-face{
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/wp-content/uploads/fonts/inter-400.woff2') format('woff2');
}
Manual takes more work but gives you complete control, worth it if you only use one or two weights.
Step 2: Preload Only the Critical Weights
Preloading tells the browser: “you will definitely need this file, start downloading it now, don’t wait until you discover it in the CSS.” For fonts, this cuts out the discovery delay entirely.
Add a preload link in your <head> for each critical font file:
<link rel="preload" href="/wp-content/uploads/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
Three rules that matter:
- Preload only what renders above the fold. Usually that’s your body text weight (400) and maybe your heading weight (700). Preloading five weights you don’t need yet just wastes bandwidth and can actually hurt performance by competing with more important requests.
- The
crossoriginattribute is required for fonts, even on your own domain. Without it, the browser downloads the font twice: once for the preload, once for actual use. This is the most common preloading mistake. - Match the URL exactly. The preload
hrefmust be character-identical to the URL in your@font-facesrc. A mismatch means the preload is ignored.
Where to add it: your theme’s header.php before wp_head(), a child theme, or a plugin like WP Rocket (which has a “preload fonts” field in its settings). OMGF can also generate these for you.
What about preconnect?
You’ll see advice to add <link rel="preconnect" href="https://fonts.gstatic.com">. That’s for sites still loading fonts from Google. Once you self-host, preconnect to Google Fonts is pointless, so remove it. One less thing in your <head>.
Step 3: Set font-display: swap
font-display: swap tells the browser: show the fallback font immediately, then swap in the web font when it arrives. Text is never invisible. This single declaration fixes the “Ensure text remains visible during webfont load” warning.
Set it in every @font-face block:
font-display: swap;
If you used OMGF or LiteSpeed Cache’s font features, this is likely already applied. Verify rather than assume. If your theme bundles its own @font-face declarations (many premium themes do), you may need to add it via custom CSS or a child theme.
Minimizing the swap shift: swap fixes invisible text but the font swap can still nudge layout. Reduce it by choosing fallback fonts with similar metrics (e.g., a system sans-serif stack for a geometric sans web font) and, if you’re thorough, using size-adjust in your @font-face to normalize the fallback’s sizing. That last technique is advanced. The first three steps get you 90% of the benefit.
Putting It Together: The Complete Checklist
- Audit: open DevTools → Network tab → filter by “Font”. Note every font file your homepage loads, its weight, and where it’s served from.
- Trim: decide which weights you actually use. Remove the rest (theme settings, OMGF, or manual CSS cleanup).
- Self-host: OMGF, LiteSpeed Cache, or manual — get the files on your server.
- Preload: add
preloadlinks for the 1–2 critical weights, withcrossorigin, exact URLs. - font-display: confirm
swapis set on every@font-face. - Cache headers: make sure font files are served with long cache lifetimes. They’re static assets that never change. Our browser caching guide covers the setup.
- CDN check: if you serve fonts through a CDN, test whether your CDN is actually working. Fonts silently falling back to origin is a common misconfiguration.
Then re-run PageSpeed Insights and watch the font warnings disappear.
How to Verify It Worked
- DevTools Network tab: reload and filter by “Font”. You should see fewer files, served from your own domain, with the critical ones showing high priority (preload) and loading early in the waterfall.
- PageSpeed Insights: the “Ensure text remains visible during webfont load” and “Preload key requests” (font-related) diagnostics should clear.
- The eye test: throttle your connection to “Fast 3G” in DevTools and reload. Text should appear immediately in a fallback font and swap smoothly, with no blank headline gaps or violent layout jumps.
For the full picture, run through our 20-step WordPress performance checklist. Fonts are one piece of a fast site, not the whole puzzle.
Frequently Asked Questions
Should I preload all my font weights?
No — preload only the 1–2 weights used above the fold (typically body 400 and heading 700). Preloading everything wastes bandwidth on files the visitor may never need and can delay more critical requests. Non-critical weights load normally via CSS and that’s fine.
Is self-hosting fonts always faster than Google Fonts?
For most WordPress sites, yes — it removes DNS lookups, extra TLS handshakes, and a render-blocking stylesheet request to Google’s servers. Google Fonts has improved over the years, but a self-hosted WOFF2 with preload and long cache headers is still the fastest setup you control.
What does font-display: swap actually do?
It tells the browser to render text immediately in a fallback font and swap in the web font when it finishes downloading. Without it, browsers hide text until the font arrives (invisible text). With it, your content is always readable — the worst case is a brief font change, not a blank page.
Why is my preload not working? The font still loads late.
The three usual suspects: (1) missing crossorigin attribute — the browser then fetches the font twice; (2) URL mismatch between the preload href and the @font-face src — they must be identical; (3) preloading a weight that isn’t actually used above the fold, so it doesn’t matter. Check all three.
Do I still need OMGF if I use WP Rocket or LiteSpeed Cache?
Not necessarily. WP Rocket can preload fonts and optimize Google Fonts loading; LiteSpeed Cache can host Google Fonts locally. OMGF is the specialist tool — use it if your caching plugin’s font features don’t cover your setup, or if you want the most automated self-hosting workflow. Don’t run overlapping font features in two plugins.
The Bottom Line
Font optimization is a three-step job: self-host (kill the Google round-trips), preload the critical weights (kill the discovery delay), and set font-display: swap (kill invisible text). Do all three and fonts go from a CLS liability to a non-issue.
It’s unglamorous work. Nobody visits your site for the fonts, but visitors absolutely notice when text pops, shifts, or vanishes. Get the fonts right and your site feels instant in a way no caching plugin alone can deliver.

