Website Performance
Web Dev

Why Most Websites Fail at Performance (And How We Fix It)

By DartSyn Team · Apr 5, 2025 · 5 min read
Back to Blog

Every website we have ever audited has had at least three fixable performance problems. Not theoretical problems — real, measurable issues causing real users to wait, bounce, and convert at lower rates than they should. Performance is not a nice-to-have. Google uses Core Web Vitals as a ranking signal. Users abandon pages that take more than three seconds to load. A 100ms improvement in page load time correlates with a 1% increase in revenue for e-commerce sites. Here are the problems we find most consistently and exactly what we do about them.

Understanding Core Web Vitals First

Before auditing any website, we align on what we are actually measuring. Google's Core Web Vitals are the metrics that matter most for both user experience and search ranking:

  • Largest Contentful Paint (LCP) — How long until the largest visible element on the page loads. Target: under 2.5 seconds. This is almost always an image or a large text block. Poor LCP is the most common Core Web Vitals failure we see.
  • Interaction to Next Paint (INP) — How quickly the page responds to user interactions. Replaced First Input Delay (FID) in March 2024. Target: under 200ms. Poor INP usually means JavaScript is blocking the main thread.
  • Cumulative Layout Shift (CLS) — How much the page visually shifts as it loads. Target: under 0.1. A button that moves right as you're about to click it is a CLS problem. So is text that jumps when an ad loads above it.

We run every audit against these three metrics using both lab data (Lighthouse, PageSpeed Insights) and real-user field data (Chrome User Experience Report, Google Search Console). Lab data tells you what's fixable. Field data tells you what real users are actually experiencing.

Performance Killer 1 — Unoptimised Images

Images are the single largest contributor to slow LCP scores on most websites we audit. This problem is almost universal and almost always fixable with minimal engineering effort.

  • Wrong format. JPEG and PNG are still the dominant image formats on most sites we inherit. WebP delivers 25–35% smaller file sizes than JPEG at equivalent quality. AVIF delivers 50% smaller files than JPEG. Every image on every page we build is served in WebP or AVIF with JPEG/PNG fallbacks. On a typical marketing site, this change alone reduces total page weight by 30–40%.
  • No responsive images. Serving a 2000px wide image to a mobile device that displays it at 400px is serving five times more data than necessary. The srcset attribute and sizes attribute allow the browser to request the appropriately-sized image for the current viewport. Implementing responsive images on image-heavy pages routinely cuts mobile page weight by 60% or more.
  • Missing width and height attributes. Images without explicit dimensions cause layout shifts as the browser doesn't know how much space to reserve before the image loads. Adding width and height attributes to every img tag is a two-minute fix that directly improves CLS scores.
  • No lazy loading below the fold. Images that are not visible on initial page load should not be loaded on initial page load. The loading="lazy" attribute defers off-screen images until they are about to enter the viewport, reducing initial page weight significantly on long pages.
  • Hero images not preloaded. The hero image is almost always the LCP element. Adding a preload link tag in the document head tells the browser to fetch it at the highest priority, before it would discover it during normal HTML parsing. This consistently improves LCP by 300–600ms on sites where we add it.

Performance Killer 2 — Render-Blocking Resources

The browser cannot render a page while it is waiting to download and parse CSS and JavaScript files. Every render-blocking resource adds directly to the time before any content appears on screen.

  • Synchronous JavaScript in the document head. A script tag without async or defer in the head blocks all parsing until the script downloads and executes. We audit every script tag in the document head and add defer to any that are not critical for initial render. Non-critical analytics scripts, chat widgets, and third-party tools are routinely left blocking by default configurations.
  • Unused CSS. Most CSS frameworks ship with far more CSS than any individual page uses. A page using Bootstrap or Tailwind with default configuration often loads 100–200KB of CSS while using 5–10KB of it. We use PurgeCSS or Tailwind's built-in purging to ship only the CSS that a page actually uses. The reduction in CSS size directly reduces render-blocking time.
  • Third-party scripts loaded synchronously. Google Tag Manager, Facebook Pixel, Intercom, Hotjar — every third-party script added to a site is a potential performance liability. These scripts should be loaded with async or defer, or deferred until after user interaction using a facade pattern. We audit third-party script loading on every performance engagement and consistently find scripts being loaded in ways that were never necessary.
  • No resource hints for critical origins. dns-prefetch and preconnect resource hints instruct the browser to establish connections to third-party origins before they are needed. Adding preconnect for Google Fonts, CDN origins, and API endpoints reduces the connection overhead when those resources are eventually requested.

Performance Killer 3 — JavaScript Bundle Bloat

Modern JavaScript-heavy applications routinely ship hundreds of kilobytes — sometimes megabytes — of JavaScript to the browser. Parsing and executing this JavaScript blocks the main thread and degrades both LCP and INP.

  • No code splitting. Shipping the entire application JavaScript bundle on every page means users downloading code for features they may never use on that visit. Route-based code splitting — available in React, Next.js, and most modern frameworks — means each page loads only the code it needs. On large applications this can reduce initial JavaScript payload by 60–80%.
  • Unanalysed bundle composition. Most teams do not know what is inside their JavaScript bundles. We run webpack-bundle-analyzer or similar tools on every audit and consistently find unexpected large dependencies — full lodash imported where a single function was needed, moment.js where a smaller date library would serve, polyfills for browsers that are no longer supported.
  • No tree shaking configuration. Tree shaking removes unused code from the final bundle during build, but it requires both the bundler to be configured correctly and the imported code to be in a tree-shakeable format. We check that production builds have tree shaking enabled and that import patterns are compatible with it.
  • Heavy third-party embeds loaded eagerly. YouTube embeds, Google Maps, social share widgets — these load significant JavaScript of their own. A YouTube embed on a page loads over 500KB of Google's JavaScript before the user ever clicks play. We replace eager embeds with facade components — lightweight placeholders that only load the full embed on user interaction.

