# Why your Matomo Campaigns report is empty (and why the URL says gad_source=1)

> Two campaign-tracking symptoms come up constantly in Matomo 5.x: the Campaigns report stays empty even though tagged links get visits, and the visit log shows ?gad_source=1 instead of your UTM parameters. Neither is usually a bug. Here's the mechanism behind each and exactly how we fix them.

Published: 2026-07-13
Categories: Matomo Tracking
Canonical: https://martez.io/blog/matomo-campaign-tracking-utm-gad-source

---

You tagged your links, the visits are clearly landing, and **Acquisition → Campaigns** is still empty. Or you open a visit in the log and the landing page reads `/pricing?gad_source=1`, with none of the `utm_source` and `utm_campaign` values you appended. Just that one stray parameter clinging to the URL. Both look like Matomo quietly threw your campaign data away.

It usually didn't. The empty report is almost always a tracking-side config problem you can find in about thirty seconds, and the `gad_source=1` URL is two normal behaviours happening at once. Different problems, different fixes, so we'll take them one at a time.

<Callout type="tip" title="TL;DR">

If your Campaigns report is empty, open the `matomo.php` request in DevTools and read its `url=` value. If your `utm_*` params aren't in there, the problem is on the page or in the tracker, not in Matomo's reports: usually `disableCampaignParameters` in the snippet, `setCustomUrl` sending a bare path, or a `utm_*` param on an exclusion list. If they are in there, it's a Matomo-side setting. The `gad_source=1` is a separate, harmless thing: a Google Ads parameter Matomo doesn't recognise, so it lingers in your stored URLs until you add it to the excluded parameters.

</Callout>

## Symptom 1: the Campaigns report is empty

When tagged links get traffic but the Campaigns report stays blank, the campaign parameters aren't reaching Matomo's tracker. Before you touch any Matomo setting, confirm that in the browser. It tells you which side of the wire the problem is on.

Open your landing page with a real campaign URL, open DevTools (F12) → **Network**, and filter for `matomo.php` (or whatever your tracker endpoint is). Click the tracking request and look at the `url=` parameter it sent:

- If `url=` is **missing** your `utm_*` / `mtm_*` params, the tracker never saw them. The problem is on the page, in the tracking code, or in something cleaning the URL before Matomo reads it.
- If `url=` **contains** them but the report is still empty, the tracker is fine and the issue is on the Matomo side: a reporting or config setting.

That one check splits the whole problem in half. Here's what to look at on each side.

### When the params are missing from `url=`

**`disableCampaignParameters` is in your tracking code.** Matomo 5.1 added `disableCampaignParameters` as a privacy switch. When it's present, the JavaScript tracker deliberately strips campaign parameters out of the request before sending it, so campaigns are never recorded:

```js
// If this line is in your snippet, campaign params never reach Matomo.
_paq.push(['disableCampaignParameters']);
```

This is the single easiest cause to miss, because everything else in the snippet looks correct. Grep your tracking template and your tag manager for `disableCampaignParameters` and pull it out unless a privacy requirement genuinely needs it. There's no recovering campaign data for visits tracked while it was active, because the parameters never left the browser. Fix the code first, then move on.

**`setCustomUrl` is dropping the query string.** On SPA and React/Next/Vue sites, it's common to push the path by hand. If you send only the pathname, you throw the campaign params away before `trackPageView` fires:

```js
// Wrong: discards ?utm_source=... entirely
_paq.push(['setCustomUrl', window.location.pathname]);

// Right: keeps the full URL, query string included
_paq.push(['setCustomUrl', window.location.href]);
```

**Your campaign params are on the excluded list.** This is Matomo's own first answer in the [official "missing campaign links" FAQ](https://matomo.org/faq/troubleshooting/missing-campaign-links-in-campaign-reports/), and it bites people who got aggressive with URL cleanup. Check two places: **Administration → Websites → Manage → Edit → Excluded Parameters** for the site, and the Matomo Tag Manager configuration variable's advanced **"Set query params to exclude from URL"** field if you track through MTM. If a `utm_*` param is sitting in either of those, Matomo strips it from the URL before it can be read as a campaign.

