{"id":"svuxq9awdbz6x1d","title":"Stop Losing Visitors to a Sluggish Website","slug":"stop-losing-visitors-sluggish-website","summary":"You can’t code around the speed of light, so move your logic and SSL handshakes closer to the user to stop wasting time on round-trips.  Caching full HTML…","imageUrl":"https://briancrabtree.me/images/journal-stop-losing-visitors-sluggish-website.webp","category":"UX Theory","date":"11/24/2025","featured":false,"likes":45,"author":"Brian Crabtree","content":"<h2>The Physics of Latency and Edge Delivery</h2>\n\n<p>You cannot beat physics. Light travels at a finite speed, and every millisecond of latency is a physical distance that data must travel. If your origin server is sitting in a data center in Virginia and your user is trying to access your site from a coffee shop in Singapore, there is an unavoidable delay that no amount of code optimization can fix on its own. This is where the geographic distribution of your logic becomes the single most critical architectural decision you make. We used to think of Content Delivery Networks as glorified file servers for images and jQuery files, but that mental model is a decade out of date. Modern architecture demands an Edge-First approach where the logic lives as close to the user as physically possible.</p>\n\n<p>A robust CDN implementation is not just about caching static assets; it is about intelligent routing and edge computation. You should be terminating SSL/TLS handshakes closer to the user to reduce round-trip times. You should be caching entire HTML documents, not just the CSS. When we utilize edge computing platforms like Cloudflare Workers or AWS Lambda@Edge, we are effectively moving the backend into the user's backyard. This allows us to modify headers, resize images on the fly, and even personalize content without ever hitting the slow, centralized origin server. The goal is to make the origin server bored. If your origin server is sweating, you have likely failed at the architectural level to distribute the load to the edge where it belongs.</p>\n\n<pre><code>// LCP target: identify hero image or H1 block in Performance panel\n// Preload LCP resource; defer non-critical JS</code></pre>\n\n<h2>Protocols and The Death of Head of Line Blocking</h2>\n\n<p>For years we were shackled by HTTP/1.1 and its limitation of six concurrent connections per origin. We built elaborate hacks to get around this, like domain sharding and CSS sprites, adding complexity just to trick the browser into downloading files faster. HTTP/2 arrived and solved the concurrency issue with multiplexing, allowing multiple requests and responses to fly over a single TCP connection. It was a massive step forward, but it still suffered from TCP Head-of-Line blocking. If a single packet got dropped in the chaos of a cellular network, the entire connection stalled while the operating system waited for the retransmission. It was a traffic jam caused by a single stalled car.</p>\n\n<p>This is why we must aggressively adopt HTTP/3 and QUIC. By moving the transport layer to UDP, we eliminate that bottleneck entirely. In a lossy network environment—which is basically every mobile network in existence—HTTP/3 ensures that independent streams of data are not held hostage by packet loss in other streams. This is not just a server configuration tweak; it is a fundamental shift in how data moves across the wire. Ensuring your infrastructure supports these modern protocols is the difference between a site that feels snappy on 4G and one that hangs indefinitely. Stop relying on default server configs from five years ago and ensure your TLS termination proxies are speaking the latest language of the web.</p>\n\n<h2>The JavaScript Payload Crisis</h2>\n\n<p>We need to have a serious conversation about JavaScript. It is the single most expensive resource on the web, and we treat it like it's free. A kilobyte of JavaScript is not equal to a kilobyte of JPEG. An image just needs to be decoded and painted; JavaScript has to be downloaded, parsed, compiled, and executed. This all happens on the main thread, the same thread responsible for responding to user interactions. When you ship a massive Single Page Application (SPA) bundle to a mid-range Android device, you are essentially locking up the browser for seconds at a time. The user taps a button, and nothing happens because the CPU is screaming trying to hydrate a complex React tree that shouldn't have been there in the first place.</p>\n\n<p>The solution is aggressive payload reduction. We must stop importing entire libraries when we only need a single utility function. Tree-shaking is a start, but it is not a silver bullet. We need to look at code splitting not as a nice-to-have, but as a mandatory pattern. Route-based splitting is the bare minimum. We should be looking at component-level splitting and ensuring that code is only loaded when the user actually needs it. If a user never opens the modal, they should never download the code for the modal. Furthermore, we must embrace the platform. Browsers now have native support for lazy loading, complex layout calculations, and animations. Stop using JavaScript to do what CSS can do for free. If you can solve a problem with HTML and CSS, doing it in JavaScript is technical debt.</p>\n\n<h2>Rendering Strategies and The Return to Static</h2>\n\n<p>The industry swung too far toward client-side rendering, and now we are paying the price with poor SEO and terrible time-to-interactive metrics. We forgot that the browser's primary job is to render HTML, and it is incredibly good at it. The most performant website is one that sends a pre-calculated HTML string that the browser can paint immediately. This brings us back to Static Site Generation (SSG) and Server-Side Rendering (SSR). By doing the heavy lifting on the server—or better yet, at build time—we relieve the client device of the burden of constructing the interface from raw data.</p>\n\n<p>However, we must be careful not to fall into the hydration trap, where the server sends HTML, but the browser then has to re-execute all the JavaScript to make it interactive, causing a weird \"uncanny valley\" where the site looks ready but isn't. The modern approach involves islands architecture or partial hydration, where vast swathes of the page remain static HTML and only the truly interactive bits are hydrated with JavaScript. This reduces the execution cost significantly. We should be striving for a world where the baseline experience works without JavaScript at all, and the scripting merely enhances the functionality. It makes the site more resilient, more accessible, and infinitely faster.</p>\n\n<h2>Asset Optimization and Caching Disciplines</h2>\n\n<p>Finally, we cannot ignore the basics of asset hygiene. Images still account for the bulk of web traffic, yet I see production sites serving uncompressed PNGs daily. It is negligence. We have modern formats like AVIF and WebP that offer superior compression at smaller file sizes. There is no excuse for not using responsive image syntax to serve the correct size based on the user's viewport. Why send a 4000-pixel wide hero image to an iPhone SE? It wastes bandwidth, battery, and memory. Automate this in your build pipeline so that no human being has the opportunity to mess it up.</p>\n\n<p>Surrounding all of this is the discipline of caching. Caching is often cited as one of the hardest problems in computer science, but mostly because people are lazy about defining cache-control headers. You need a tiered caching strategy. Immutable assets with hashed filenames should be cached forever. API responses should have appropriate `max-age` directives. Service Workers should be employed to create an offline-first experience, acting as a client-side proxy that insulates the user from network jitter. When you combine aggressive edge caching with a smart Service Worker strategy, you create an experience that feels instantaneous because, for the user, the network request never actually happens.</p>\n\n<p>Speed is not magic. It is engineering. It is a series of deliberate, often boring choices to prioritize the user over the developer experience. It requires understanding the full stack, from the TCP handshake to the pixel pipeline. Stop chasing trends and start building systems that respect the physics of the network and the patience of your users. For a related angle I keep coming back to, see <a href=\"/journal/why-pagespeed-scores-change-every-run/\">Why PageSpeed Scores Change Every Run (And What to Fix First)</a>.</p>","tags":["React Server Components","Next.js App Router","Web Vitals Optimization","JavaScript Bundling","API Design Principles"],"views":108}