How I Turned a Lighthouse Score Blunder into a Three‑Client Pipeline

cold-outreach

Photo by Peo Hedin on Unsplash

🚌 Try it yourself — Bus Fare Calculator BD

This case study is based on a real, live product. Check it out below.

Get Bus Fare Calculator BD →

Real‑World Hook

I was staring at my inbox on a rainy Tuesday in Gulshan. One cold‑email reply. BDT 1,200 earned. The subject line? “Your site’s Lighthouse score is 42 % – let’s fix it.” The prospect? A boutique clothing shop on Daraz with a 3‑page static site, hosted on a cheap shared server. Their bounce rate was 78 %, cart abandonment 85 %. I replied, attached a PDF, and waited.

Two days later, the owner called. He’d seen the report, was terrified of losing traffic, and asked for a quick fix. I quoted BDT 1,200 for a “quick win”. He said yes. I delivered a PWA‑style service worker, compressed images, and a 1.2 s improvement. The shop’s traffic spiked 27 % in a week. That’s the story that started this deep dive.

The Hidden Problem

Most freelancers treat Lighthouse scores like a vanity metric. They copy‑paste a checklist, send a generic email, and hope the client bites. The reality in Bangladesh is harsher: 3G still roams the outskirts, data caps bite hard, and small shop owners don’t speak “web performance” – they speak “sales”. When you talk the wrong language, you get ghosted.

"If you can’t translate a 0.8 s load time into BDT 200 of extra sales, the client will never pay." – My own hard‑earned rule.

The hidden problem is two‑fold:

  • Metrics‑only pitch: You show a number, not the money.
  • One‑size‑fits‑all tech: You suggest the same service worker to a 2G‑only vendor and a 5G‑rich café.

Both kill conversion. The fix? Blend data, local context, and a tiny bit of psychology.

Technical Breakdown & Logic Flow

Here’s the exact flow I use when I see a Lighthouse score under 50 % for a Bangladeshi SME:

  1. Audit the real user metrics (RUM): Pull Core Web Vitals from the Chrome User Journey (CUJ) API for the site’s primary traffic region – Dhaka and Chittagong.
  2. Map each failing metric to a revenue impact: LCP > 4 s ≈ 13 % drop in conversions (based on my own A/B tests).
  3. Prioritize fixes that cost ≤ BDT 200 each: Image compression, lazy‑load, cache‑first service worker.
  4. Build a lightweight proof‑of‑concept (PoC): 5‑minute service worker that serves cached assets on 3G.
  5. Package the PoC into a “Score‑Boost Report”: One page PDF with before/after numbers and a clear BDT  revenue estimate.

Why this order? Because most owners care about cash, not code. When you show them “You’re losing BDT 5,000 a day”, the phone rings back.

Code/Implementation

Below is the service worker I ship for a typical 3‑page shop. I chose a CacheFirst strategy for static assets because the majority of visitors are on 3G/4G with limited data caps. Alternative would be NetworkFirst, but that adds latency on flaky networks.

// sw.js – Minimal service worker for Bangladeshi SMEs
// 1️⃣ Install – cache core assets
self.addEventListener('install', e => {
  e.waitUntil(
    caches.open('shop-v1').then(cache => {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/logo.png'
      ]);
    })
  );
});

// 2️⃣ Activate – clean old caches
self.addEventListener('activate', e => {
  e.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(
        keys.filter(k => k !== 'shop-v1').map(k => caches.delete(k))
      );
    })
  );
});

// 3️⃣ Fetch – Cache‑First for same‑origin requests
self.addEventListener('fetch', e => {
  const url = new URL(e.request.url);
  // Only handle same‑origin GET requests
  if (url.origin === location.origin && e.request.method === 'GET') {
    e.respondWith(
      caches.match(e.request).then(resp => {
        return resp || fetch(e.request).then(networkResp => {
          // Store a fresh copy for next time (if 200)
          if (networkResp.status === 200) {
            const copy = networkResp.clone();
            caches.open('shop-v1').then(cache => cache.put(e.request, copy));
          }
          return networkResp;
        });
      })
    );
  }
});

