AdSense Script Installed but Slots Stay Blank

Script loaded, but ad slots empty for hours. Slot config or page eligibility.

You’ve added <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-..."> and the <ins class="adsbygoogle"> tag. DevTools Network confirms adsbygoogle.js returned 200. But after refreshes, after hours, the ad slot stays a blank <ins> block. This is the most “looks correct, isn’t” failure mode in AdSense — and almost always traces to one of six wiring issues, not a code bug.

The diagnostic key is the data-ad-status attribute the AdSense script sets back onto your <ins> tag. Once you read that, the fix is usually 5 minutes.

Common causes

Ordered by hit rate, highest first.

1. (adsbygoogle = window.adsbygoogle || []).push({}) never fires

The script loads, but the push({}) call that activates the slot is missing or in a place that never runs (inside a route change handler that didn’t fire, gated by an if, removed during SSR vs hydration).

How to spot it: In DevTools console: window.adsbygoogle.loaded. If undefined or false, the push never ran. Also: the <ins> will have no data-ad-status attribute at all (not even unfilled).

2. Slot rendered with width = 0

The <ins> element exists in a parent that’s display: none, has 0 width on the current breakpoint, or is inside a tab/accordion that isn’t open. AdSense rejects 0-width slots silently and the slot stays blank.

How to spot it: Right-click → inspect the <ins>. Computed → width. If 0px, that’s it. Common in flex containers that collapse and width: max-content boxes around the ad.

3. Multiple <script async ...adsbygoogle.js> tags

If your layout has the AdSense script in two places (e.g., once in the head, once in a component), it loads twice and the second one can fail to bind correctly. The slots end up half-pushed.

How to spot it: Array.from(document.querySelectorAll('script[src*="adsbygoogle"]')).length in console. Should be 1.

4. <ins> markup is invalid

Common typos: data-ad-clinet (sic), data-ad-slt, missing class="adsbygoogle", missing style="display:block", or React mangling kebab-case to camelCase when you wrote dataAdClient.

How to spot it: View source (not React render tree). The actual HTML must contain exactly class="adsbygoogle" and data-ad-client="ca-pub-...".

5. Page is below AdSense’s content threshold

Some pages — a 50-word “Hello world” stub, a category page that’s just a link list with no description, a search results page — don’t have enough content to host an ad. AdSense’s data-ad-status="unfilled" will fire.

How to spot it: Compare to a known-good article on the same site. If long articles fill but thin pages don’t, content threshold is the cause.

6. Your IP or test session is flagged

If you’ve been F5-ing the page to test ads, AdSense may rate-limit serving on your IP for the day. You’ll see ads from another network (mobile data, friend’s laptop) but not your own.

How to spot it: Open the page from your phone on cellular data (different IP). If ads serve there, you’re being rate-limited locally.

Shortest path to fix

Step 1: Read data-ad-status from the rendered <ins>

In console:

document.querySelectorAll('ins.adsbygoogle').forEach((el, i) =>
  console.log(i, el.getAttribute('data-ad-status'), el.offsetWidth));
StatusWidthMeaning
undefinedanyPush never ran (cause #1 or #4)
unfilled> 0Push ran, auction returned nothing (cause #5 or #6)
unfilled0Width problem (cause #2)
filled> 0Working — check ad blockers if you don’t see it

Step 2: Force the push call (test it manually)

In console:

(window.adsbygoogle = window.adsbygoogle || []).push({});

If a slot fills now but didn’t before, your push code wasn’t running. For an SPA / Next / Astro page, place the push in:

<ins class="adsbygoogle" ...></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>

Inline, immediately after the <ins> tag. SPA route changes: re-run the push on route change.

Step 3: Fix the markup

Use this exact template; do not change attribute names:

<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
     data-ad-slot="1234567890"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>

In React / Astro, write attributes as dangerouslySetInnerHTML or as kebab-case props your framework preserves (Astro keeps them as-is; React requires data-ad-client).

Step 4: Remove duplicate script tags

grep -rn "adsbygoogle.js" src/ public/

Should appear in exactly one layout / template file. Delete duplicates.

Step 5: Force min-width on slots

.adsbygoogle {
  display: block;
  min-width: 250px;
  min-height: 100px;
}

Don’t put ads inside display: none until visible. Use visibility: hidden only if needed.

Step 6: Test from a fresh IP

Phone on cellular, friend’s network, or a VPN. If it serves there, AdSense will recover for you in 24 hours — stop reloading your own site to test.

Prevention

  • Use a single <AdSlot> component / partial. All slot markup goes through it. No copy-paste.
  • Pin the AdSense script to one place — root layout or document head.
  • Lint that every <ins class="adsbygoogle"> has a matching push({}) call.
  • Add a CSS min-width: 250px rule for .adsbygoogle in your global stylesheet.
  • Don’t reload the same page repeatedly when testing — use a phone or another browser.

Tags: #AdSense #Monetization #Debug #Troubleshooting