UTM & URL Query String 2 Cookies

UTM & URL Query String to Cookies in Google Tag Manager — A Complete Recipe Without Custom JavaScript

Persist any URL query string parameter — UTM values, click IDs, or custom campaign codes — into session or long-lived persistent cookies using only GTM sandboxed Custom Templates. No Custom HTML tags. No new CSP hashes.

Published · Tags: GTM, Google Analytics 4, UTM, Cookies, Query String, dataLayer, CSP, GA4, First-Touch Attribution, Lead Attribution

UTM parameters tell you exactly how a visitor arrived at your site. But they only exist in the URL on the landing page. The moment a user navigates to a second page, clicks an internal link, or returns to the site in the same browser session, those parameters vanish from the address bar. If your marketing team needs to know which campaign drove a lead form submission that happened three days after the original visit — a common scenario in B2B with longer consideration cycles — you cannot read the UTMs from the URL. They are gone.

This recipe solves that problem cleanly. On the landing page visit, it reads any nominated URL query string parameter and writes its value into a browser cookie — either a session cookie that expires when the browser closes, or a persistent cookie that survives for up to 730 days. Those cookie values are then available to every subsequent page view and every GTM event throughout the user's future visits, ready to be attached to GA4 events, read by form scripts, or forwarded in dataLayer pushes. No custom JavaScript. No new Content Security Policy SHA-256 hashes required.

⬇ Download the GTM Container JSON from Google Drive

The Core Problem: UTMs Live in the URL, Not in the Session

A visitor lands on https://example.com/?utm_source=newsletter&utm_medium=email&utm_campaign=spring-offer. GA4 picks up those parameters automatically for the page_view event. Three days later, in a fresh browser session, the same visitor returns directly to https://example.com/pricing and submits a lead form. What UTM values does your CRM receive? Without a persistence layer: none. GA4's session attribution helps for analytics, but it does not make the original UTMs available to your form submission handler or marketing automation platform.

Cookies are the browser's built-in persistence mechanism for exactly this purpose. Writing the UTM values to cookies on the first visit and reading them back on the form submission page gives you first-touch attribution data that survives across sessions, browser restarts, and multi-day gaps — without requiring any server-side infrastructure.

Why this matters for CSP: The traditional approach to writing cookies in GTM is a Custom HTML tag containing an inline <script> block. Any such script requires its SHA-256 hash to be listed in your Content-Security-Policy header's script-src directive. Every time the script changes — even whitespace — the hash changes and must be redeployed. This recipe uses only GTM sandboxed Custom Templates, which execute inside GTM's own sandbox and require zero additions to your CSP. The cookie-writing and cookie-rewriting logic runs entirely within the GTM runtime — no new hashes, no security team review cycles.

What's Inside the Container

The exported container (utm-and-url-query-string-2-cookies-recipe.json) is organised into three folders of assets, plus the existing Analytics infrastructure and three sandboxed Custom Templates authored by drewspen.

Folders

FolderContains
Analytics GA4 configuration tag, shared settings variables (including UTM cookie reads), environment/stream routing variables, and client ID and timestamp utilities.
Process URL query strings 2 cookies Two tags (session and user/persistent), plus ten Cookie Variable variables reading the values back from each cookie. Fires at page load to capture incoming UTM parameters.
Extend Cookie Life One tag and two supporting variables. Rewrites the persistent UTM cookies on every page visit to refresh their expiry date, ensuring the 730-day window resets on each visit rather than counting from first write.

Tags