Performance Killer 4 — No Caching Strategy

Every resource that can be cached is a resource that does not need to be re-downloaded on subsequent visits. Most sites we audit have either no caching headers or default caching headers that are significantly suboptimal.

  • Static assets with short cache lifetimes. JavaScript, CSS, and image files that change infrequently should be cached for a year or more. When these files are updated, cache-busting via content hashing in the filename ensures users get the new version immediately. Most sites we audit serve these files with 1-hour or 24-hour cache lifetimes, causing unnecessary re-downloads on every return visit.
  • No CDN for static assets. Serving static assets from an origin server in one region means users in other regions experience higher latency for every asset request. A CDN with edge caching serves those assets from a location close to the user. CloudFront, Cloudflare, or Fastly — the choice matters less than having one.
  • API responses not cached where appropriate. Not all API data changes on every request. Responses that are the same for all users (public product catalogues, blog post lists, configuration data) can be cached at the CDN edge or in a reverse proxy. We identify cacheable API endpoints during audits and implement appropriate cache-control headers. The reduction in origin server load is frequently dramatic.
  • Service worker caching not implemented for returning users. For web applications where returning user experience matters, a service worker that caches the application shell and critical assets delivers near-instant loads on repeat visits. This is not appropriate for all sites, but for SaaS applications and frequently-visited tools it is a significant differentiator.

Performance Killer 5 — Font Loading Problems

Web fonts are a common and underappreciated source of both LCP and CLS failures. Most font problems are fixable in under an hour once identified.

  • No font-display strategy. Without a font-display declaration, fonts block text rendering until they download — or render with a flash of invisible text. font-display: swap renders text immediately in a fallback font and swaps to the custom font when it loads. font-display: optional skips the custom font entirely if it hasn't loaded within a short window. Both are preferable to the default blocking behaviour.
  • Fonts loaded from third-party CDNs without preconnect. Google Fonts and other font CDNs require a DNS lookup and connection establishment before downloading. Adding a preconnect resource hint for the font CDN origin eliminates this connection overhead from the critical path.
  • Loading entire font families when only a subset is used. Requesting a font family with all weights and styles loads significantly more data than requesting only the specific weights and styles in use. We audit font usage across each page and request only what is needed.
  • No font subsetting for non-Latin character sets. If an application doesn't use non-Latin characters, loading a full Unicode font file is unnecessary. Subsetting fonts to include only the character ranges actually used reduces font file sizes by 60–80% in many cases.

Our Performance Audit Process

When a client brings us a site with performance problems, we follow a consistent audit process before making any changes:

  • Baseline measurement — Run PageSpeed Insights on the five most important pages (homepage, highest-traffic landing page, key conversion page, product/service page, checkout or sign-up page). Record all three Core Web Vitals scores for both mobile and desktop. This is the baseline everything is measured against.
  • Waterfall analysis — Open the site in Chrome DevTools with network throttling set to "Slow 3G" and record the network waterfall. This shows exactly what is blocking the initial render and in what order resources load. Visual inspection of a waterfall frequently reveals obvious issues in minutes.
  • Bundle analysis — Run the JavaScript bundle through an analyser to identify large dependencies, duplicate packages, and code that should be split or removed.
  • Image audit — Export a list of all images loaded on key pages and check format, dimensions, compression level, and whether responsive images are implemented correctly.
  • Third-party script inventory — List every third-party script loaded on key pages, its size, whether it is render-blocking, and whether it is strictly necessary. Third-party scripts are often added and never reviewed — we routinely find scripts from tools that were cancelled months or years previously still loading on live sites.

Real Results From Our Performance Work

To make this concrete — specific before and after numbers from recent performance engagements:

  • E-commerce client: LCP improved from 5.8s to 1.9s after image optimisation and hero preloading. Conversion rate increased 22% in the following month.
  • SaaS marketing site: Total page weight reduced from 3.4MB to 680KB after WebP conversion, code splitting, and third-party script deferral. Mobile Lighthouse score improved from 34 to 91.
  • FinTech web app: INP reduced from 420ms to 85ms after main-thread JavaScript optimisation and event handler cleanup. User-reported "feels slow" complaints dropped to near zero in post-optimisation user testing.
  • Health platform: CLS reduced from 0.31 to 0.02 after adding width/height attributes to all images and fixing a dynamically injected banner that was pushing content down on load.

Have a website that scores poorly on PageSpeed or feels slow to your users? We run performance audits and fix what we find — get in touch and we'll take a look.

Web Performance Core Web Vitals Page Speed LCP JavaScript SEO
Sajawal Khan
Sajawal Khan Sadozai
Founder & CEO, DartSyn

Building software products for clients across 12+ countries. Passionate about AI, product engineering, and turning complex problems into elegant solutions.