---
title: "Fix click tracking on third-party ad tags"
description: "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."
type: "troubleshooting"
audience: [network-manager]
keywords: [click tracking, third-party tag, click redirect, CLICK_REDIRECT_URL, CLICK_URL, cache buster, RANDOM, encoding, ESC, 2_ESC, clicks not counting, impression dedupe]
ui_path: "Account → Advertisers → advertiser → campaign → banner (HTML Code)"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Fix click tracking on third-party ad tags

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](https://epom.com/ad-server/docs/reference/macros.md); for the banner form itself see
[HTML code and Template banners](https://epom.com/ad-server/docs/guides/advertising/banner-html-and-template.md). Macros
are expanded at serve time, so a live impression is the only true test — but
[Preview](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md) 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
<!-- 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:

```html
<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:

```html
<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:

```html
<!-- 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](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md).
- **Preview shows it but the live page doesn't (or vice versa):** see
  [Tags and preview problems](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md).

## 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](https://epom.com/ad-server/docs/reference/macros.md) — every `$$…$$` token and its `_ESC` variants
- [HTML code and Template banners](https://epom.com/ad-server/docs/guides/advertising/banner-html-and-template.md)
- [Tags and preview problems](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md)
- [My ad is not serving](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md)
- [Invocation code types](https://epom.com/ad-server/docs/reference/invocation-codes.md)
- [Track impressions of a partner's ad code](https://epom.com/ad-server/docs/guides/conversions/track-third-party-ad-code.md) — wrapping a partner's code, impression pixel URLs, render detection
- [Troubleshooting overview](https://epom.com/ad-server/docs/troubleshooting.md)