Why Cloud Apps Get Slow


A client of mine runs a small SaaS dashboard for logistics companies, built on Next.js hosted on Vercel, talking to a Node API sitting on Render. Nice small stack, nothing exotic. For months it ran fine.

Then he messaged me saying the app "feels sluggish, but only sometimes," and asked if I could take a look before his biggest customer renewed their contract.

That phrase — "only sometimes" — is usually the most annoying kind of bug report you can get, because it means the problem isn't broken; it's just slow, and slow is way harder to pin down than broken.

I opened the dashboard expecting to spend maybe twenty minutes on it. I ended up spending most of that afternoon, and what I found wasn't one bug. It was four small, boring inefficiencies stacked on top of each other, each one shaving off a little bit of speed until the whole thing felt like it was wading through mud.

Also Read: What Happens When Cloud Resources Are Fully Used


"The Cloud" Isn't One Thing, So "Slow" Isn't One Cause

This is the part people miss first. A cloud app isn't a single machine you can just poke and diagnose. It's a chain: your browser, a CDN, a load balancer, a server (or a serverless function that may or may not even be running yet), a database, and sometimes a handful of third-party APIs stitched in between all of that.

Slowness can start at any single link in that chain, and each one has a completely different fix. That's exactly why "just add more servers" rarely actually solves it — you can throw unlimited compute at a problem that was never a compute problem to begin with.

In my client's case, the slowness wasn't the server being overloaded. It was four separate things quietly adding delay, one after another, on almost every page load.


The Four Things That Were Actually Slowing It Down

  1. Cold starts on the API.
    Render, like a lot of platforms, spins down inactive services to save resources. When nobody had hit the API in a while, the very next request had to wait for the whole backend to boot back up before it could even start doing real work. That first request after any quiet period could take four or five seconds by itself, before any actual logic ran.
  2. An N+1 query buried in the dashboard's main view.
    The page loaded a list of shipments, and then, for every single shipment, fired off a separate database query to fetch its status history. Twenty shipments meant twenty-one queries instead of two. Nobody noticed this in testing because the test account only ever had three or four sample shipments in it. In production, some customers had hundreds.
  3. A waterfall of API calls on the frontend.
    Instead of the page requesting the data it needed in one or two calls, it was making a request, waiting for that to finish, then making the next request based on the result, then the next. Each one added its own round-trip time on top of the last, and none of them were running in parallel.
  4. An analytics script loaded synchronously in the page head.
    A third-party tracking script was blocking the page from rendering until it finished loading from an external server. On a fast connection, you'd barely notice. On a spotty coffee-shop Wi-Fi connection, which is exactly what several of his customers were using in warehouses, it added a very visible stall before anything appeared.

None of these four things were dramatic on their own. Together, they made a genuinely well-built app feel like it was struggling.


How I Actually Tracked This Down, Step by Step

You don't need an expensive monitoring suite to find most of this. Here's the order I actually worked through it in:

  1. Opened the browser's Network tab (in Chrome DevTools) and reloaded the dashboard with cache disabled. This immediately showed the waterfall of sequential requests, since you can literally see each one start only after the previous one finishes.
  2. Ran a Lighthouse audit straight from DevTools, which flagged the render-blocking analytics script without me having to go hunting for it manually.
  3. Checked the backend logs on Render for response times specifically on the first request after a period of inactivity, which confirmed the cold start pattern — fast, fast, fast, then one request that took several seconds, on repeat.
  4. Turned on query logging on the Postgres database for a few minutes during a normal dashboard load. This is where the N+1 problem became obvious — one page view generating dozens of nearly identical queries in a fraction of a second is not subtle once you're actually looking at the log.
  5. Compare timings from two different networks — my home Wi-Fi and my phone's mobile hotspot — because slowness that only shows up on weaker connections points you straight at render-blocking scripts and unoptimized assets rather than backend issues.

That's really the whole diagnostic process. Frontend network tab, Lighthouse, backend logs, database query log, and a second network to compare against. Most cloud app slowness reveals itself somewhere in those five checks.


Also Read

If the slowdown you're chasing looks more like the whole server choking rather than the app just feeling laggy, this one covers that side of it: What Happens When a Website Server Goes Down


A Quick Reference:

SymptomLikely CauseWhere to Check
First request after idle time is slow, then it's fineCold start on serverless/free-tier hostingBackend logs, timing the first vs. later requests
Page with a list is slow only when there's a lot of dataN+1 queriesDatabase query log during a real page load
Page loads in visible stages, one section at a timeSequential API waterfallBrowser DevTools Network tab
Page freezes briefly before anything appearsRender-blocking scriptLighthouse audit, view page source order
Slow for some users, fine for othersGeographic latency or no CDN cachingTest from a different region/connection
Gets worse gradually over hours or daysMemory leak in a long-running processProvider's memory usage graph over time


Fixes That Actually Made a Difference

For the cold start, the honest fix was upgrading off the free tier to a plan that keeps the service warm, since that's a platform limitation, not a code bug you can engineer your way around for free.

For the N+1 queries, the fix was combining the shipment list and its status history into a single joined query instead of looping and querying per row. Same data, one round trip to the database instead of dozens.

For the API waterfall, we restructured the frontend to fire the independent requests in parallel using Promise.all instead of waiting on each one before starting the next, which cut the total wait time roughly in half on its own.

For the analytics script, we simply added the async attribute to the script tag so it loads in the background instead of blocking the page from rendering while it fetches.

Four small changes. No new servers, no bigger plan, no rewrite. The app just stopped doing unnecessary waiting.


The Mistake I Nearly Made

My first instinct, before I actually opened DevTools, was to tell my client, "let's just upgrade your hosting plan." It would have cost him more money every month, and it wouldn't have fixed a single one of the four actual problems, because none of them were caused by insufficient resources.

That's the trap with cloud performance issues specifically. It's very easy to throw compute at a problem instead of diagnosing it, because scaling up feels like doing something, and it's usually just one click in a dashboard. More resources only help if the bottleneck was actually resources, and in my experience, it usually isn't. It's almost always unnecessary waiting, built into the code itself.


A Few Mistakes Worth Avoiding

  • Testing only with tiny sample data, which hides N+1 query problems until real customer data exposes them.
  • Loading third-party scripts synchronously in the page head without checking whether they block rendering.
  • Assuming a slow page means a slow server, when the actual delay is happening in sequential frontend requests instead.
  • Upgrading hosting plans as the default fix, before actually measuring where the time is going.
  • Never testing on a weaker connection, so render-blocking issues stay invisible until a real customer on bad Wi-Fi hits them.

Final Thoughts

Cloud apps rarely get slow because of one big obvious failure. They get slow because a handful of small, unnoticed delays quietly pile up — a cold service here, a few extra queries there, a script that didn't need to block anything in the first place.

The good news is that almost all of it is genuinely fixable without spending more money on infrastructure. You just have to actually look at where the time is going instead of guessing, and the tools to do that — your browser's own DevTools, Lighthouse, and your provider's logs — are already sitting there, free, waiting for someone to open them.

Hashir
Author At TopicGems • Published Friday, September 18, 2026
Hashir is a freelance cybersecurity professional and web developer, working with clients since 2022. He writes about virtualization, networking, and cloud infrastructure based on hands-on client work.

comments