Skip to content
Sandeep Kumar ChaudharySandeep
Back to BlogWeb Performance

AdSense Cut My Mobile Lighthouse Score From 94 to 42

By Sandeep Kumar ChaudharyOct 10, 20263 min read
Performance gauge showing a website speed score

TL;DR

The standard AdSense snippet loads roughly 440 KB of Google ad and consent scripts that compete with your own page on slow phones. Loading the script after the visitor's first interaction, or four seconds after the page becomes interactive, restored a mobile score of 89 while still showing ads to real readers.

Key takeaways

  • Measure before and after adding any third-party script. The cost is often far larger than the file itself.
  • On throttled mobile, AdSense pushed Largest Contentful Paint from 2.8 to 8.6 seconds.
  • Best Practices fell to about 60 because of a third-party cookie and a deprecated API inside Google's code.
  • Loading on first interaction or after a short timeout keeps ads for real readers and keeps the page fast.

When I connected this site to Google AdSense, I did what the setup screen says: paste the adsbygoogle.js snippet into the <head> of every page. Then I ran Lighthouse, the tool behind PageSpeed Insights, out of habit.

The mobile performance score had dropped from 94 to 42.

This post covers what changed, why, and the small loader that brought the score back to 89 without giving up the ads.

The measurement

I ran Lighthouse against the live homepage three times: before AdSense, with the standard snippet, and with the deferred loader described below. The mobile runs use Lighthouse's default throttling, which simulates a mid-range phone on a slow 4G connection.

Mobile LighthouseBefore adsStandard snippetDeferred loader
Performance944289
Largest Contentful Paint2.8 s8.6 s3.2 s
Total Blocking Time30 ms580 ms220 ms
Best Practices10061100

Desktop barely moved, going from 100 to 92, because a desktop test has a fast CPU and no network throttling. The damage is almost entirely on slower phones, which is where most of my visitors are.

Why one snippet costs so much

The snippet itself is small. It is a loader that pulls in more code. Lighthouse's third-party summary showed what actually arrived:

  • About 333 KB from Google's ad-serving domains
  • About 81 KB from Funding Choices, Google's consent-message system
  • About 27 KB from an ad-traffic-quality script

That is roughly 440 KB of JavaScript, parsed and run on the main thread during the same seconds the browser is trying to show the page. On a throttled phone, the page's own scripts had to wait their turn, and the largest piece of content took 8.6 seconds to appear.

The Best Practices drop came from inside Google's code, not mine. Lighthouse flagged a third-party cookie set by the ad server and a deprecated unload event listener in one of Google's scripts. Neither can be fixed from the page that loads them.

The fix: load the ads once the page is usable

The idea is simple. Do not load the ad script during the first paint at all. Load it as soon as there is evidence of a real reader, or after a short delay, whichever comes first.

In this Next.js site that is a tiny client component:

"use client";

import { useEffect } from "react";
import { ADSENSE_SRC } from "@/lib/adsense";

const INTERACTIONS = ["scroll", "pointerdown", "keydown", "touchstart", "mousemove"] as const;
const FALLBACK_MS = 4000;

export function AdSenseScript() {
  useEffect(() => {
    const present = () => document.querySelector(`script[src="${ADSENSE_SRC}"]`) !== null;
    if (present()) return;

    let timer = 0;
    const cleanup = () => {
      window.clearTimeout(timer);
      INTERACTIONS.forEach((e) => window.removeEventListener(e, load));
    };
    function load() {
      cleanup();
      if (present()) return;
      const s = document.createElement("script");
      s.src = ADSENSE_SRC;
      s.async = true;
      s.crossOrigin = "anonymous";
      document.head.appendChild(s);
    }

    INTERACTIONS.forEach((e) => window.addEventListener(e, load, { once: true, passive: true }));
    timer = window.setTimeout(load, FALLBACK_MS);
    return cleanup;
  }, []);

  return null;
}

A few choices are deliberate:

  • Any interaction triggers the load. Scrolling, tapping, typing or moving the mouse all count, so a real reader gets ads almost immediately.
  • The four-second fallback covers readers who are simply reading without scrolling.
  • The script is only added once, even when the visitor moves between pages, because the component checks for an existing tag first.
  • The component renders nothing. It does not add any markup, so it cannot shift the layout.

I verified the behaviour in a real browser: no ad script at load, the script present after about four seconds, and present immediately after the first scroll.

What about verification and policy?

Deferring the script does not affect site verification. AdSense accepts either the google-adsense-account meta tag or the ads.txt file, and both stay in place. Google's documentation also allows loading the script asynchronously.

I only render the loader on pages with real content: articles, project pages and the homepage. AdSense policy does not allow ads on error pages or on pages that are mostly navigation or forms, so the contact, legal and 404 pages leave it out.

Should you do this?

If your audience is mostly on phones, yes. Measure first. Run Lighthouse in mobile mode with and without your ad code, compare Largest Contentful Paint and Total Blocking Time, and decide with numbers in front of you.

The trade-off is small. Visitors who leave within four seconds without touching the page will not see an ad. Everyone else will, on a page that loads in about three seconds instead of nine.

#AdSense#Lighthouse#Core Web Vitals#Next.js

Frequently Asked Questions

Does deferring the AdSense script break site verification?

No. Verification can use the google-adsense-account meta tag or the ads.txt file, and both stay in the page and on the server regardless of when the ad script loads.

Will deferring AdSense reduce ad revenue?

Visitors who leave within a few seconds without touching the page will not see ads. Everyone who scrolls, taps or stays a few seconds will. In practice those quick bounces rarely generate revenue anyway.

Why not use the async attribute alone?

async stops the script from blocking HTML parsing, but it still downloads and runs during the critical first seconds, competing with your own scripts and images for bandwidth and CPU.

Sandeep Kumar Chaudhary

Sandeep Kumar Chaudhary

Full Stack Software Developer· Nepal's SEO, AEO, GEO & AIO expert and share-market educator. More about me