A slow website is a broken website. Users do not wait around. Google measured that 53% of mobile visitors leave a page that takes longer than 3 seconds to load. And those visitors rarely come back.
But speed problems are not mysterious. They almost always boil down to a handful of common causes. In this article, we walk through the 7 issues we see most frequently in our performance audits, with concrete diagnosis steps and fixes for each.
How Slow Is Slow?
Before diving into causes, let us establish what "fast" and "slow" actually mean. Google's Core Web Vitals provide measurable thresholds:
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5s - 4.0s | > 4.0s |
| FID (First Input Delay) | < 100ms | 100ms - 300ms | > 300ms |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1 - 0.25 | > 0.25 |
| INP (Interaction to Next Paint) | < 200ms | 200ms - 500ms | > 500ms |
You can measure your site using:
- Google PageSpeed Insights: Free, gives both lab and field data
- WebPageTest: Detailed waterfall charts and filmstrip view
- Chrome DevTools: Performance panel and Lighthouse audit
- Google Search Console: Core Web Vitals report for real user data
Now let us look at what is actually slowing your site down.
Cause 1: Unoptimized Images
This is the single most common performance problem we encounter. Images typically account for 50-80% of a page's total weight. An unoptimized hero image can easily be 3-5 MB when it could be 100-200 KB without visible quality loss.
How to Diagnose
- Open Chrome DevTools, go to the Network tab, filter by "Img"
- Sort by size. Any image over 200 KB on a standard web page warrants investigation
- Check if images are being served at their display size or larger (a 4000px wide image displayed at 800px is wasting bandwidth)
- Look at the format: JPEG and PNG are fine, but WebP and AVIF offer 25-50% better compression
How to Fix
- Resize images to their display dimensions. If an image displays at 800px wide, do not serve a 3000px original. Use responsive images with
srcsetto serve different sizes for different screen widths. - Use modern formats. Convert to WebP for broad compatibility (supported by all modern browsers). Use AVIF where supported for even better compression. Keep JPEG/PNG as fallback.
- Compress appropriately. For photographs, JPEG quality 75-85 or WebP quality 75-80 is visually identical to uncompressed for most people. Tools: Squoosh (web-based), ImageOptim (Mac), or Sharp (Node.js library for automated pipelines).
- Lazy load below-the-fold images. Add
loading="lazy"to images that are not visible in the initial viewport. This defers loading until the user scrolls near them. - Specify dimensions. Always include
widthandheightattributes on<img>tags to prevent layout shifts (CLS issues).
Impact
Properly optimizing images typically reduces page weight by 60-80% and improves LCP by 1-3 seconds. It is the single highest-impact optimization you can make.
Cause 2: Too Many HTTP Requests
Every file your page needs (HTML, CSS, JavaScript, images, fonts, icons) requires a separate HTTP request. Each request has overhead: DNS lookup, TCP connection, TLS handshake, server processing, and data transfer. While HTTP/2 mitigates this with multiplexing, excessive requests still slow things down.
How to Diagnose
- Open Chrome DevTools Network tab and count the total requests (shown in the bottom status bar)
- A well-optimized page typically has 30-50 requests. Pages with 100+ requests have a problem.
- Look for patterns: multiple small CSS files, many JavaScript files, dozens of individual icon images
- Check for third-party scripts: analytics, chat widgets, social media buttons, advertising scripts. Each one adds requests.
How to Fix
- Bundle CSS and JavaScript. Use build tools (Webpack, Vite, esbuild) to combine multiple files into fewer bundles. Most modern frameworks handle this automatically.
- Use CSS sprites or SVG icons. Instead of 40 individual icon files, combine them into a single sprite sheet or use an SVG icon system.
- Inline critical CSS. The CSS needed to render the above-the-fold content can be inlined directly in the
<head>, eliminating one round trip. - Audit third-party scripts. Do you really need that chat widget, that social sharing bar, those five different analytics tools? Each one you remove is several requests saved.
- Use font subsetting. Instead of loading an entire font family with all character sets, subset it to include only the characters your site actually uses.
Impact
Reducing requests from 100+ to 40-50 typically improves Time to Interactive by 1-2 seconds, especially on mobile connections.
Cause 3: Render-Blocking Resources
When a browser encounters a <link> tag for CSS or a <script> tag for JavaScript in the <head>, it stops rendering the page until that resource is downloaded and processed. If those resources are large or slow to load, your users stare at a blank screen.
How to Diagnose
- Run Lighthouse in Chrome DevTools. It specifically flags render-blocking resources.
- Look at the waterfall chart in WebPageTest. Resources that block the first paint are clearly visible.
- Check the
<head>of your HTML for<script>tags withoutasyncordeferattributes. - Look for multiple CSS files loaded in the
<head>, especially large framework CSS (Bootstrap, Tailwind full build, etc.).
How to Fix
- Defer non-critical JavaScript. Add the
deferattribute to script tags that do not need to execute before the page renders. Useasyncfor scripts that are independent (analytics, for example). - Inline critical CSS. Extract the CSS needed for above-the-fold rendering and inline it. Load the rest asynchronously.
- Preload key resources. Use
<link rel="preload">for critical fonts and CSS to start downloading them earlier. - Remove unused CSS. Tools like PurgeCSS can strip unused rules from your stylesheets. A full Bootstrap CSS file is 230+ KB. Most sites use less than 20% of it.
- Move scripts to the bottom. If you cannot use
deferorasync, move script tags just before</body>.
Impact
Eliminating render-blocking resources typically improves First Contentful Paint by 0.5-2 seconds. This is what makes a site "feel" fast even before all content has loaded.
Cause 4: Slow Server Response (TTFB)
Time to First Byte (TTFB) measures how long it takes for the server to start sending the HTML response. If your server is slow, nothing else matters. You cannot optimize your way out of a slow server.
How to Diagnose
- Check TTFB in Chrome DevTools (Network tab, select the main HTML document, look at "Waiting" time)
- A good TTFB is under 200ms. Over 600ms indicates a server problem.
- Test from multiple locations to distinguish between server issues and network distance
Common Causes of Slow TTFB
- Cheap shared hosting: Your site shares CPU and RAM with hundreds of other sites. When one site gets a traffic spike, everyone suffers. This is the most common cause for small business websites.
- No page caching: WordPress and other CMS platforms generate pages dynamically on every request. Without caching, each page visit triggers database queries, PHP processing, and template rendering.
- Unoptimized database: Slow queries, missing indexes, or a bloated database (years of post revisions, spam comments, transient options) drag down response time.
- PHP version: PHP 8.x is roughly 3x faster than PHP 7.0 and dramatically faster than PHP 5.6. Yes, sites still run on PHP 5.6.
How to Fix
- Upgrade hosting. Move from shared hosting to a managed VPS, cloud hosting, or a managed WordPress host. The cost difference is typically CHF 10-30/month to CHF 30-100/month. That investment pays for itself in better rankings and user experience.
- Implement page caching. For WordPress: WP Super Cache, W3 Total Cache, or WP Rocket. For other stacks: Varnish, Redis, or Nginx FastCGI cache. A cached page serves in 20-50ms instead of 500-2000ms.
- Optimize the database. Remove post revisions, spam, transients. Add proper indexes. Schedule regular optimization.
- Use the latest PHP version. Seriously. If your hosting still runs PHP 7.x, upgrade. The performance improvement is free.
Impact
Fixing TTFB from 800ms to 200ms improves every single metric because everything else depends on the server responding first.
Cause 5: No CDN (Content Delivery Network)
If your server is in Zurich and your visitor is in Tokyo, the data has to travel roughly 9,500 km each way. Physics imposes a minimum latency of about 30ms per hop, and there are many hops. Without a CDN, distant visitors experience significantly slower load times.
How to Diagnose
- Test your site from multiple geographic locations using WebPageTest (it offers test locations worldwide)
- If load times vary dramatically by location (fast locally, slow internationally), you probably lack a CDN
- Check your response headers for CDN indicators (e.g.,
cf-rayfor Cloudflare,x-amz-cf-idfor CloudFront)
How to Fix
- Deploy a CDN. Popular options: Cloudflare (free tier available, excellent for most sites), AWS CloudFront, Bunny CDN (affordable, great performance), Fastly.
- Configure proper caching. Static assets (images, CSS, JS, fonts) should have long cache lifetimes (1 year) with cache-busting via filename hashes. HTML can be cached for shorter periods or served dynamically.
- Enable full-page caching at the edge. For static or semi-static sites, serve the HTML from CDN edge nodes. Cloudflare Workers, Vercel, Netlify, and Cloudflare Pages handle this natively.
Impact
A CDN typically reduces load times for distant visitors by 50-70%. For a Swiss business with international clients, this is significant. Even for local visitors, the CDN edge node in Zurich or Frankfurt is likely faster than your origin server.
Cause 6: Heavy JavaScript Frameworks
Modern JavaScript frameworks (React, Angular, Vue) enable rich, interactive applications. They also ship a lot of JavaScript. A basic React application with a few common libraries can easily produce 500 KB - 1 MB of JavaScript after minification.
The problem is not just download size. JavaScript must be parsed, compiled, and executed. On a mid-range mobile phone, parsing 1 MB of JavaScript takes 2-4 seconds. During that time, the page is unresponsive.
How to Diagnose
- Chrome DevTools Coverage tab: shows how much of your loaded JavaScript is actually executed on the current page
- Lighthouse performance audit: flags excessive JavaScript
- Webpack Bundle Analyzer (or similar): visualizes what is in your JavaScript bundles
- Check for duplicate libraries (e.g., two versions of Lodash, jQuery loaded alongside a modern framework)
How to Fix
- Code splitting. Split your JavaScript into chunks loaded on demand. Users should not download the code for the admin dashboard when viewing the homepage. All modern bundlers support this.
- Tree shaking. Ensure your build process eliminates dead code. Import only what you use:
import { debounce } from 'lodash-es'instead ofimport _ from 'lodash'. - Evaluate your dependencies. Do you need Moment.js (330 KB) when
date-fns(tree-shakable) or the nativeIntl.DateTimeFormatAPI works? Does that carousel plugin justify 100 KB of JavaScript? - Consider lighter alternatives. Preact (3 KB) is a drop-in replacement for React (40+ KB). Alpine.js (15 KB) handles interactivity that does not need a full framework. For content-focused sites, a static site generator with minimal JavaScript is often the right choice.
- Server-side render (SSR) or static site generation (SSG). Frameworks like Next.js, Nuxt, and Astro can render pages server-side, sending HTML instead of an empty shell that requires JavaScript to render. This dramatically improves perceived performance.
Impact
Reducing JavaScript from 1 MB to 200 KB typically improves Time to Interactive by 2-5 seconds on mobile devices. This is often the difference between a usable site and an unusable one on slower hardware.
Cause 7: Unoptimized Database Queries
If your website uses a database (WordPress, e-commerce platforms, web applications with dynamic content), slow queries can be a major bottleneck. A single poorly written query can add seconds to your page load time.
How to Diagnose
- WordPress: Install the Query Monitor plugin. It shows every database query, how long each took, and which plugin or theme initiated it.
- General: Enable slow query logging in MySQL/PostgreSQL. Queries taking longer than 1 second need attention. Queries taking longer than 100ms on a page load need attention if there are many of them.
- Check query count: A WordPress page that fires 200+ queries has a problem, likely a plugin or theme with poor database practices. A well-optimized page should need 20-50 queries.
Common Database Problems
- Missing indexes: A query that searches a column without an index has to scan every row in the table. With an index, it can jump directly to the relevant rows.
- N+1 queries: Loading a list of 50 posts and then running a separate query for each post's metadata (50 extra queries) instead of fetching everything in a single JOIN query.
- Bloated tables: WordPress stores post revisions, transient options, and orphaned metadata indefinitely. A site that has been running for years can have millions of unnecessary rows.
- No object caching: Without an object cache (Redis, Memcached), the same database queries run on every page load. An object cache stores query results in memory so they do not hit the database repeatedly.
How to Fix
- Add indexes. Identify slow queries with
EXPLAINand add appropriate indexes. This is the single most impactful database optimization. - Clean the database. Remove post revisions (or limit them in
wp-config.php:define('WP_POST_REVISIONS', 5)), delete spam comments, clean expired transients. - Implement object caching. Install Redis or Memcached and configure WordPress to use it (via a plugin like Redis Object Cache). This alone can cut TTFB in half for dynamic pages.
- Optimize queries. Replace N+1 patterns with JOINs or batch queries. Review plugin code or replace plugins that generate excessive queries.
- Consider static generation. If your content does not change frequently, generate static HTML and skip the database entirely. This is the ultimate database optimization: not having one.
Impact
Database optimization typically improves TTFB by 30-70% for dynamic pages. Combined with page caching, database overhead becomes nearly zero for most visitors.
Putting It All Together: A Performance Optimization Plan
Do not try to fix everything at once. Prioritize based on impact:
Quick Wins (Hours)
- Enable page caching
- Compress and resize images
- Defer non-critical JavaScript
- Remove unused plugins/scripts
Medium Effort (Days)
- Set up a CDN
- Implement lazy loading for images
- Convert images to WebP
- Inline critical CSS
- Add missing image dimensions
Larger Projects (Weeks)
- Upgrade hosting infrastructure
- Implement object caching (Redis)
- Audit and reduce JavaScript bundle size
- Optimize database queries and structure
- Evaluate framework choice for future development
Measuring Results
Performance optimization without measurement is guesswork. Before making changes, benchmark your site:
- Record PageSpeed Insights scores for key pages
- Note Core Web Vitals field data from Google Search Console
- Run a WebPageTest audit and save the results
- After each change, re-test and compare
For a deeper look at how performance metrics affect your SEO rankings, read our guide on Core Web Vitals and SEO. And for a comprehensive approach to making your site faster, check our performance optimization guide.
If you want expert help diagnosing and fixing your website's performance problems, reach out to our team. We will pinpoint the bottlenecks and get your site loading fast.
Want to know if your site is secure?
Request a free security audit. In 48 hours you get a complete report.
Request Free Audit