TagTypePurpose
Google Analytics Configuration Google Tag (googtag) Standard GA4 config tag, fires once per load. Shared Event Settings automatically forward UTM cookie values as GA4 event parameters on every hit.
Write certain URL query strings 2 cookies session Custom Template — Write URL query strings 2 cookies Fires on all pages. Reads utm_source, utm_medium, utm_campaign, utm_content, and utm_term from the URL query string and writes each as a session cookie (expires when the browser closes). Cookie names: utm_source, utm_medium, utm_campaign, utm_content, utm_term.
Write certain URL query strings 2 cookies user Custom Template — Write URL query strings 2 cookies Fires on all pages (configured with no trigger override — uses the All Pages trigger inherited from the session tag). Reads the same five UTM parameters and writes them as persistent cookies expiring in 730 days (2 years). Cookie names have a _1 suffix: utm_source_1, utm_medium_1, utm_campaign_1, utm_content_1, utm_term_1.
Extend Cookie Life Tag Custom Template — Cookie Rewriter Fires on DOM Ready, but only if it has not already run in the current page load (guarded by the _arrewr sentinel cookie). Rewrites all ten persistent UTM cookies — both the _1 and _u variants — resetting their expiry to 720 days from now.

Triggers

TriggerTypePurpose
All Pages (built-in) Page View Fires the two Write query strings tags on every page load so incoming UTM parameters are always captured.
Extend Cookie Life Trigger DOM Ready Fires the Cookie Rewriter tag, but only when the _arrewr sentinel cookie is absent or empty — ensuring the cookie extension runs exactly once per page, not on every DOM mutation.

Custom Templates (by drewspen)

TemplatePurpose
Write URL query strings 2 cookies Reads nominated URL query string parameters and writes them to browser cookies, with configurable expiry (session or persistent), cookie name suffix, root domain scoping, and optional URI decoding. Runs entirely within the GTM sandboxed template runtime — no Custom HTML, no CSP implications.
Cookie Rewriter Rewrites a specified list of existing cookies with a refreshed expiry date, allowing persistent cookies to behave as rolling windows that extend on each visit rather than fixed windows from first write.
If Else If — Advanced Lookup Table (Sublimetrix) Powers coalesce and routing logic elsewhere in the container.
Timestamp (Sublimetrix) Reads Unix timestamps from GA4 session and client cookies for shared event parameters.
Get Root Domain Extracts the registrable root domain for cookie scoping, ensuring cookies are accessible across subdomains (e.g. www.example.com and app.example.com).

Two Tracks: Session Cookies and Persistent Cookies

The recipe deliberately writes each UTM parameter twice — once as a session cookie and once as a persistent cookie. This gives you two distinct attribution windows in a single implementation.

Session Cookies — Current Visit Attribution

Session cookies expire automatically when the browser is closed. They hold the UTM values for the current visit only. This makes them ideal for "last-touch within session" attribution: if a user clicks a paid search ad, lands on the site, navigates through three pages, and completes a transaction, the session cookie carries utm_source=google and utm_medium=cpc all the way through the checkout flow — even though only the landing page URL contained those parameters.

Cookie NameTypeContent
utm_sourceSessionValue of utm_source query parameter on landing
utm_mediumSessionValue of utm_medium query parameter on landing
utm_campaignSessionValue of utm_campaign query parameter on landing
utm_contentSessionValue of utm_content query parameter on landing
utm_termSessionValue of utm_term query parameter on landing

Persistent Cookies — First-Touch Attribution Across Visits

Persistent cookies survive browser restarts and are retained for up to 730 days (2 years) from initial write. The _1 suffix in the cookie name is the distinguishing convention: these cookies hold the values from the first tracked landing, making them the foundation of first-touch attribution. When the Cookie Rewriter runs on subsequent visits, it refreshes the expiry without overwriting the values — the original campaign data is preserved.

Cookie NameTypeExpiryContent
utm_source_1Persistent730 days (rolling)Value of utm_source on first UTM-tagged landing
utm_medium_1Persistent730 days (rolling)Value of utm_medium on first UTM-tagged landing
utm_campaign_1Persistent730 days (rolling)Value of utm_campaign on first UTM-tagged landing
utm_content_1Persistent730 days (rolling)Value of utm_content on first UTM-tagged landing
utm_term_1Persistent730 days (rolling)Value of utm_term on first UTM-tagged landing
The _u variants: The Extend Cookie Life Tag also refreshes cookies with a _u suffix (utm_campaign_u, utm_source_u, etc.). These are placeholders for a future-use "last UTM-tagged visit" persistent track — write the user-level tag a second time with a _u suffix to maintain a rolling record of the most recent campaign that drove a visit, distinct from the first-touch _1 values. The extension infrastructure is already in place.

