$AdSenseSim
data-remove-google-ads

Block Real AdSense.
Four layers deep.

One attribute on the script tag is all it takes. The simulator intercepts every method Google AdSense uses to inject itself — so window.adsbygoogle stays yours and your page never makes a real ad request.

<script
  src="adsense-simulator.min.js"
  data-remove-google-ads="true"
></script>

// how it works

4 Layers of Protection

Each layer is independent. Together they cover every injection vector Google AdSense uses — past, present, and future.

01Layer 1createElement Intercept

Patches document.createElement so that any <script> element trying to set its src to adsbygoogle.js gets the assignment silently dropped. The browser never starts a network request — not even a DNS lookup.

document.createElement = function(tag) {
  const el = native(tag)
  if (tag === 'script') {
    Object.defineProperty(el, 'src', {
      set(value) {
        if (isAdsenseUrl(value)) return // blocked
        nativeSrc.set.call(this, value)
      }
    })
  }
  return el
}
02Layer 2Existing Script Removal

On startup, querySelectorAll scans for any adsbygoogle.js scripts already in the DOM — injected server-side or by a previous script. Each is given a blocked MIME type, its src is cleared, and it is removed before it can run.

document
  .querySelectorAll('script[src*="adsbygoogle.js"]')
  .forEach(el => {
    el.type = 'javascript/blocked'
    el.src  = ''
    el.remove()
  })
03Layer 3MutationObserver Fallback

A MutationObserver watches the full document tree after load. Scripts injected via innerHTML, third-party tag managers, or lazy imports are caught the moment they enter the DOM and blocked before the browser fetches the URL.

new MutationObserver(mutations => {
  for (const { addedNodes } of mutations)
    for (const node of addedNodes)
      if (isBlockableScript(node))
        block(node) // type=blocked, src='', remove()
}).observe(document.documentElement, {
  childList: true, subtree: true
})
04Layer 4Queue Property Trap

Even if adsbygoogle.js fully loads and executes, it cannot replace window.adsbygoogle. An Object.defineProperty getter locks the property to the simulator's queue permanently. Any write attempt is swallowed with a console message.

Object.defineProperty(window, 'adsbygoogle', {
  get() { return simulatorQueue },
  set() {
    // real AdSense tries to replace — blocked
    console.info('blocked attempt to replace')
  },
  configurable: true
})

// live demo

See It Block in Real Time

Toggle the blocker flag and watch the simulator console — every intercept and block attempt is logged live.