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.
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
| Macro | What it produces | Use it to… |
|---|---|---|
$$CLICK_REDIRECT_URL$$ | An Epom redirect URL that ends open, waiting for a destination | Prepend 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:
| Variant | Encoding | Use 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
_ESCvariants encode only the Epom redirect URL, not the landing URL you type after them. When you use_ESC/_2_ESCbecause 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
- Macros reference — every
$$…$$token and its_ESCvariants - HTML code and Template banners
- Tags and preview problems
- My ad is not serving
- Invocation code types
- Track impressions of a partner’s ad code — wrapping a partner’s code, impression pixel URLs, render detection
- Troubleshooting overview