Performance

Why a CDN Doesn't Fix Everything: Diagnosing CPU Bottlenecks That Hurt INP

8 min read
Device CPU utilisation chart showing main thread saturation during user interactions

A CDN dramatically improves delivery speed—pages load faster, assets arrive sooner, TTFB drops. But some performance problems persist no matter how fast content arrives. When visitors click a button and the interface freezes for 400ms, the CDN has already done its job. The problem is on the device, in the browser’s main thread, where JavaScript execution blocks responsiveness. Understanding this distinction prevents wasting optimisation effort on the wrong layer.

What a CDN actually optimises

CDNs address network-layer performance: how quickly bytes travel from server to browser. They reduce TTFB by serving from nearby edge locations. They decrease download time by distributing assets globally. They absorb traffic spikes so your origin doesn’t slow down.

These improvements directly affect loading metrics: LCP, TTFB, and resource download time. A page that took 3 seconds to start loading from a distant server might start in 200ms from a nearby CDN edge. That’s a genuine, meaningful improvement.

But CDN improvements stop at the browser’s front door. Once bytes arrive, the browser must parse HTML, execute JavaScript, calculate styles, layout elements, and paint pixels. These operations run on the visitor’s device CPU. A CDN can deliver JavaScript faster, but it can’t make JavaScript execute faster.

Why INP problems survive CDN optimisation

INP measures what happens after the page has loaded—when a user interacts. The click, tap, or keypress triggers JavaScript event handlers, state updates, DOM mutations, and re-rendering. All of this runs on the visitor’s CPU, using the browser’s main thread.

A CDN delivering JavaScript in 50ms instead of 500ms means the page becomes interactive sooner. But once interactive, the responsiveness of each interaction depends entirely on what code runs when the user clicks. If a click handler executes 300ms of synchronous JavaScript, the interaction takes 300ms regardless of how the code arrived.

Third-party scripts compound this. Analytics, advertising, chat widgets, and social embeds downloaded faster via CDN still execute on the main thread. Faster delivery means they start executing sooner—which can actually make INP worse during early interactions if more scripts are competing for CPU time.

Diagnosing CPU-bound INP problems

Open Chrome DevTools Performance panel and record a session that includes interactions. Look at the main thread timeline during your click or tap. Long yellow blocks during or immediately after your interaction indicate CPU-bound processing.

Measure total blocking time (TBT) as a proxy for INP potential. TBT captures how much time the main thread is blocked by long tasks during page load. High TBT indicates a main thread under pressure—interactions during or after load are likely to be slow.

Profile specific interactions. Click a button that feels slow while recording, then examine what executed. The call tree reveals which functions consumed the most time. Often, one library or one handler dominates. Knowing exactly what’s slow focuses optimisation effort.

Test on throttled CPU. DevTools allows 4x or 6x CPU throttling to simulate mid-range devices. A handler taking 50ms on your fast development machine takes 200-300ms on a budget phone. This simulated slowdown reveals INP problems invisible on developer hardware.

Common CPU bottleneck patterns

Heavy framework re-rendering is a frequent culprit. React components re-rendering large lists, Vue computed properties recalculating expensive operations, or complex state management cascades can consume significant CPU time during interactions. The CDN delivered the framework quickly, but the framework’s runtime cost is a device-side problem.

Unoptimised DOM operations cause layout thrashing. Reading layout properties (width, height, position) after making DOM changes forces synchronous layout calculation. Inside a loop, this creates repeated forced layouts that consume CPU aggressively. Batching reads before writes eliminates this pattern.

Large dataset processing in event handlers—filtering thousands of items, sorting arrays, computing aggregations—runs synchronously on the main thread. Moving this to Web Workers or implementing incremental processing keeps the main thread responsive during these operations.

Image decoding and canvas operations can monopolise CPU. Processing images client-side, manipulating canvas elements, or running WebGL operations compete with interaction handlers for main thread time. Offloading to workers or using createImageBitmap for async decoding helps.

The right fix for CPU-bound problems

Reduce JavaScript execution during interactions. Audit what runs when users click. Defer non-visual work: analytics events, background syncing, lazy computations. The user needs visual feedback; everything else can happen after the next paint.

Break synchronous work into chunks. Use scheduler.yield(), setTimeout(0), or requestAnimationFrame to yield the main thread between processing chunks. This allows the browser to process user input and update the display between chunks of work.

Move computation off the main thread. Web Workers run JavaScript in background threads without blocking interactions. Transfer data-processing tasks, complex calculations, and formatting operations to workers. The main thread stays available for user interactions.

Reduce third-party script impact. Audit every script’s CPU cost. Remove scripts with high CPU cost and low value. Defer remaining scripts to load after the page becomes interactive. Use requestIdleCallback for non-critical initialisation.

Optimise rendering. Reduce DOM size, simplify CSS selectors, use CSS containment for isolated components, and prefer compositor-friendly animations (transform, opacity). These reduce the rendering cost after event handlers complete.

When CDN and CPU optimisation work together

The ideal approach addresses both layers. CDN ensures content arrives quickly (good LCP, low TTFB). CPU optimisation ensures interactions respond quickly (good INP). Neither substitutes for the other.

A fast CDN with unoptimised JavaScript delivers: quick initial load, then sluggish interactions. A slow CDN with optimised JavaScript delivers: slow initial load, then responsive interactions once loaded. Neither is acceptable; both layers need attention.

Prioritise based on which metric fails. If LCP fails, CDN and delivery optimisation should come first. If INP fails, client-side CPU optimisation is the priority. If both fail, address them in parallel since they involve different layers and teams.

The practical takeaway

CDNs solve delivery problems. CPU optimisation solves execution problems. INP is primarily an execution problem. When someone says “we added a CDN but INP is still failing,” the diagnosis is clear: the bottleneck is device-side JavaScript execution, not network delivery.

Measure where time is spent. If most interaction latency comes from network delays (rare for interactive pages that are already loaded), CDN improvements help. If most latency comes from JavaScript execution and rendering (common), the fix is in your code, not your infrastructure.

Understanding which layer constrains performance prevents misdirected effort. A team spending months optimising CDN configuration while INP fails due to heavy client-side JavaScript is solving the wrong problem. Match the optimisation to the actual constraint. For a clear diagnosis of whether your performance issues are delivery-side or device-side, a performance assessment can separate CDN concerns from application concerns and recommend targeted fixes.

Related insights