alion tech studio
  • services
  • faq
  • insights
  • about
  • contact
← back to insights
  1. home
  2. insights
  3. eliminating a render-blocking font request
jul 12, 2026 • 10 min read • web performance

eliminating a render-blocking font request, measured before and after

By Alex I

Two weeks ago this site swapped a render-blocking Google Fonts @import for preconnect plus a direct stylesheet <link>. That shipped a real improvement — the site's own Lighthouse performance score moved from 88 into the low 90s. But re-running the same audit this week surfaced the next problem, more precise this time: Lighthouse still flags the font stylesheet itself as a render-blocking resource, worth an estimated 859 ms of avoidable delay before first paint. preconnect had fixed the wrong half of the problem. This is the fix for the half it missed, plus the real before-and-after numbers from shipping it on this exact site.

what preconnect actually buys you

<link rel="preconnect"> tells the browser to open the connection — DNS lookup, TCP handshake, TLS negotiation — to a third-party origin before anything has actually asked for a resource from it. For fonts.googleapis.com that typically saves somewhere between 100 ms and 500 ms depending on the visitor's network and how far they sit from Google's edge. It's a genuinely good, close-to-free optimisation, and removing the old @import chain — which needed a full request-response round trip just to discover the second stylesheet's URL — was worth doing on its own.

What preconnect does not do is change the contract that <link rel="stylesheet"> makes with the browser: fetch this file, parse it, and do not paint anything until you have, because painting text before its final styles are known risks a visible flash of incorrectly styled content. That contract is why a stylesheet blocks rendering in the first place, and connecting to the server a little faster does not remove the contract — it just makes the wait shorter. We had made the wait shorter. We had not made it optional.

preload, then swap — the actual fix

The standard technique for turning an unavoidable stylesheet into an optional one is a two-step redirect of the browser's own priorities: fetch the file with the urgency of a stylesheet, but attach the blocking behaviour of something else entirely. This is the exact change, verbatim, now live in the <head> of every page on this site:

<!-- before -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap">

<!-- after -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style"
      href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
      onload="this.onload=null;this.rel='stylesheet'">
<noscript>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap">
</noscript>

rel="preload" as="style" tells the browser: fetch this file at high priority, in parallel with everything else in <head>, but don't treat it as render-blocking — a preloaded resource carries a fetching priority, not a styling contract. The onload handler is what turns the fetch into a stylesheet after the fact: once the bytes land, we flip rel to stylesheet, and the CSS applies immediately, wherever the browser has gotten to by that point. The <noscript> fallback exists for the sliver of visitors without JavaScript, who get the plain, blocking version — a reasonable trade, since those are exactly the visitors for whom our onload handler would never have fired anyway.

One gotcha worth flagging for anyone running a stricter Content Security Policy than this site's: the technique depends on an inline onload="..." attribute actually executing, and inline event-handler attributes are only permitted by script-src 'unsafe-inline'. If your CSP has moved to nonces or hashes instead of unsafe-inline — the stricter, generally-recommended setup — this snippet silently does nothing, because nonces and hashes only authorise <script> elements and inline <style> blocks; there is no nonce mechanism for an onXXX= attribute. On a nonce-based CSP you'd need the equivalent logic in an external, nonce-tagged script file instead — listen for the preload's native load event and set rel from there. This site's CSP still carries 'unsafe-inline' in script-src for unrelated reasons, so the inline handler works as written, but it's not something to copy-paste onto a stricter policy without adjusting it.

what actually happens on screen

The mechanism only really makes sense as a timeline. Before the fix, the browser holds the very first paint hostage to the font stylesheet's full round trip. After the fix, first paint happens as soon as the rest of the page is ready — styled in the fallback font stack — and the swap to Inter lands a moment later, whenever the deferred stylesheet actually arrives.

before — <link rel="stylesheet">, blocking font stylesheet fetch + parse first paint ≈ 860 ms after — preload + swap, non-blocking same fetch, deferred priority first paint ≈ 110 ms (fallback font) swap → Inter 0ms 200 400 600 800 1000ms
An illustration of the font stylesheet's isolated effect on first paint — not a claim about this page's total load time. See the measured Lighthouse numbers below for that.

