Browse the help center

Fix click tracking on third-party ad tags

Where to put Epom's click-redirect macro and the $$RANDOM$$ cache buster in a pasted HTML/JS or VAST tag so clicks are counted and cached impressions still log fresh.

Troubleshooting network manager Account → Advertisers → advertiser → campaign → banner (HTML Code)

You pasted a third-party HTML/JS or VAST tag into an HTML Code banner, it renders fine, but Epom reports zero clicks — or double-counts impressions on a tag that a page caches. Both are macro-placement problems: a third-party tag doesn’t know about Epom’s click redirector or its cache buster unless you insert them, and they only work in the exact right spot with the exact right encoding.

This page is the concrete Epom recipe. For the full macro catalogue see the macros reference; for the banner form itself see HTML code and Template banners. Macros are expanded at serve time, so a live impression is the only true test — but Preview shows the substituted markup.

Who this is for

Ad-ops users and advertisers trafficking third-party creatives through the HTML Code banner type. Requires knowing your tag’s own structure — which part is the click-through and which is the impression pixel.

Two macros do two different jobs — don’t mix them up

MacroWhat it producesUse it to…
$$CLICK_REDIRECT_URL$$An Epom redirect URL that ends open, waiting for a destinationPrepend to the landing URL so the click is counted, then forwarded
$$CLICK_URL$$An Epom click-tracking pixel URL (counts a click, redirects nowhere)Fire from custom JS when you can’t wrap the click destination

The redirect form is what you want in almost every third-party tag. Under the hood it expands to a URL that terminates in an empty redirect parameter (…&u=); the ad server treats whatever follows the macro as the destination, counts the click, then 302-forwards there. That single fact drives every rule below.

Corner case: if the banner has no click-through configured, $$CLICK_REDIRECT_URL$$ and $$CLICK_URL$$ resolve to nothing and are removed from the markup. A tag that relies on them then has no click tracking at all. Make sure the banner (or the tag) actually supplies a destination.

Fix 1 — clicks not counting: prepend the redirect at the very start

Because the redirect URL ends open, the click macro must sit at the very beginning of the click destination, immediately in front of the landing URL. Anything between the macro and the URL becomes part of — or breaks — the redirect target.

Find the click-through in your tag (the href, the clickTAG/clickTag variable, or the tracker’s click/redir/url parameter) and put the macro first:

<!-- HTML/JS creative — wrap the click-through -->
<a href="$$CLICK_REDIRECT_URL$$https://brand.example.com/landing">…</a>

<!-- clickTAG-style creative -->
var clickTag = "$$CLICK_REDIRECT_URL$$https://brand.example.com/landing";
  • Do not URL-encode the landing URL yourself when it sits directly after the plain $$CLICK_REDIRECT_URL$$ — the server handles that final hop.
  • Do not put anything (a ?, a space, another parameter) between the macro and the destination. The macro already ends in …&u=.

Corner case: a tag that already had its own click redirector still needs Epom’s macro in front of that redirector so the click passes through Epom first, then the third party, then the landing page. Chaining order is left-to-right in the string.

Fix 2 — the landing URL is inside another tracker: pick the right _ESC variant

When the destination isn’t a bare URL but is itself a parameter of another tracker (an ad-verification or a DSP click tracker that will decode it before redirecting), the redirect value gets URL-decoded one or more times on the way. Match the encoding to the number of decode hops:

VariantEncodingUse when the click passes through…
$$CLICK_REDIRECT_URL$$none…nothing — Epom is the only redirector, landing URL follows directly
$$CLICK_REDIRECT_URL_ESC$$single URL-encode…one external tracker that decodes once before redirecting
$$CLICK_REDIRECT_URL_2_ESC$$double URL-encode…a tracker that decodes twice (nested trackers, some DSP macros)

Example — the Epom click sits inside a third-party tracker’s url= parameter, which will decode it once. The macro encodes the Epom URL, so encode the landing URL you append to the same depth:

<a href="https://tracker.example/click?id=42&url=$$CLICK_REDIRECT_URL_ESC$$https%3A%2F%2Fbrand.example.com%2Flanding">…</a>

If clicks land on a broken/truncated page, you have the wrong encoding level: too little encoding and the outer tracker eats part of the URL at its decode step; too much and the final URL arrives still-encoded. Step the variant up or down one level and re-test.

Corner case: the _ESC variants encode only the Epom redirect URL, not the landing URL you type after them. When you use _ESC/_2_ESC because an outer tracker will decode, encode the landing portion to the matching depth yourself, or let the outer tracker’s own macro supply it.

Fix 3 — pixel-only counting when you can’t wrap the click

Some tags expose no click-through you can prefix (a self-contained iframe creative whose click is handled internally). Count the click with the pixel form instead — fire $$CLICK_URL$$ from the creative’s own click handler:

<img src="$$CLICK_URL$$" width="0" height="0" style="display:none" alt="">
<!-- or, from JS on click: new Image().src = "$$CLICK_URL$$"; -->

This registers the click in Epom without redirecting; the creative still navigates the user itself. Use $$CLICK_URL_ESC$$ if the pixel URL goes into a parameter that is decoded.

Fix 4 — impressions double-counting or not deduping: add $$RANDOM$$

A third-party impression pixel or an iframe src that a browser or CDN caches will be re-served from cache — the third party then under- or mis-counts, and stale creatives can reappear. Add a cache buster: $$RANDOM$$ expands to a fresh random integer (0–2147483647) on every impression, so each request URL is unique and bypasses the cache.

Put it on the query string of any URL the third party wants fetched fresh — its impression pixel, its tag src, its own cb/rand/ord cache-buster slot:

<!-- third-party impression pixel -->
<img src="https://verify.example/imp?id=42&cb=$$RANDOM$$" width="1" height="1" alt="">

<!-- iframe/script tag the vendor wants uncached -->
<iframe src="https://vendor.example/tag?ord=$$RANDOM$$"></iframe>
  • Replace the vendor’s own cache-buster placeholder (often [timestamp], %n, ${CACHEBUSTER}, ord) with $$RANDOM$$ — vendors expect you to fill it.
  • Use $$RANDOM(min, max)$$ if the vendor requires a bounded value: the start is inclusive and the end exclusive, so $$RANDOM(0, 100)$$ gives 0 to 99.

Corner case: $$RANDOM$$ only changes per served response. It does not help against an iframe the page keeps in the DOM and never re-requests — that’s a page-side caching issue, not a macro one. The macro guarantees a fresh value each time the ad server builds the markup; it can’t force the browser to re-fetch a cached node.

When the fix doesn’t stick

  • Verify in Preview first. Open the banner’s Preview button — the click and random macros should be replaced by a real Epom redirect URL and a number. If a macro is still literal ($$CLICK_REDIRECT_URL$$ shown verbatim), it’s mistyped or in a context the processor doesn’t reach; check spelling and the $$…$$ delimiters.
  • Confirm the banner has a destination — see the first corner case; a missing landing URL silently strips the click macros.
  • Still zero clicks with a good redirect? The problem may be upstream — the banner isn’t serving at all. Run My ad is not serving.
  • Preview shows it but the live page doesn’t (or vice versa): see Tags and preview problems.

Per-role notes

  • Shared creatives: a banner shared at Can view opens with its markup Read-only and no Save button — but Preview still renders the substituted markup, so you can read the redirect URL the macro produced and hand the owner a diagnosis rather than a guess.

Related pages

Please disable Adblock
Are you using adblock? Disable it and reload the page to proceed to the destination page.

A registration has already been submitted from this page. Please try again in 10 minutes.