Ad Tags Explained: What They Are, How They Work, and Why They Fail
An ad tag is a snippet of JavaScript or HTML embedded in a webpage that tells the browser to make an ad request. When the page loads, the tag calls an ad server (or kicks off a header bidding auction), receives a creative, and renders it in a designated slot. Modern web publishers use Google Publisher Tags (GPT). Mobile apps do not use HTML ad tags. They use SDK-based ad calls instead.
The rest of this article covers the actual serving sequence, GPT specifics, how to debug a tag in production, why ad tags fail to serve even when they fire correctly, and where the mobile/app differences land.
What an ad tag actually is
An ad tag does two things. First, it defines the slot: size, ad unit identifier, and position on the page. Second, it triggers the ad request: the call to your ad server (or to a header-bidding wrapper that runs an auction first).
The tag is not the ad. It is the instruction that fetches the ad. This distinction matters when something goes wrong. "Ad not showing" is not the same problem as "tag not firing." They get debugged differently.
The basic anatomy of any ad tag includes:
- The ad server URL (
securepubads.g.doubleclick.netfor GAM, or a network-specific endpoint) - A publisher or network ID (your numeric GAM network code)
- An ad unit identifier (the path that maps to inventory in your ad server)
- Targeting parameters (key-values, page URL, content category)
- Size declaration (the dimensions the slot expects)
If any of these are wrong or missing, the request still fires, but the response will not match what the slot needs.
How an ad tag works in production: the serving sequence
There are four stages between page load and rendered ad.
Stage 1: tag executes. When the browser parses the page, GPT (or a legacy ad tag) runs. The tag defines the slot and queues the ad request.
Stage 2: request leaves the browser. GPT fires a network call to the ad server. You can see this in Chrome DevTools: a request to securepubads.g.doubleclick.net or pagead2.googlesyndication.com.
Stage 3: auction or direct deal evaluates. On the server side, GAM checks line items, runs Open Bidding, and (if you have Prebid configured) accepts header-bidding bids that came in with the request. Highest eligible bidder wins.
Stage 4: creative renders in the slot. GAM responds with the winning creative's payload. GPT injects it into the div on your page.
If the request leaves the browser but nothing renders in the div, the failure is in stages 3 or 4, not in the tag itself. That is the most common debugging mistake: assuming an unfilled slot is a tag problem when it is actually an inventory or demand problem.
The passback exception is worth flagging. If you have a passback configured, an unfilled primary tag triggers a secondary call (often to a backup network like AdSense or another partner). Passback loops happen when the secondary tag also returns no fill and the chain bounces back to the primary. The result is a cascading delay and no ad. Fix: terminate the passback chain with a house ad or an intentional blank.
GPT ad tags: the current standard
GPT (Google Publisher Tags) is the JavaScript library that manages ad tags on modern web pages. Virtually every GAM publisher uses it. Legacy synchronous DoubleClick tags and iFrame-only tags are mostly gone from production.
A GPT setup loads one library in the <head>:
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
Slots are defined via googletag.defineSlot(), queued in the GPT command queue, and triggered with googletag.cmd.push(). The library batches ad calls, supports lazy loading, and handles SRA (Single Request Architecture) for page-level auctions.
GPT vs legacy tags: a legacy tag loaded a separate script per slot, blocking page render until each ad call returned. GPT consolidates calls into one request (with SRA), loads asynchronously, and never blocks page rendering.
The googletag.cmd.push pattern shows up in every implementation. It is a queue. Commands you push are held until the GPT library finishes loading, then run in order. If you call GPT functions outside the cmd queue and the library has not loaded yet, you hit a googletag is undefined error.
GPT setup breaks silently in three common patterns:
- The slot is defined but
googletag.display()is never called. The slot exists in JavaScript but is never requested. - The slot is defined in the wrong order vs the GPT library load. A bare
googletag.defineSlot()outsidecmd.push()may run before GPT is ready. - SRA is enabled but the page structure prevents it. SRA fires one request for all slots on
enableServices(). If slots are defined dynamically after the initial load, those slots miss the SRA call and need explicitrefresh().
A note on header bidding: Prebid.js sits on top of GPT. Prebid runs the bidder auction first, then passes the winning bid as a price-priority key-value into GPT. GPT then runs its own GAM auction and the winner serves. The ad tag is still a GPT tag. Prebid is a layer above it.
Reading and debugging an ad tag in production
This is the section the typical glossary article skips. It is also the question that makes Reddit threads rank for ad-tag debugging queries.
Tool: Chrome DevTools, Network tab. Filter by googlesyndication, doubleclick.net, or your ad server domain to find outgoing ad calls.
What to look for in the request URL:
- The ad unit path (confirms the right slot is being called)
- The
szparameter (confirms correct size) cust_paramsort(confirms key-value targeting fired)- The
gdprandgdpr_consentparameters (confirms consent signal is attached)
What to look for in the response:
- HTTP 200 with a non-empty body: ad served. Render in DevTools Elements tab to confirm.
- HTTP 200 with an empty body: server responded, returned no fill. This is an inventory or targeting problem, not a tag problem.
- HTTP 4xx or 5xx: rare. Check ad server status, network code typos, or a malformed request.
The most common production failures (in roughly the order I see them):
- The tag fires after the slot div is removed or hidden. Ad renders into an invisible element. Impression counts but no user sees it.
- GPT library not fully loaded when a function is called outside the cmd queue.
- Lazy loading configured with too aggressive an offset. The tag never fires on short pages or on fast-scrolling visitors.
- Size mismatch. The tag declares
[728, 90]but the containing div is 300px wide. Ad serves but is clipped or rejected by demand. - SafeFrame conflict. Creative inside SafeFrame cannot communicate with the parent page, blocking expand or viewability measurement.
- Consent not resolved before the tag fires. The CMP has not yet returned a TCF string, the tag sends with restricted signal, and most demand opts out.
When you have a slot that is "broken," walk through the network call first. If the call goes out and comes back empty, you have the wrong problem in mind. The tag is fine. Look at GAM line items, floor prices, and consent next.
Why ad tags fail to serve: operator caveats
Tag fired is not the same as ad served. Treat them as separate questions.
Fill rate at the tag level vs fill rate at the line item level: you can have 100% tag fire rate and 40% fill. The gap is in the auction, not the tag.
Common causes of zero fill on a live tag:
- The ad unit is not active in GAM
- No line item is targeting the slot (or the targeting excludes the page context)
- Floor price is set too high for current demand
- Geo-block on targeting
- Brand safety category signal is blocking demand
- Ad density on the page exceeds advertiser thresholds (some advertisers block placements above a certain ad-to-content ratio)
VAST tags are a separate specification from display tags. A VAST tag is an XML document, not HTML or JS, that describes a video ad: source URL, duration, click-through, tracking pixels. Publishers serving video in display slots may encounter VAST errors (codes 100 through 901) which have nothing to do with how the display tag fires. If you are debugging a video unit and your display debugging is coming up clean, you are probably looking at the wrong layer.
GPT ad tag vs simpler ad tags: which to use when
Quick decision framework:
- New GAM setup, single ad unit on page: GPT tag, async mode, single slot
- Multiple ad units per page, header bidding: GPT with SRA enabled, Prebid wrapper on top
- AdSense only, no GAM: AdSense auto ads (no manual tag) or AdSense ad unit tag
- AMP pages: AMP ad tag (
<amp-ad>), entirely separate from GPT - Mobile app (iOS or Android): not an ad tag at all. See the next section.
Mobile apps do not use HTML or JavaScript ad tags. Ad serving in apps runs entirely through SDKs. When AdMob, AppLovin MAX, ironSource, or Unity LevelPlay loads an ad, the SDK makes an API call from native code. There is no <script> tag, no GPT, no DOM. Publishers who manage both web and app inventory in GAM see app placements alongside web placements in reporting, but the integration mechanism is completely different. If you are debugging a mobile app ad, DevTools and tag auditing are not the right tools. Check the SDK debug logs on device or in your mediation platform's diagnostics.
What to do this week
If you have a slot that is firing but not rendering:
- Open DevTools, Network tab, filter by
googlesyndication. Confirm the request is going out and check what comes back. - If the response is empty, look at GAM line item targeting and floor prices for the ad unit. The tag is fine.
- If the request is not firing at all, check that the slot is defined inside
googletag.cmd.push(), the slot has been added to the publisher ads service, andenableServices()has been called. - If you are running Prebid, confirm the Prebid
adUnit.codematches the GPT slot's div id. Mismatched and Prebid bids never render. - If you are seeing impressions but no revenue, the issue is downstream (in demand or floor pricing), not in the tag layer.
If your tags are firing clean but fill rate is still low, that is usually a stack configuration issue worth a real look. Book a 30-minute call. I will walk through your setup and tell you where the gap is.
Frequently asked questions
What is the difference between an ad tag and an ad unit?
An ad unit is the named placement defined in your ad server (for example, homepage-leaderboard in GAM). An ad tag is the code that activates that placement on a specific page. One ad unit can be referenced by multiple tags placed across different pages. The ad unit lives in GAM. The tag lives in your page's HTML.
What is a GPT ad tag?
GPT stands for Google Publisher Tags. It is the JavaScript library publishers use to define ad slots and request ads from Google Ad Manager. A GPT ad tag calls the GPT library, defines a slot with an ad unit path and size, and triggers the ad request. It replaced legacy synchronous DoubleClick tags and is the current standard for GAM-based web monetization.
Why did my ad tag fire but no ad appeared?
A tag firing (confirmed in DevTools as a network request to securepubads.g.doubleclick.net) is separate from an ad rendering. Common causes for no render after a successful request: no demand matching the targeting, floor price above what buyers will pay, the creative serving to a hidden or zero-width div, or a consent signal restricting demand. Check GAM's fill rate report for the ad unit. If it shows requests but zero impressions, the issue is in line item setup or targeting, not in the tag.
What is the difference between a synchronous and asynchronous ad tag?
A synchronous tag blocks the browser from rendering the rest of the page until the ad call completes. If the ad server is slow, the whole page waits. Asynchronous tags load independently. The page continues rendering while the ad call runs in parallel. All current GPT implementations are asynchronous. Synchronous tags were standard before 2012 and should not appear in new implementations.
Do mobile apps use ad tags?
No. Mobile apps on iOS and Android do not use HTML or JavaScript ad tags. Ad serving in apps runs through SDK calls made from native code: AdMob SDK, AppLovin MAX SDK, Unity LevelPlay, ironSource, and other mediation platforms. The mechanism is completely different from web ad tags. If you manage both web and app inventory in GAM, both appear in reporting, but the integration paths are entirely separate.
What is a VAST ad tag?
VAST (Video Ad Serving Template) is an XML specification for video ads. A VAST tag is a URL that points to an XML document describing a video creative, its duration, click-through, and tracking pixels. It is unrelated to display ad tags. Publishers serving in-stream or out-stream video use VAST tags, which are handled by the video player (Video.js, JWPlayer, native players in apps), not by GPT. VAST error codes (100 through 901) diagnose video-specific failures and have nothing to do with display tag firing.
Category
Monetization Basics