**On self-hosted installs, check `exclude_requests` too, but know it's a different thing.** The `[Tracker] exclude_requests` setting in `config/config.ini.php` doesn't clean up query parameters; it discards whole tracking requests that match an expression (for example `url=@utm_`). It's rarely the cause, but if someone wrote a rule that happens to match your campaign landing pages, those hits never get recorded at all, which looks identical to an empty Campaigns report.

**Something upstream is cleaning the URL.** A Cloudflare "Cache Everything" rule, a reverse-proxy rewrite that returns 200 while dropping the query string, or a consent-management script that "tidies" the URL before the tracker runs will all remove the params without leaving a trace in your code. If you self-host behind a proxy and see other oddities too, our note on [reverse-proxy 502s and header handling](/blog/self-host-matomo-reverse-proxy-502) covers the same class of "the proxy ate part of the request" problem.

### When the params are present but the report is still empty

If `url=` already carries your `utm_*` values, the tracker is doing its job and the problem is on the Matomo side. Work through these roughly in order:

- **Right site and date range.** The dull one that catches everybody at least once: the report is empty because the wrong site is selected, or the date range predates the campaign.
- **Archiving.** Reports populate from archived data, so a recent campaign may not appear until an archive run completes. Trigger one before you conclude the data isn't there. If your archiving isn't running on a schedule at all, our [cron archiving setup](/blog/set-up-matomo-cron-archiving) covers that.
- **Custom campaign parameter overrides.** If someone changed which parameters Matomo treats as the campaign, the defaults quietly stop working. That can live in `[Tracker]` or `[MarketingCampaignsReporting]` config, or in the JavaScript via `setCampaignNameKey` / `setCampaignKeywordKey`. Make sure your links and Matomo agree on which parameter carries the campaign name and keyword.
- **Privacy or compliance mode.** A CNIL-style compliance configuration can discard campaign values as they arrive. If you turned one on for legal reasons, an empty Campaigns report may be working as designed.
- **The Marketing Campaigns Reporting plugin**, but only if you're missing the *richer* reports, not all campaign data. Matomo core already recognises `utm_campaign`, `utm_source`, and `utm_medium` for the campaign name and `utm_term` for the keyword. The free [Marketing Campaigns Reporting](https://plugins.matomo.org/MarketingCampaignsReporting) plugin adds separate reports for campaign name, source, medium, keyword, content, and campaign ID, including `utm_content` and `utm_id`. It's a Marketplace install on self-hosted Matomo, not bundled with core, so confirm it's installed and active if the basic name and keyword data shows up but the channel breakdown doesn't.

