ghostQR

Campaign measurement

How to Track QR Campaigns in GA4 with UTM Parameters

Give each placement its own campaign link and follow the visit beyond the scan.

By GhostQR ·

A QR code can get someone to your website, but Google Analytics 4 needs campaign context to tell you which printed placement brought them there. The practical setup is simple: add UTM parameters to the destination URL, create a separate dynamic QR code for each placement, then compare those sessions in GA4.

GhostQR does not connect to or configure GA4. It redirects each scan to the complete destination you provide, including its query string. Your own website's Google tag then decides whether that landing visit is measured.

UTM-tagged destinations work with static QR codes too. This walkthrough uses dynamic codes for editable destinations and separate GhostQR scan totals; those require a paid plan. If you only need website measurement, you can encode a tagged URL with the free static generator.

Choose one UTM naming plan

Google's official custom campaign URL guide says to include utm_source, utm_medium, and utm_campaign. It also documents utm_content for distinguishing creative variants. For a print campaign, use a short, consistent vocabulary:

utm_source=qr
The visit came through a QR code.
utm_medium=offline
The broader channel was physical media.
utm_campaign=spring_menu_2026
The same campaign name across every placement.
utm_content=counter_sign
The specific placement or creative.

UTM values are case sensitive. Pick lowercase names, use underscores consistently, and keep a register of every URL you publish. Spring_Menu and spring_menu can become separate report rows. Do not put names, email addresses, order numbers, or other personal information in UTM values because they can appear in URLs and analytics systems.

Download the QR campaign UTM template to keep the naming plan beside the print quantity and launch date.

Build two placement URLs

Suppose a cafe promotes one spring menu on a counter sign and a takeaway flyer. Both links share the source, medium, and campaign. Only utm_content changes. This lets GA4 roll both placements into one campaign while preserving the placement detail.

Example destinations. These URLs and all later results are illustrative.
PlacementDestination saved in GhostQR
Counter signhttps://example.com/spring-menu?utm_source=qr&utm_medium=offline&utm_campaign=spring_menu_2026&utm_content=counter_sign
Takeaway flyerhttps://example.com/spring-menu?utm_source=qr&utm_medium=offline&utm_campaign=spring_menu_2026&utm_content=takeaway_flyer

Keep these as two separate codes even though they open the same page. The code-level split gives you independent scan totals in GhostQR, while utm_content gives you the matching placement dimension in GA4. Reusing one printed code on both items removes that distinction.

Create, test, and publish each code

  1. Confirm that GA4 is installed on the destination site. Google's website setup guide covers creating a web data stream and installing the Google tag.
  2. In the GhostQR dynamic generator, create one code named “Spring menu - counter sign” and save the counter URL above.
  3. Create a second code named “Spring menu - takeaway flyer” and save the flyer URL. Do not merely download the first QR image twice.
  4. Scan each code from a test print. Confirm the browser lands on the intended page and that its address contains the expected UTM values. Test the physical size, contrast, lighting, and scanning distance you plan to use.
  5. Check GA4 Realtime for a quick installation check. Google says Realtime covers the last 5 and 30 minutes, but its traffic cards use first-user dimensions, so use the session-scoped Traffic acquisition report for campaign comparison.

If your landing page redirects again, verify that the next redirect preserves the query string. A redirect that drops everything after ? removes the campaign tags before GA4 can read them.

Find QR sessions in GA4

Open Reports → Acquisition → Traffic acquisition. Google describes this as the report for where new and returning users' sessions came from. Change the primary table dimension to Session campaign and search for spring_menu_2026. You can also inspect Session source / medium for qr / offline. These field names and the report path come from Google's Traffic acquisition documentation.

For placement detail, Google maps utm_content to Session manual ad content. Build a table using its free-form exploration:

  1. Open Explore → Free form and choose the campaign date range.
  2. Import Session campaign and Session manual ad content under Dimensions, and Sessions and Key events under Metrics.
  3. Add Session manual ad content to Rows and the metrics to Values.
  4. Filter Session campaign to exactly match spring_menu_2026. The placement rows should use the two values from your register.

Report availability depends on your property and permissions. The custom medium offline does not create a new default channel: it may appear as Unassigned in the default channel grouping. Use the campaign and source / medium dimensions above, or configure a custom channel group. Google explains the channel matching rules.

Illustrative comparison after both placements have run for the same seven days.
PlacementGhostQR scansGA4 sessionsGA4 key events
Counter sign14211817
Takeaway flyer96738

In this example, the counter sign produced more measured visits and key events. That is a useful lead, not proof that the sign caused every outcome. Compare equal date ranges, print quantities, audience exposure, offer, and landing page before changing the whole campaign.

Why scans and sessions do not match

A GhostQR scan is a request to a dynamic short link. A GA4 session begins only when the destination page loads and its Google tag sends measurement. Someone can close the tab, lose connectivity, block scripts, deny analytics consent, or scan repeatedly. Test scans and automated requests may also raise the short-link count. Those differences make a one-to-one match unlikely.

Google explains that GA4's default web setup uses first-party cookies to distinguish users and sessions, and that consent settings affect storage and collection. Follow the privacy and consent rules that apply to your visitors. Do not treat a missing GA4 session as evidence that GhostQR failed to redirect.

Standard reports can also lag. Google's data freshness guide says processing can take 24 to 48 hours and report data may change during that period. Use Realtime as an initial signal, then evaluate the completed date range in Traffic acquisition.

Use both layers of measurement

GhostQR scan analytics tells you that the printed short link was requested, with separate results for each dynamic code. GA4 tells you what the measured website session did after arrival, including engaged sessions and key events you have configured. Together they show more than either layer alone.

Start with two placements, one naming convention, and one decision you want the data to support. Compare dynamic-code plans or, if you already have one, create your campaign codes.


GHOSTQR © 2026 — ALL RIGHTS RESERVEDCHANGE THE LINK. NOT THE PRINT.V0.1.0 · MADE IN AUSTRALIA