The Rolling Expiry: How the Cookie Rewriter Works

A persistent cookie written once with a 730-day expiry will expire 730 days after it was first written, regardless of how often the user returns. For long sales cycles — a B2B prospect who first visited from a newsletter eleven months ago and finally requests a demo today — a static expiry is often sufficient. But many implementations prefer a rolling expiry: every visit resets the clock, so an active user's attribution never expires.

The Extend Cookie Life Tag achieves this by running the Cookie Rewriter template on every page visit. It reads each of the ten persistent UTM cookies and rewrites them with the same value but a new expiry date set 720 days from the current time. The result is a rolling 720-day window that resets on each visit.

The Sentinel Guard: Firing Exactly Once Per Page

To avoid the Cookie Rewriter firing multiple times per page — once for each DOM Ready event if the page triggers several — the recipe uses a sentinel cookie named _arrewr. The Extend Cookie Life Trigger condition checks this cookie's value against the regex ^(undefined|null|0|false|NaN|)$. The tag fires only when the cookie is absent or empty. The Cookie Rewriter template sets _arrewr on its first run, preventing any subsequent firing within the same page load.

Cookie Variables: Reading the Values Back

Ten Cookie Variable variables are included in the Process URL query strings 2 cookies folder, one for each cookie. These are GTM's built-in First-Party Cookie variable type and require no custom code.

Variable NameCookie ReadTrack
utm_source cookie sessionutm_sourceCurrent session
utm_medium cookie sessionutm_mediumCurrent session
utm_campaign cookie sessionutm_campaignCurrent session
utm_content cookie sessionutm_contentCurrent session
utm_term cookie sessionutm_termCurrent session
utm_source cookie userutm_source_1First touch (persistent)
utm_medium cookie userutm_medium_1First touch (persistent)
utm_campaign cookie userutm_campaign_1First touch (persistent)
utm_content cookie userutm_content_1First touch (persistent)
utm_term cookie userutm_term_1First touch (persistent)

How UTM Cookies Flow Into Every GA4 Hit

The cookie variable values are wired directly into the Google Tag Shared Event Settings variable. Because this variable is referenced by the Google Analytics Configuration tag, its parameter table is merged into every GA4 event sent by the container — page views, scroll events, clicks, form submissions, and conversions alike. You receive both the current-session UTMs and the first-touch UTMs on every event, without touching individual tag configurations.

Shared Event Settings — UTM Parameters

GA4 ParameterGTM VariableAttribution Track
e_cur_utm_source{{utm_source cookie session}}Source from current session's landing page UTMs
e_cur_utm_medium{{utm_medium cookie session}}Medium from current session's landing page UTMs
e_cur_utm_campaign{{utm_campaign cookie session}}Campaign from current session's landing page UTMs
e_1st_utm_source{{utm_source cookie user}}Source from user's first ever UTM-tagged visit
e_1st_utm_medium{{utm_medium cookie user}}Medium from user's first ever UTM-tagged visit
e_1st_utm_campaign{{utm_campaign cookie user}}Campaign from user's first ever UTM-tagged visit
Note on utm_content and utm_term: The five cookies are written for all UTM parameters including utm_content and utm_term, and their cookie variables are available in the container. The Shared Event Settings above are pre-wired for source, medium, and campaign — the most commonly used dimensions. Add e_cur_utm_content, e_cur_utm_term, e_1st_utm_content, and e_1st_utm_term to the Shared Event Settings table if your reporting requires them.

All Available Query String Parameters

The Write URL query strings 2 cookies template supports a wide range of standard and platform-specific click tracking parameters. The container ships with UTM parameters enabled and all click IDs disabled — a sensible default for most implementations. Enable additional parameters by toggling their checkboxes in the tag configuration UI.