This is the same "tracking fires but the report stays empty" situation we wrote up for [ecommerce orders that don't show up](/blog/matomo-ecommerce-not-tracking). The request reaching the tracker and the report populating are two separate gates, and it helps to know which one you're stuck at before you start changing things.

## Symptom 2: the URL shows gad_source=1 instead of your UTM params

This one isn't broken at all. It's two normal behaviours happening together.

**Matomo strips recognised campaign params out of the stored page URL by design.** Once `utm_source`, `utm_campaign`, and friends are digested into the Campaign dimension, Matomo drops them from the page URL it stores. That's intentional. It stops one page from fragmenting into dozens of variants (`/pricing?utm_campaign=a`, `/pricing?utm_campaign=b`, and so on) in your Pages report. Your attribution is fully intact at the visit level. The campaign is recorded; it's just no longer duplicated in the raw URL. This is expected behaviour, not data loss.

**`gad_source=1` comes from Google, and Matomo doesn't recognise it.** Google Ads and Google's tags add their own parameters to ad-click landing URLs. `gad_source` is one of them; auto-tagging also adds the classic `gclid` click identifier, and you'll see `gbraid` or `wbraid` in specific measurement contexts. The detail that matters here is the same for all of them: none are Matomo campaign parameters, so Matomo never digests or strips them. That's why `gad_source=1` is the one query param still hanging off the URL in your visit log. Nothing is removing your UTMs *instead of* showing `gad_source`. The UTMs were absorbed into the Campaign dimension, and `gad_source` just survived because nothing was looking for it.

<FlowSteps
  caption="One landing URL splits two ways: the UTM params become campaign data, the unrecognised Google param stays on the stored page URL."
  steps={[
    { title: "Landing URL", detail: "utm_source, utm_campaign, gad_source" },
    { title: "Matomo tracker", detail: "reads url= in matomo.php" },
  ]}
  outcomes={[
    {
      label: "recognised",
      title: "Campaign dimension",
      detail: "source / medium / campaign",
      tone: "ok",
    },
    {
      label: "not recognised",
      title: "Stays on the stored Page URL",
      detail: "/pricing?gad_source=1",
      tone: "warn",
    },
  ]}
/>

So your campaign attribution still works. Check the Campaigns report and the visit-level referrer if you want to see it. The `gad_source=1` is just unstripped Google noise sitting in your Pages report.

To get it out of Matomo's stored Page URLs and the Pages report, add it to your excluded parameters. This only changes what Matomo stores from now on. It doesn't rewrite the visitor's browser URL, touch your Google tags, or clean up URLs already sitting in your database:

```text
gad_source
gclid
gbraid
wbraid
```

Per site, that's **Administration → Websites → Manage → Edit → Excluded Parameters**. To cover every site at once, use **Administration → Websites → Settings → "Global list of Query URL parameters to exclude"** and put one parameter per line. Either way, the rule only touches new traffic, so your historical Pages report won't tidy itself up.

If you run several properties, this is exactly the kind of setting that drifts out of sync between them. Our walkthrough of [managing multiple sites, domains, and measurables](/blog/matomo-multiple-sites-domains-measurables) goes into keeping per-site config aligned.

## If you actually need per-campaign page detail

Some teams want to compare the *same* page across different campaigns, and the URL stripping seems to take that away. Don't fight it by un-stripping the params and re-polluting your Pages report. Use **Segments** instead, one per campaign name, plus the segment **comparison** toolbar in the report view. You get the per-campaign breakdown without splitting every page into a dozen URL variants. It's the clean version of what people are usually reaching for when they ask to keep the params on the URL.

## What we'd actually do

Run the thirty-second DevTools check first, every time. Whether `url=` carries your campaign params decides everything downstream, and it stops you from changing Matomo settings to fix a problem that lives in the page. If the params are gone there, hunt the tracking side: `disableCampaignParameters` in the snippet, `setCustomUrl` sending a bare pathname, or a `utm_*` param sitting on an exclusion list.

For `gad_source=1`, add the Google parameters (`gad_source`, `gclid`, and friends) to your excluded list and leave the by-design UTM stripping alone. It's keeping your Pages report sane, not eating your data. And standardise on one prefix across all your links, either Matomo-native `mtm_*` or Google-style `utm_*`, so you're never debugging mixed attribution on top of everything else.

If JavaScript tracking keeps mangling your campaign URLs no matter what you do, whether that's a heavy CMP, a stubborn CDN, or an SPA you can't fully control, moving campaign attribution to the [server-side HTTP Tracking API](/blog/matomo-http-tracking-api-server-side) takes the browser out of the equation. It's more work, but the params arrive exactly as you send them.

[Martez](/?utm_source=martez&utm_medium=blog&utm_campaign=matomo-campaign-tracking-utm-gad-source) connects Matomo with Meta Ads and Google Ads so ROAS, CLV, and attribution sit next to your web analytics instead of in a separate spreadsheet. It's in private beta. [Join the waitlist](/signup?utm_source=martez&utm_medium=blog&utm_campaign=matomo-campaign-tracking-utm-gad-source) if that's relevant.

Your UTMs are only really missing if they're missing from the `url=` value sent to Matomo. If they're there, Matomo already pulled them into your campaign data, and `gad_source` is just an unrelated Google parameter waiting for a cleanup rule.
