All postsTech

I Took a Client's PageSpeed From 39 to 77 in One Sitting, for Free. The Exact Checklist.

Open PageSpeed Insights right now and run your site. Mobile tab, not desktop. If the number staring back at you is in the 30s or 40s, you are losing people...

Garvit Sharma

Garvit Sharma

12 June 2026 · 6 MIN READ

Tech

I Took a Client's PageSpeed From 39 to 77 in One Sitting, for Free. The Exact Checklist.

webight.com
On this page

Open PageSpeed Insights right now and run your site. Mobile tab, not desktop. If the number staring back at you is in the 30s or 40s, you are losing people before your page even paints. And you have probably been told the fix is a rebuild, or a new framework, or a month of agency work.

It is one evening of work. I have done it.

A while back, one of our retainer clients at Webight, Zulal, had a mobile score of 39. Nobody asked me to fix it. Speed was outside the retainer scope and I could have left it alone with a clear conscience. Instead I sat down one evening, ran the checklist I am about to give you, and the score came out at 77. Same site, same hosting, same design. I charged zero rupees for it.

They are still on retainer with us today. Hold that thought, because the second half of this post is about why charging nothing was the best billing decision I made that year. First, the checklist, in the order I actually run it.

1. Compress your images before touching anything else

Every site I have audited with a score under 50 had an image problem. Every single one. Designers export at full resolution, someone uploads straight from a phone, and suddenly the homepage ships more megabytes than a mobile connection can chew before the visitor gives up.

Open DevTools, go to the Network tab, filter by Img, hard reload, sort by size. On Zulal's homepage, the gallery images alone added up to 9.4 MB.

The fix is mechanical. Resize to the largest size the layout actually displays, convert to WebP, cap quality around 78. Here is the exact script I run. Drop it in the project root, point it at your images folder, done.

// compress-images.mjs
// setup: npm i sharp
// run:   node compress-images.mjs
import sharp from "sharp";
import { readdir, mkdir, stat } from "fs/promises";
import path from "path";

const SRC = "./public/images";
const OUT = "./public/images/web";

await mkdir(OUT, { recursive: true });

for (const file of await readdir(SRC)) {
  if (!/\.(jpe?g|png)$/i.test(file)) continue;
  const input = path.join(SRC, file);
  const output = path.join(OUT, path.parse(file).name + ".webp");

  await sharp(input)
    .resize({ width: 1600, withoutEnlargement: true })
    .webp({ quality: 78 })
    .toFile(output);

  const before = (await stat(input)).size;
  const after = (await stat(output)).size;
  console.log(`${file}: ${(before / 1024).toFixed(0)} KB -> ${(after / 1024).toFixed(0)} KB`);
}

Then swap the references in your markup. That one step on Zulal moved the score more than everything else on this list combined. If you do nothing else from this post, do this.

2. Cache everything that does not change

A returning visitor should never download your logo twice. Sounds obvious. Most sites still ship every asset fresh on every visit because nobody ever set Cache-Control headers.

The rule: anything fingerprinted or rarely changed (JS bundles, CSS, fonts, images) gets a one-year immutable cache. HTML stays short so deploys show up immediately.

On Vercel, this is a vercel.json in the root:

