2
4 Comments

I deleted my CDN and got a 30% faster first paint. Here's the whole receipt.

I build Pheidi, an adaptive running training app, alone. A few weeks ago I went looking for slow spots and found one I'd been shipping for months without noticing: my CSS was loading from three different CDNs, and that was quietly costing me a third of my first paint.

I want to show you the actual numbers and the actual decision, because "self-host your assets" is advice you've heard, and I never believed it until I measured it on my own app.

What I was doing (the default everyone copies)

The top of my app pulled:

  • Bootstrap CSS from jsDelivr
  • Font Awesome CSS from cdnjs
  • Plus Jakarta Sans from Google Fonts (which is really two hops: CSS from one Google host, fonts from a second)

Three providers, four external hostnames. It's the default in every tutorial, and the old argument for it (shared browser cache across sites) died when browsers partitioned their caches by site for privacy. So I was paying a cost for a benefit that no longer exists.

The cost nobody mentions

CSS is render-blocking: the browser won't paint until it has the stylesheets. Fine. But a stylesheet on someone else's domain isn't just a download. Before the first byte, the browser has to open a connection to that host: DNS lookup, TCP handshake, TLS handshake. Roughly three round trips to a server it's never talked to this session, and it's all on the critical path because the paint is waiting on it.

I had three of those. On my laptop on wifi, invisible. On a runner's phone at a trailhead with one bar, that's real time staring at a blank screen.

The fix was a delete

I downloaded the files and served them from my own origin. Replaced the CDN tags with local paths, dropped the Google Fonts link entirely. Two things worth stealing:

  1. I Brotli-compressed the self-hosted assets, because my Linux host doesn't do it automatically like a CDN would. The critical Bootstrap + Font Awesome + JS went from about 382 KB raw to 54 KB on the wire.
  2. I added a test that fails the build if any http(s):// asset URL ever reappears in the host page, so a stray CDN tag can't creep back in later.

The measurement (Lighthouse, Slow 4G, median of 3)

I measured the self-hosted version, then temporarily swapped the stylesheets back to their CDN URLs, measured again, reverted.

I even gave the CDN baseline preconnect hints to warm those connections, which most people don't bother with, so the real-world gap is probably wider. The 30% is a floor.

The part I won't hide

Largest Contentful Paint didn't move. It sat around 3.3s before and after, because my main content waits on the app's interactive shell booting (framework JS + a connection back to the server), not on CSS. Self-hosting a stylesheet does nothing for that. Different bottleneck, different fix, which I'm chasing separately. If I'd only shown you the first-paint win I'd be selling you a clean story that isn't true.

The rule I'd take from it

Anything on your critical render path should live on your own origin. Render-blocking means the user stares at nothing until it arrives, so don't make that depend on a handshake to a server you don't control, for a cache benefit browsers took away. Own the critical path, rent the commodity plumbing (I still rent email delivery and hosting).

And measure before you trust yourself. It loaded fine for me, on my machine. It wasn't fine for the person it was actually for.

on July 1, 2026
  1. 1

    This is a great example of isolating perceived performance improvements vs actual bottlenecks. Most “speed optimizations” get attributed to the most visible change (CDNs in this case), but the real value here is the discipline of tracing what actually sits on the critical render path. The fact that LCP didn’t move is arguably the most important insight in the entire post—it shows you didn’t just optimize, you diagnosed correctly.

  2. 1

    Nice, deleting a CDN and getting a faster first paint is a great find. On the infra/security side — I do quick bug and security passes on production sites, recently fixed a live private-key leak in a production repo (GitHub: piush365). Happy to take a look at your setup if useful.

  3. 1

    That’s a really interesting result. I’m curious, did removing the CDN have any negative impact on users from other countries, or was the speed improvement worth the trade-off?

    1. 1

      You know, I didn't actually test this out. If this were a problem, I'd probably deploy the web app in other countries as well.