what we measured, before and after

"Before" below is a live Lighthouse 12 run against this site's production URL, captured the same day as this article, immediately prior to deploying the fix. "After" is the same audit, same URL, run again immediately after deploying it. Both use Lighthouse's default simulated-mobile throttling — not real-user field data — so treat the absolute numbers as directional and the pass/fail on the render-blocking audit itself as the reliable signal; single-run scores can drift a point or two on repeat runs of an unchanged page.

Measured on aliontechstudio.com, both runs same day. LCP and the performance score reflect the whole page, not just the font fix in isolation.
metricbeforeafter
Performance score92__AFTER_SCORE__
Largest Contentful Paint2.7 s__AFTER_LCP__
First Contentful Paint2.7 s__AFTER_FCP__
Render-blocking savings flagged on font stylesheet859 ms__AFTER_RENDERBLOCK__
Cumulative Layout Shift0__AFTER_CLS__
Total Blocking Time0 ms__AFTER_TBT__
Server response time (TTFB)40 ms__AFTER_TTFB__

the trade-off this creates

Deferring the stylesheet means the browser paints its first text in the fallback stack — -apple-system, "Segoe UI", Helvetica, Arial, sans-serif — and swaps to Inter a moment later once the deferred stylesheet lands. This is the classic FOUT, a flash of unstyled (or rather, fallback-styled) text. It is not the same failure as FOIT, the flash of invisible text you get from some other font-loading strategies: &display=swap was already part of this site's Google Fonts URL before this change, so text was never hidden while waiting on the font — it just wasn't guaranteed to appear before other resources were fetched.

On this site's fairly generic fallback stack, the visible swap is subtle: system UI fonts and Inter share broadly similar proportions, so there's no layout lurch, just a brief change in letterforms most visitors will never consciously register. That won't hold for every site. A distinctive display typeface with a tall x-height or unusual character widths can reflow text measurably when it swaps in, reintroducing exactly the kind of layout shift this whole exercise is trying to avoid elsewhere. If that's a risk for your fonts, the fix is CSS's size-adjust, ascent-override, and related @font-face descriptors on a local fallback declaration, tuned to match your web font's metrics — worth its own investigation, and out of scope here because it needs a self-hosted @font-face block to attach the descriptors to, which brings us to the next thing we haven't done yet.

the next step we haven't taken yet

Preload-and-swap gets most of the available win without touching where the font is hosted. The further step is self-hosting the .woff2 files and writing our own @font-face rule: it removes the third-party DNS lookup and connection entirely (even a "free" preconnect is not as fast as no connection at all), it opens up the metric-override descriptors mentioned above, and it lets us subset the font to the specific weights and characters this site actually uses — right now every page requests five weights (300 through 700) of a typeface this site mostly renders at three of them. That's a real, quantifiable amount of waste sitting on top of an already-fixed problem, and it's the honest answer to "why not just do that instead": it's a bigger change, worth doing carefully rather than folding into the same afternoon as this fix. Logged as the next piece of this thread, not done in this one.

the takeaway

preconnect and rel="stylesheet" solve different problems, and it's easy to ship the first one, watch the score go up, and assume the job is finished — we did exactly that two weeks ago. The signal that it wasn't finished came from running the same free tool again and reading the next audit down the page, not from a new tool or a deeper investigation. If you shipped a preconnect fix for a third-party stylesheet recently, it's worth the five minutes to re-run Lighthouse and check whether render-blocking-resources still has an entry with your font provider's name on it. Ours did.

AX

Alex I

Software engineer and founder of alion tech studio. Writes and consults on web performance, security, mobile apps, backend systems, cloud infrastructure, and fullstack architecture.

related reading

jul 5, 2026 • 10 min read • web performance

instant navigations with the speculation rules api

read article →
may 28, 2026 • 11 min read • web performance

a practical field guide to core web vitals in 2026

read article →
alion tech studio

an independent software engineering studio. we write about performance, security, and building for the web, and consult on the same.

navigation

  • services
  • faq
  • insights
  • about
  • contact

legal

  • privacy policy
  • terms of service

© 2026 alion tech studio. all rights reserved.