{
  "headers": [
    {
      "source": "/(.*)\\.(js|css|woff2|webp|avif|png|jpg|svg)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

On nginx it is the same idea with a location block and expires 1y;. Ten minutes of work. The first visit gets faster from the other steps, and every visit after this one is nearly free.

3. Kill render-blocking resources

By default, every stylesheet and every plain <script src> in your head blocks rendering. The browser sits there with a white screen, waiting, while your visitor watches nothing happen on a 4G connection.

Two moves. Scripts that are not needed for first paint get defer. Stylesheets that only style below-the-fold sections get loaded async with the media trick:

<!-- script: downloads in parallel, runs after parse -->
<script src="/js/analytics.js" defer></script>

<!-- stylesheet: loads without blocking render -->
<link rel="stylesheet" href="/css/below-fold.css"
      media="print" onload="this.media='all'">
<noscript>
  <link rel="stylesheet" href="/css/below-fold.css">
</noscript>

PageSpeed literally lists these for you under "Eliminate render-blocking resources". Go down the list one by one and ask of each file: does the first screen need this before it can paint? If no, defer it. That's it.

For example, on our own webight.com audit this June, two render-blocking stylesheets worth 22.5 KiB disappeared with one flag in next.config.ts, because we are on Next.js:

// next.config.ts
const nextConfig = {
  experimental: {
    inlineCss: true,
  },
};

export default nextConfig;

Next.js inlines the CSS into the HTML and the separate blocking requests are gone. If you are on Next, this is the cheapest win on the whole list.

4. Load fonts properly

Fonts cause two separate problems. The browser hides your text while the font downloads, and if you load from Google Fonts, you pay for an extra connection to another domain before a single glyph shows.

Self-host the font file, preload it, and set font-display: swap so text renders immediately in a fallback and swaps when ready:

<link rel="preload" href="/fonts/Inter-Variable.woff2"
      as="font" type="font/woff2" crossorigin>
@font-face {
  font-family: "Inter";
  src: url("/fonts/Inter-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

One variable font file instead of four static weights. One domain instead of two. Text on screen instantly instead of after the download. See, none of this is clever. It is just deciding that the visitor seeing words matters more than the font arriving in style.

5. Delete the scripts nobody is using

This is the step everyone skips because it feels like archaeology. Old sites collect scripts the way drawers collect dead pens. A chat widget from a tool the client cancelled. A heatmap trial from 2023. Two analytics snippets counting the same visitors.

Zulal's pages were loading 13 third-party scripts. Five of them were doing anything. The other eight were pure weight: parse time, network time, main-thread time, all spent on tools nobody had logged into for months.

How to find yours: DevTools, Cmd+Shift+P, type "Coverage", record a page load. Anything sitting at 95%+ unused bytes goes on the suspect list. Then ask the client, tool by tool: do you still use this? You will be surprised how often the answer is "what is that?"

Deleting code is the only performance work with zero risk of making something slower.

Why I did it for free

Now the part that matters more than the checklist.

I did Zulal's speed work free because the return on that evening is asymmetric. The downside was capped: a few hours of my time, and I genuinely enjoy this kind of work, so it barely counts as a cost. The upside had no cap. A client who watches you fix something you were never paid to fix recalibrates what you are to them. You stop being a vendor line item. You become the person who cares about their business when nobody is watching.

There is a psychology principle underneath this and it is reciprocity. People feel a real pull to return value they received without being asked. Cialdini wrote a whole book on it. But you cannot fake it. Reciprocity fires when the gesture is genuine, and clients smell the difference between a gift and a setup.

The numbers back it up for us. Webight has never sent a cold email. Not one, ever. Every client we have, across India, the Gulf, the US, Southeast Asia and the Netherlands, came organically, and a big chunk of that is people who watched us over-deliver somewhere and talked about it. "I'll do this for free, if you like it let's continue" has opened more doors for me than any pitch deck.

One condition, so you don't misread this. Free work pays off when you choose it, for someone already in a relationship with you or close to one, on something with a visible result. A 39 to 77 jump is visible. The client can screenshot it. Spec work demanded by a stranger who is shopping five agencies is a different thing entirely, and I do not do that. The gift only works when it is actually a gift.

So here is the deal. Run your site through PageSpeed Insights tonight, take the checklist above, and give it one honest evening. Then do the same thing for one client or one friend who did not ask, send them the before and after screenshots, and charge nothing. The score will jump in a day. What it does for the relationship will pay you back for years.

If you get stuck on any step, my DMs are open.

web performancepagespeed insightsfreelancingweb developmentclient relationships
Next move

We build custom platforms, websites, and automation.

The two people who build it are the two you talk to, and every price is on the page.

Garvit Sharma

Garvit Sharma

Full-stack developer and co-founder of Webight, a two-person web and AI studio in India. He writes these from real client work. More about us.