There's a specific kind of email founders send me that always starts the same way: "The site was fast when we launched. Now customers are complaining it's slow. Nothing changed. What's going on?"
Something did change, of course — real users are hitting it now. And real users are the honest test that everything before them wasn't. Developer laptops, staging environments, and controlled demos are all fast. Production, at scale, on the coffee-shop Wi-Fi of a customer who is one loading spinner away from closing the tab, is the only performance test that matters.
I've traced hundreds of "why is my site slow" reports over the years. The findings are almost always in one of three buckets, and one of the three fixes solves more than half of them. This post is the map I wish every founder had before they hired a developer to "make it faster."
First: is it actually slow?
Before you spend a dollar making it faster, spend 15 minutes making sure you're solving the real problem. "Slow" means different things:
Perceived slow ≠ actually slow. A site that shows a loading spinner for 2 seconds feels slower than a site that shows something meaningful for 3 seconds. Perception is dominated by what's happening on the screen while the browser works, not by the raw milliseconds.
First byte vs full load. If your Time to First Byte (TTFB) is 1.5 seconds, the server is slow. If your TTFB is 100ms but the page still takes 4 seconds to become usable, the frontend is slow. These are completely different problems with completely different fixes. Don't optimize the wrong one.
Mobile network reality. Your desktop broadband is 200 Mbps. Your customer on a phone in a parking lot is on 4 Mbps with 300ms latency. A site that looks fast on your laptop can be nearly unusable in the wild. Test on a throttled connection before deciding what's slow.
Real cache misses vs first paint. The first visitor to a page pays the full cost of loading it. The tenth visitor gets almost everything from cache. If you're testing by refreshing repeatedly on your own machine, you're testing the cached case, not the customer case.
The tools to check this properly are free and take 5 minutes each:
- Lighthouse (Chrome DevTools → Lighthouse tab). Gives you TTFB, Largest Contentful Paint, Total Blocking Time, and a prioritized list of what to fix. Run it in incognito on a throttled 4G connection.
- WebPageTest.org. Shows you the exact waterfall of every network request, so you can see which one is holding up the page.
- PageSpeed Insights. Google's own view of your site, which is also the view that affects your search ranking. Uses real-world Chrome user data, not just synthetic tests.
If Lighthouse gives you a mobile score above 80 and users are still saying it's slow, the problem is probably perception — a spinner in the wrong place, a layout that shifts as it loads, a "please wait" screen that stays up longer than it needs to. That's a UX fix, not a performance fix.
If the score is below 60, or specific pages are noticeably slower than others, you have a real problem. Here's where it usually lives.
Cause #1: The database (the most common culprit by far)
Roughly 60% of the "site is slow" cases I audit come down to one of three database problems:
N+1 queries. Your product listing page loads 20 products. To render each product's category name, the code makes a separate database query to look up that category. That's not 1 query — it's 21. On the developer's laptop, 21 queries against 20 rows takes 40ms and looks fine. In production, against a table with a million rows, it takes 8 seconds. This is the single most common performance bug I find, and it's usually a 10-line fix once you know where it is.
Missing indexes. Every query that filters or sorts on a column needs an index on that column, or the database scans the entire table row by row. In development, with a hundred rows, "scan the whole table" is fast. In production, with a hundred thousand rows, it isn't. The fix is a single database command to add the index. The hard part is knowing which queries need one, which is what a database explain plan tells you.
No caching layer. Every page request re-runs the same expensive query, even though the data hasn't changed in an hour. Adding a caching layer — even something as simple as caching a category tree for 60 seconds — often takes minutes to implement and cuts the load by 90%.
How to check without being a developer: ask your developer to run a database "slow query log" for 24 hours in production. It'll show them the queries that are taking longest, ranked by total impact. Anything at the top of that list is the fix that pays the most.
Cause #2: Third-party API calls
The second-biggest cause of "slow after launch" is code that waits for external services to respond before rendering the page.
Blocking calls with no timeout. Your checkout page calls Stripe, an email service, and a shipping calculator before rendering. Each one takes 200ms in the best case. That's 600ms of blocking time added to every checkout view. If any of them hangs — and they will — the whole page hangs with it.
No fallback for external outages. When your third-party recommendation engine goes down for 20 seconds (they do), your product page goes down with it, because the code doesn't have a "if this service isn't responding in 500ms, skip it and show the page anyway" path. Real-world third-party services are 99.5% reliable at best. Design for the 0.5%.
Waterfalls of external calls. Instead of making three third-party calls in parallel (waiting for the slowest one, ~300ms total), the code makes them one after the other (300ms + 200ms + 400ms = 900ms). This is one of the highest-ROI fixes I do, because it's usually a small code change with a large user-visible impact.
How to check: in Chrome DevTools, open the Network tab, filter to "XHR" or "Fetch," and reload the page. Every horizontal bar is one external request; if they're stacked left-to-right instead of stacked top-to-bottom, they're serial. If any single bar is longer than 500ms, that's a bottleneck. If any bar is red, that request failed and your code is probably still waiting for it.
Cause #3: Frontend bloat
The third bucket is everything the browser has to download and process before your page becomes usable.
Oversized JavaScript bundle. Modern web apps ship large JavaScript bundles that the browser has to download, parse, compile, and execute before the page becomes interactive. Every unused library you imported is a tax. Every "just in case" polyfill adds weight. Some sites I audit ship 3 MB of JavaScript for a page that displays 300 words of text — that's not a rendering problem, that's a shipping problem.
Unoptimized images. The single biggest byte-weight cost on most sites is images. A raw phone photo is 4 MB; the same image, converted to WebP at the right size for its container, is 40 KB. On mobile networks, that difference is 5+ seconds. Every image on your site should be sized to the container it displays in and served in a modern format (WebP or AVIF).
Hydration cost. If your site uses a modern JavaScript framework (React, Vue, Next.js, etc.), the server sends HTML and JavaScript, and the browser then has to "hydrate" the HTML by re-running the JavaScript to make it interactive. This work is invisible but it's why the page can look ready and still not respond to clicks for a second or two. There are patterns to reduce it, but they require the developer to know they exist.
How to check: Lighthouse's "Reduce unused JavaScript" and "Properly size images" sections tell you exactly what and how much. The numbers there translate almost directly to loading-time savings.
The one fix that solves more than half of it: caching
If you take one thing from this post: the single most cost-effective performance improvement on almost every site is a caching layer between the user and the origin.
- HTTP caching / CDN. Static assets (images, CSS, JavaScript) should be served from a CDN with long cache times. If they're being served from your origin server on every request, you're paying for and slowing down every single page load unnecessarily. Cloudflare, Fastly, and Vercel's edge network all offer this essentially for free.
- Application-level caching. Frequently-read, rarely-changed data (category trees, feature flags, user profiles) should be cached in memory for a bounded time (seconds to minutes). This turns a 200ms database query into a 0.2ms memory lookup.
- Page caching. Some pages don't change at all between users (marketing pages, blog posts, product pages) — these should be rendered once and served to everyone until the underlying data changes. This can turn a 2-second dynamic page into a 50ms static one.
Caching is not a silver bullet — it introduces its own class of bugs (stale data being served, cache invalidation being wrong). But the tradeoffs are almost always worth it, and a competent engineer can add appropriate caching to a typical web app in a few days of work.
When "slow" is a business decision, not a bug
One last honest observation: sometimes what looks like a performance problem is actually a feature-vs-speed decision that nobody made explicitly.
Every real-time chat widget you add costs milliseconds of load time and blocks the main thread. Every analytics tracker slows the page. Every A/B testing tool adds latency before the page can render. Every third-party review widget, every "recommended for you" panel, every social proof popup — each one costs performance.
At some point, your site is slow because you asked it to do a lot of things. The fix isn't a rewrite or a new stack; it's cutting the features that aren't earning their weight.
I've done audits where the honest recommendation was "delete these four widgets and your site will be twice as fast." That was harder for the founder to accept than "spend $30k on a rewrite," because it meant giving something up. But it worked, and it took a day instead of six months.
What to do this week
If your site is slow and you don't yet know why:
Run Lighthouse in incognito on a throttled mobile connection. Screenshot the results. Note the specific numbers for TTFB, Largest Contentful Paint, Total Blocking Time, and Cumulative Layout Shift. These four numbers will tell you which of the three buckets your problem is in.
Ask your developer to run a slow query log for 24 hours. If the top three queries account for more than 50% of the total database time (they almost always do), you know exactly where to spend the first day of fix work.
Open Chrome DevTools → Network → reload your slowest page. If any single request is over 500ms, if any bar is red, or if requests are stacked serially instead of in parallel, you've found your third-party bottleneck.
Before you spend money on a rewrite because the site is slow — read the refactor vs rewrite framework. Performance is almost never a reason to rewrite. It's a reason to profile, then fix the three worst hotspots.
If you don't yet have a developer to work through this with, or you want a second opinion before you spend the money — that's what an independent audit is for. The free MVP planner can also give you a baseline for what a well-scoped, well-performing version of your product looks like, which is often the missing yardstick in this conversation.
A slow site isn't usually a mystery. It's a small number of hotspots doing 90% of the damage, and once you know which ones, the fixes are cheap. The founders who solve this well are the ones who diagnose before they buy — not the ones who assume the whole thing needs to be rebuilt.
---
If you'd like me to look at your site personally and tell you what I'd fix first, reach out via the contact page with the URL and a short note about what your users are complaining about. I'll spend 30 minutes on it and send you a real diagnosis.
