> For the complete documentation index, see [llms.txt](https://docs.adpage.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.adpage.io/integrations-websites/shopify/cookie-recovery-on-shopify.md).

# Cookie recovery on Shopify

Safari deletes tracking cookies after seven days, even when you use your own subdomain for server-side tagging.

Safari deletes tracking cookies after seven days, even when you use your own subdomain for server-side tagging. This article explains why that happens, what the cookie recovery script does about it, and how to install it on your Shopify theme.

### What this script does

The script stores a visitor ID (the `_taggingmk` cookie) in two places:

* **In a first-party cookie on your main domain:** the primary storage location
* **In localStorage:** a backup, used to restore the cookie if it disappears

That ID is the anchor our server uses to rebuild your tracking cookies when a browser wipes them.

### Safari erases your marketing data

Safari's Intelligent Tracking Prevention (ITP) automatically deletes cookies after seven days. Using a custom tracking subdomain such as `tagging.webshop.com` does not solve this: Safari recognises that the subdomain resolves to a different server than your webshop, and treats its cookies as third-party-ish.

The result is that after one week you lose the link to your visitor. Repeat visits from Safari users look like brand new visitors, and your conversion attribution stops adding up.

In practice that means:

* Returning customers are counted as new visitors
* Your customer journey data becomes unreliable
* Attribution analyses go wrong, for example first-click versus returning customer
* Conversion paths are incomplete

#### How cookie recovery works around it

Three steps:

**1. The anchor.** We place a cookie directly on your main domain (`webshop.com`). Because your own website sets it with JavaScript on the primary domain, Safari allows it to live longer than a cookie on your tracking subdomain.

**2. The check.** On every page load, the script sends this anchor ID to our server, the proxy.

**3. The recovery.** Our server checks whether the tracking cookies on `tagging.webshop.com` are still present. If Safari removed them after seven days, the server writes them back based on the anchor ID.

The localStorage backup protects the anchor itself. If the anchor cookie is cleared but localStorage survives, the script restores the cookie from the backup instead of generating a new ID, so the visitor keeps the same identity.

The outcome: your tracking stays accurate over a longer period, which means you can measure and optimise your marketing campaigns properly.

### Installing on Shopify

1. Log in to your Shopify admin.
2. Go to **Online Store → Themes**.
3. On your active theme, click the three dots and choose **Edit code**.
4. In the left sidebar, open **`theme.liquid`** (under Layout).
5. Paste the script below directly after the opening `<head>` tag. Placing it high in the `<head>` matters: it needs to run before your other tracking scripts.
6. Click **Save** in the top right.

```html
<script type="text/javascript">
(function() {
    'use strict';

    // Generate UUID v4
    function mkTagging_generateUUID() {
        if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
            return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, function(c) {
                return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16);
            });
        } else {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0;
                var v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }
    }

    // Get cookie value by name
    function mkTagging_getCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) === ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
        }
        return null;
    }

    // Set cookie with expiration
    function mkTagging_setCookie(name, value, days) {
        var expires = "";
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            expires = "; expires=" + date.toUTCString();
        }
        document.cookie = name + "=" + value + expires + "; path=/; SameSite=Lax";
    }

    // Safe localStorage wrapper
    function mkTagging_getLocalStorage(key) {
        try {
            if (typeof Storage !== 'undefined' && window.localStorage) {
                return localStorage.getItem(key);
            }
        } catch (e) {
            console.log('localStorage not available:', e);
        }
        return null;
    }

    function mkTagging_setLocalStorage(key, value) {
        try {
            if (typeof Storage !== 'undefined' && window.localStorage) {
                localStorage.setItem(key, value);
                return true;
            }
        } catch (e) {
            console.log('localStorage not available:', e);
        }
        return false;
    }

    // Main initialization function
    function mkTagging_initVisitorId() {
        var cookieName = '_taggingmk';
        var storageKey = '_taggingmk_backup';
        var visitorId = null;

        // 1. Check if cookie exists
        visitorId = mkTagging_getCookie(cookieName);

        // 2. If no cookie, check localStorage backup
        if (!visitorId) {
            visitorId = mkTagging_getLocalStorage(storageKey);

            // If found in localStorage but not in cookie, restore the cookie
            if (visitorId) {
                mkTagging_setCookie(cookieName, visitorId, 365);
                console.log('MK Tagging: Visitor ID restored from localStorage');
            }
        }

        // 3. If still no ID, generate a new one
        if (!visitorId) {
            visitorId = mkTagging_generateUUID();
            mkTagging_setCookie(cookieName, visitorId, 365);
            console.log('MK Tagging: New visitor ID generated');
        }

        // 4. Always sync to localStorage as backup
        if (mkTagging_setLocalStorage(storageKey, visitorId)) {
            console.log('MK Tagging: Visitor ID backed up to localStorage');
        }

        return visitorId;
    }

    // Run on DOM ready or immediately if already loaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', mkTagging_initVisitorId);
    } else {
        mkTagging_initVisitorId();
    }

    // Create a namespaced object for any exposed functionality
    window.mkTagging = window.mkTagging || {};
    window.mkTagging.getVisitorId = function() {
        return mkTagging_getCookie('_taggingmk') || mkTagging_getLocalStorage('_taggingmk_backup');
    };

})();
</script>
```

### Verifying the installation

Open your website in an incognito window, then open Developer Tools with F12.

#### Test 1: the cookie is created

Go to the **Console** tab. You should see `MK Tagging: New visitor ID generated`, followed by `MK Tagging: Visitor ID backed up to localStorage`.

Then go to **Application → Cookies** and look for the `_taggingmk` cookie. Its value should be a UUID, for example `a3f2e1d4-5b6c-4d7e-8f9a-0b1c2d3e4f5a`. Refresh the page and confirm the value stays the same.

#### Test 2: the localStorage backup works

Go to **Application → Local Storage** and look for `_taggingmk_backup`. Its value must be identical to the cookie value.

#### Test 3: recovery works

Delete the `_taggingmk` cookie manually in Developer Tools, then refresh the page. The Console should now show `MK Tagging: Visitor ID restored from localStorage`, and the cookie should be back with the same value as before.

If the value changed instead of being restored, the localStorage backup is not being read. Check the Console for JavaScript errors and confirm the script is present in the rendered `<head>`.

### How the logic flows

```
Visitor arrives on site
        ↓
Does the cookie exist? → YES → Sync to localStorage → Done
        ↓ NO
Does the localStorage backup exist? → YES → Restore cookie → Done
        ↓ NO
Generate new UUID → Store in cookie + localStorage → Done
```

### Privacy and GDPR

* **No personal data.** The script stores a random UUID only, no personal information.
* **First-party cookie.** The cookie is set on your own domain, not by a third party.
* **Technically functional.** This is a technical cookie used for analytics, which is often permitted without explicit consent.

One caveat: check with your privacy officer whether this cookie qualifies as strictly necessary in your situation, or whether it needs to sit behind consent in your cookie banner. That assessment depends on what you use the data for, not on the script itself.

### Troubleshooting

**The script does not seem to do anything.** Check the Console for JavaScript errors, and make sure the script sits before your other tracking scripts in the `<head>`. If another script throws an error earlier in the page, the recovery script may never run.

**The cookie still gets deleted.** In some Safari modes ITP restricts localStorage as well, which removes the backup along with the cookie. In that case server-side tracking is the more durable primary solution rather than a client-side recovery layer.

**Different IDs across subdomains.** By default the cookie is scoped to the exact hostname. Adjust the `mkTagging_setCookie` function to include `domain=.yourdomain.com` so the same ID is shared across all subdomains.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.adpage.io/integrations-websites/shopify/cookie-recovery-on-shopify.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
