How I Reduced a Client's Page Load Time by 70%
6 min read
The Starting Point
A client came to me with a problem that’s unfortunately common: their e-commerce site was slow, and it was costing them sales. Google PageSpeed Insights scored the site at 35 on mobile. Average page load time was over 6 seconds. Bounce rates were climbing. The team knew performance was an issue but didn’t know where to start.
Performance optimization is one of the services I’m asked about most. This article walks through what I did, step by step, and what the results looked like.
Step 1: Measure Everything
Before touching any code, I established baselines. You can’t improve what you don’t measure.
I collected:
- Lighthouse scores: Performance, Accessibility, Best Practices, SEO (run on both mobile and desktop)
- Core Web Vitals: LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift), INP (Interaction to Next Paint)
- Server metrics: average response time, database query count per page, memory usage
- Real user data: page load times by page type (homepage, product listing, product detail, checkout)
The numbers told a clear story: the backend was slow (average 2.5s server response time), the frontend was heavy (3MB+ of unoptimized assets), and caching was essentially nonexistent.
Step 2: Database and Backend (The Biggest Win)
The backend was responsible for most of the load time. I started there because it had the highest impact-to-effort ratio.
N+1 Query Elimination
The product listing page was executing over 200 database queries, a textbook N+1 problem. Each product loaded its categories, images, and price rules in separate queries. I added eager loading and reduced this to 5 queries.
Index Optimization
Several frequently queried columns had no indexes. Adding indexes to products.category_id, products.status, order_items.product_id, and a composite index on products(status, category_id, sort_order) cut query times by 60-80%.
Query Restructuring
Some pages used multiple sequential queries where a single join would work. The order history page, for example, made one query per order to fetch line items. I restructured it to a single query with joins and PHP-side grouping.
Result: Server response time dropped from 2.5s to 400ms.
Step 3: Caching Strategy
With the database optimized, I layered in caching to reduce load further.
- Redis for sessions: moved from file-based sessions to Redis, eliminating filesystem I/O on every request
- Page-level caching: product listing pages and category pages cached for 5 minutes with tag-based invalidation
- Query result caching: expensive aggregation queries (top sellers, related products) cached for 15 minutes
- HTTP caching headers: proper
Cache-Controlheaders on static assets and API responses
I used tag-based invalidation so that when a product is updated, only cache entries containing that product are flushed, not the entire cache.
Result: Repeat page loads now serve in under 100ms.
Step 4: Frontend Optimization
The frontend was shipping 3MB+ of assets on every page load. Here’s what I did:
JavaScript
- Audit and remove: found two analytics scripts, a chat widget, and a social sharing library that were all loading on every page. Moved them to load only where needed.
- Code splitting: split the main JavaScript bundle into route-based chunks so each page only loads the code it needs.
- Defer non-critical scripts: analytics, chat, and tracking scripts now load after the page is interactive.
CSS
- Purge unused CSS: the CSS file included styles for components that weren’t used on most pages. Tailwind’s purge (now content configuration) removed 80% of the CSS.
- Critical CSS inlining: extracted the CSS needed for above-the-fold content and inlined it in the
<head>, deferring the rest.
Images
- Format conversion: converted all product images from PNG/JPG to WebP, with AVIF where supported. Average image size dropped by 60%.
- Responsive images: generated multiple sizes for each image and used
srcsetto serve the appropriate size for each viewport. - Lazy loading: images below the fold load only when they enter the viewport.
Fonts
- Subset fonts: the site was loading 4 font weights including character sets for languages it didn’t support. I subset to only the required characters and reduced font file sizes by 70%.
- Font display swap: set
font-display: swapto prevent invisible text during font loading.
Result: Total page weight dropped from 3.2MB to 680KB.
Step 5: Core Web Vitals
With the heavy lifting done, I fine-tuned for Google’s Core Web Vitals:
- LCP (Largest Contentful Paint): The hero image was the LCP element. I preloaded it with
<link rel="preload">, served it in AVIF format, and ensured the server response was fast. LCP went from 4.2s to 1.1s. - CLS (Cumulative Layout Shift): Layout shifts were caused by images without dimensions and late-loading fonts. I added explicit
widthandheightattributes to all images and preloaded fonts. CLS went from 0.25 to 0.02. - INP (Interaction to Next Paint): Slow interactions were caused by heavy JavaScript running on click handlers. I moved expensive computations to web workers and debounced input handlers. INP went from 380ms to 90ms.
The Results
| Metric | Before | After | Improvement |
|---|---|---|---|
| PageSpeed Score (mobile) | 35 | 94 | +169% |
| Average Page Load | 6.2s | 1.8s | -71% |
| Server Response Time | 2.5s | 0.4s | -84% |
| Total Page Weight | 3.2MB | 680KB | -79% |
| LCP | 4.2s | 1.1s | -74% |
| CLS | 0.25 | 0.02 | -92% |
| INP | 380ms | 90ms | -76% |
The client also saw measurable business impact in the weeks following the optimization, but those numbers are theirs to share, not mine.
What I’d Do Differently
If I were starting from scratch, I’d bake performance budgets into the CI pipeline from day one. It’s much easier to maintain fast performance than to fix a slow site retroactively. A simple Lighthouse CI check on every PR would have caught most of these issues before they accumulated.
Your Turn
If your site is slow and you’re not sure where to start, the process is always the same: measure, fix the biggest bottleneck, measure again, repeat. The tools are free (Lighthouse, WebPageTest, Chrome DevTools). The hard part is knowing which numbers matter and what to do about them.
Need help? Get in touch or visit tedbin.com.