ParameterDefaultPlatform / Purpose
utm_source✅ EnabledStandard UTM — traffic source
utm_medium✅ EnabledStandard UTM — traffic medium
utm_campaign✅ EnabledStandard UTM — campaign name
utm_content✅ EnabledStandard UTM — ad content / variant
utm_term✅ EnabledStandard UTM — keyword
gclid☐ DisabledGoogle Ads — auto-tagged click ID
gad_source☐ DisabledGoogle Ads — ad source
gad_campaignid☐ DisabledGoogle Ads — campaign ID
gbraid☐ DisabledGoogle Ads — iOS app conversion
wbraid☐ DisabledGoogle Ads — web-to-app conversion
gclsrc☐ DisabledGoogle — DoubleClick import source
dclid☐ DisabledGoogle Display & Video 360 click ID
msclkid☐ DisabledMicrosoft Advertising click ID
fbclid☐ DisabledMeta (Facebook/Instagram) click ID
igshid☐ DisabledInstagram share ID
ttclid☐ DisabledTikTok click ID
ttcid☐ DisabledTikTok campaign ID
twclid☐ DisabledX (Twitter) click ID
li_fat_id☐ DisabledLinkedIn first-party ad tracking ID
ScCID / ScCid☐ DisabledSnapchat click ID
rdt_cid☐ DisabledReddit click ID
srsltid☐ DisabledGoogle Shopping — surface result listing ID
utm_id☐ DisabledUTM campaign ID (GA4 extended)
utm_source_platform☐ DisabledUTM source platform (GA4 extended)
utm_marketing_tactic☐ DisabledUTM marketing tactic (GA4 extended)
utm_creative_format☐ DisabledUTM creative format (GA4 extended)
epic☐ DisabledCustom campaign / partner click ID
cid☐ DisabledCustom contact or campaign ID

Architecture: How a Click Becomes a Cookie Becomes a GA4 Parameter

  1. User clicks a UTM-tagged link — e.g. from a newsletter, paid ad, or social post — and lands on your site with query parameters in the URL.
  2. GTM fires on All Pages. The Write certain URL query strings 2 cookies session tag runs immediately. For each enabled parameter, it reads the query string value and writes a session cookie scoped to the root domain (e.g. .example.com).
  3. The user tag also runs. Write certain URL query strings 2 cookies user writes the same values as persistent cookies with the _1 suffix, expiring 730 days from now. If a persistent cookie already exists for a parameter, the existing value is retained — preserving first-touch attribution.
  4. GTM fires DOM Ready. The Extend Cookie Life Trigger checks the _arrewr sentinel. If absent, the Extend Cookie Life Tag runs the Cookie Rewriter against all ten persistent UTM cookies, resetting their expiry to 720 days from now.
  5. Cookie Variable variables are evaluated. On every subsequent GTM event, the ten Cookie Variable variables read the current values from the browser cookies.
  6. Shared Event Settings deliver values to GA4. The six UTM shared event parameters (e_cur_utm_* and e_1st_utm_*) are included in every GA4 hit automatically via the Google Tag Shared Event Settings variable.
  7. Days or weeks later, the user returns. Their persistent cookies still hold the original first-touch UTM values. On this second visit, the same session cookies are overwritten with the new landing page UTMs (or remain blank if this visit has no UTMs). The persistent cookies are extended again by the Cookie Rewriter.
  8. The user submits a form. Your form handler can read the UTM cookies directly from document.cookie, or a separate GTM tag can read the GTM Cookie Variable values and push them into the form's hidden fields or a dataLayer event.

The Real-World Use Case: Lead Attribution Across Days

Consider a B2B software company running LinkedIn campaigns. A prospect clicks an ad on a Monday, lands on a product page, reads a case study, and leaves without converting. The UTM parameters from that Monday click are now stored in persistent cookies: utm_source_1=linkedin, utm_medium_1=paid-social, utm_campaign_1=q2-enterprise-saas.

On Thursday, the prospect types the company URL directly into their browser, reads the pricing page, and submits a contact form. The URL has no UTM parameters. GA4 attributes this session to direct. But your CRM receives the hidden form fields populated from the persistent cookies — and your sales team knows this lead originated from the LinkedIn Q2 enterprise campaign.