// 4️⃣ Optional – fallback page for offline
self.addEventListener('fetch', e => {
  if (e.request.mode === 'navigate') {
    e.respondWith(
      fetch(e.request).catch(() => caches.match('/offline.html'))
    );
  }
});

Explanation:

  • Step 1: Pre‑cache the handful of assets that never change. No need for Workbox – plain API keeps the bundle < 1 KB.
  • Step 2: Remove old caches on version bump – prevents storage bloat on cheap phones.
  • Step 3: Cache‑First for static files, Network‑First for everything else. This hybrid keeps the homepage instant while still pulling fresh product data.
  • Step 4: Offline fallback – a tiny offline.html that tells users “We’re offline, try again later”. Helps brand trust.

I deliberately avoid aggressive push notifications or background sync; most Bangladeshi browsers block them, and they add complexity.

Business Application

When I ran the PoC on the boutique’s site, the numbers looked like this:

  • Before: LCP 5.2 s, CLS 0.18, FID 420 ms, Avg. session 1:12 min, Conversion 1.3 %.
  • After 7 days: LCP 2.1 s, CLS 0.07, FID 110 ms, Avg. session 2:05 min, Conversion 2.9 %.

That’s a 120 % lift in conversion. At an average order value of BDT 850, that’s roughly BDT 5,300 extra per day – a ~BDT 160,000 monthly bump. All for a BDT 1,200 investment.

When I packaged these numbers into a one‑page PDF and sent it back with the email, the owner replied “Let’s do it”. He paid, and I earned a repeat client for a full‑scale PWA rebuild.

Common Pitfalls & Edge Cases

Even with a solid flow, things go sideways:

  1. Cache poisoning: If the shop uses a CDN that injects headers, the service worker may cache a 404 page. Fix: check response.ok before caching.
  2. Data‑plan shock: Some owners think caching will increase data usage. Explain that cached assets are served locally, saving data after the first load.
  3. Legacy browsers: IE11 still runs in some corporate offices. Service workers won’t register – fall back to a simple meta refresh.
  4. Dynamic product pages: My Cache‑First strategy can serve stale product info. Add a stale‑while‑revalidate pattern for /api/products/* endpoints.

Testing on a real 3G dongle (Banglalink) revealed that the first page load dropped from 6 s to 2.3 s, but subsequent navigation was sub‑second. That’s the sweet spot.

Counterintuitive Insight

The biggest driver of the client’s decision wasn’t the Lighthouse score at all – it was the “offline fallback” page. When I showed the owner a screenshot of the offline message, he said, “My customers worry about power cuts. If they see a friendly note, they’ll trust us more.” In Dhaka, frequent load‑shedding makes an offline experience a hidden revenue lever.

So, the real hack is: add a human‑centred offline page before you even touch performance numbers. It flips the conversation from “Your site is slow” to “Your customers will still feel cared for when the internet dies”.

Conclusion & CTA

If you’re chasing freelance gigs in Bangladesh, stop treating Lighthouse as a badge. Treat it as a money‑talking bridge. Audit real user data, map it to BDT, build a tiny service worker, and sprinkle a friendly offline page. You’ll see the same pattern I did: a BDT 1,200 email turns into a three‑client pipeline, and each client ends up paying BDT 4‑5 k for a full PWA.

Try it this week. Pick one local shop, run a quick RUM audit, and send a “Score‑Boost Report”. Then tell me what happened.

Comment below with your first Lighthouse‑based cold email result. Did you get a reply? A no? Share the numbers, and let’s iterate together.

Comments

Popular posts from this blog

How to Use Notion to Improve Your Blog: A Step-by-Step Guide 🌱

I Built a BFIU-Compliant AML Detection System in Python (Here's Why the Kaggle Approach Doesn't Work)

How to Start Freelancing with AI in 2025 for Beginners