This is the gap that UTM-to-cookie bridging fills: GA4 session attribution versus real-world multi-touch, multi-day customer journeys where the conversion rarely happens on the same visit as the first paid impression.

Why Sandboxed Templates Do Not Require New CSP Hashes

GTM's sandboxed Custom Template runtime is granted access to browser APIs through GTM's own internal permission model — not through Content Security Policy. A template that writes a cookie uses GTM's setCookie sandbox API, which executes within code already authorised by the GTM container's existing CSP allowance (the allowance granted to googletagmanager.com or your server-side GTM origin).

In contrast, a Custom HTML tag writes a literal <script> element into the DOM. CSP's script-src directive applies to every inline script element. Unless you have unsafe-inline enabled (which defeats much of CSP's purpose) or a matching SHA-256 hash, the browser will block execution. Every change to that script — adding a comment, reformatting a line — changes the hash and requires a CSP header update.

With sandboxed templates, your CSP never changes when GTM tag logic changes. The entire cookie management implementation in this recipe is CSP-transparent.

How to Import

  1. Download the JSON file from the Google Drive link.
  2. In GTM, go to Admin → Import Container.
  3. Upload utm-and-url-query-string-2-cookies-recipe.json.
  4. Choose Merge (not Overwrite) to preserve your existing tags and triggers.
  5. Update Measurement Stream ID Production and Measurement Stream ID Development constant variables to your actual GA4 Measurement IDs.
  6. Review the enabled query string parameters in both Write tags and toggle on any additional parameters (click IDs, custom fields) that your implementation requires.
  7. Verify that {{Root Domain}} resolves correctly for your site — this controls the cookie's domain scope. Check its value in GTM Preview mode on your site.
  8. In GA4, register e_cur_utm_source, e_cur_utm_medium, e_cur_utm_campaign, e_1st_utm_source, e_1st_utm_medium, and e_1st_utm_campaign as custom event-scoped dimensions under Admin → Custom definitions → Custom dimensions.
  9. Open GTM Preview mode on a URL with UTM parameters (e.g. ?utm_source=test&utm_medium=test&utm_campaign=test) and confirm the cookie variables populate correctly in the Tag Assistant panel.
  10. Navigate to a second page with no UTM parameters and confirm the session and persistent cookie values persist across the navigation.
Cookie consent: Writing persistent cookies that track marketing attribution may require consent under GDPR, CCPA, or equivalent regulations depending on your jurisdiction and how the data is used. Review with your legal team. The recipe writes cookies on the first page view; if you operate a consent-managed site, gate both Write tags behind a consent trigger that only fires after the user has accepted marketing or analytics cookies.

What You Can Do With It

  • Attach first-touch UTM values to CRM lead records by reading persistent cookies from hidden form fields — connect campaign spend to closed revenue without server-side infrastructure
  • Compare e_cur_utm_campaign (session UTM) against e_1st_utm_campaign (first-touch UTM) in GA4 Explorations to understand how many conversions involve multi-campaign journeys
  • Persist platform click IDs (gclid, fbclid, msclkid) by enabling the relevant parameters — forward them to your CRM for enhanced conversions and offline conversion imports
  • Build BigQuery attribution models that join on e_1st_utm_source to compare first-touch against data-driven attribution without rebuilding your tracking implementation
  • Extend the cookie name suffix convention (_1 for first touch, _u for last touch) to create a full first/last attribution duality in a single container
  • Use session UTM cookies to personalise on-site content via GTM variables — show a returning email subscriber a different headline without any server-side personalisation

The full source — including the container JSON, variable inventory, and the drewspen sandboxed Custom Templates — is available on GitHub. If you enable additional query string parameters or extend the cookie naming convention, open a pull request or issue.

⬇ Download GTM Container JSON

Comments

Popular posts from this blog

Site Landing & Site Referrer Preservation

GTM Browser Viewport Measurement