Skip to content

Server-side cookie

A server-side cookie is a cookie your web server sets with a Set-Cookie header in the HTTP response for a page. The browser gives it the longest lifetime a cookie can have. Set one next to Qinto’s own cookie to extend how long a visitor on an Apple device stays recognised. Adding one takes a few lines on your server and one setting in the Blotout platform.

Every site that runs its own server, or an edge layer in front of one, can set this cookie, and this page shows how. Shopify stores cannot, see Hosted platforms.

Safari and the other browsers on Apple devices give the longest lifetime, up to 400 days, to a cookie the site’s own server sets in the page response. Apple describes the rules in Tracking Prevention in WebKit. A server-side cookie next to Qinto’s cookie puts that lifetime to work for you: Qinto uses it as a second way to recognise a returning visitor, so a visitor on an iPhone or a Mac stays known for as long as your cookie lives, and the purchases, consent and traits from their earlier visits stay on their profile. The ID graph page explains what that profile holds.

Qinto only reads your cookie. It never changes it, stores only a hash of the value, and passes it to no app.

Attribute Value Why
Name A name no other tool on your site uses, such as site_visitor Qinto reads the cookie by this name.
Value A random id that is unique per browser, such as a UUID, at least 4 characters Two visitors with the same value would be merged into one profile. Never use a static value, a hash of the IP address or a personal identifier such as an email.
Domain Your site’s root domain, the domain you gave the Blotout platform when you created the site, such as example.com The browser sends a cookie to the host that set it and, with Domain, to every host under that domain. Your Qinto host is one of them. A host-only cookie set by www.example.com never reaches q.example.com.
Max-Age 34560000 (400 days) The longest lifetime browsers allow. A later date is cut to 400 days.
Path / So every page sends it.
Secure Set Your Qinto host is HTTPS only.
HttpOnly Set No script needs to read it, and an injected script cannot read a cookie with this flag.
SameSite Lax Your pages and your Qinto host share a site, so Lax is enough. Strict works too.

Four rules about when to set it:

  • Set it on the HTML response. The tag connects to Qinto after the page has loaded, so a cookie set in the page response is already in the browser on the first connection. A cookie set later, by an API call the page makes, arrives after Qinto has already recorded the visitor without it.
  • Keep the existing value. When the request already carries the cookie, send the same value back. A new value on every response is a new visitor on every response.
  • Send it on every page response. Re-sending the cookie renews its 400 days. A cookie set only on the first visit expires 400 days later, however often the visitor returns.
  • Your server must set it, not a tag manager or a script. A cookie written by JavaScript, even JavaScript on your own domain, lives 7 days at most on Apple devices and extends nothing.

A cookie you already set can serve, if it is set by the server, unique per browser and stable for the life of the browser. A session cookie that rotates on login does not qualify, because the value Qinto learned is gone the next time it looks.

A cookie that identifies a browser is a cookie in the sense of your privacy policy and your consent banner. Treat it the way you treat Qinto’s own cookie, in the same category.

Every example sets the cookie described above on a page response, reuses a value the request already carries, and reads the root domain from configuration. Replace example.com with your root domain and pick a cookie name of your own.

Express middleware. Express does not parse the Cookie header on its own, so install cookie-parser and register both before your routes.

import { randomUUID } from 'node:crypto'
import cookieParser from 'cookie-parser'
const COOKIE = 'site_visitor'
const ROOT_DOMAIN = 'example.com'
const MAX_AGE_MS = 400 * 24 * 60 * 60 * 1000
app.use(cookieParser())
app.use((req, res, next) => {
const existing = req.cookies[COOKIE]
res.cookie(COOKIE, existing ?? randomUUID(), {
domain: ROOT_DOMAIN,
maxAge: MAX_AGE_MS,
path: '/',
secure: true,
httpOnly: true,
sameSite: 'lax'
})
next()
})

Without Express, append the header yourself:

const value = existing ?? randomUUID()
res.setHeader('Set-Cookie', `${COOKIE}=${value}; Max-Age=34560000; Domain=${ROOT_DOMAIN}; Path=/; Secure; HttpOnly; SameSite=Lax`)

Shopify gives a merchant no place to set a response header, so a Shopify store cannot add a server-side cookie. Use the Blotout Shopify app, which recognises returning customers for you. If you write your own custom pixel instead, send the customer id Shopify exposes on checkout events with identify and alias, so a customer is recognised across visits by their id. See User data.

WordPress and WooCommerce need the plugin from the WordPress tab above. Neither sets a stable per-visitor cookie by default.

Static hosting such as Netlify, Vercel or S3 behind a CDN cannot set a cookie from the page itself. Use the Cloudflare Workers example if your DNS is on Cloudflare, the Next.js middleware on Vercel, or an edge function or nginx rule on your host.

Qinto reads your cookie only once it knows the name.

  1. In the Blotout platform, open your site, then Settings → Capturing data.
  2. Under Recover visitors with your own cookie, enter the cookie name and select Save cookie name.

The site redeploys with the new setting on its own, which takes a moment. A page that connects before the redeploy finishes still works, it just does not use the cookie yet.

The same setting is available through the Platform API as the fallbackCookie field of Update site settings. It needs a team API token.

Terminal window
curl -X PATCH https://qinto-api.blotout.io/teams/{teamId}/domains/{domainId}/settings \
-H "Authorization: Bearer $QINTO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "fallbackCookie": "site_visitor" }'

Send null to turn recovery off. Renaming the cookie starts over: pairings Qinto recorded under the old name do not carry to the new one.

  1. Open your site in a private window. In the browser’s developer tools, under Application → Cookies in Chrome or Storage → Cookies in Safari and Firefox, find your cookie. It must show your root domain in the Domain column, an expiry about 400 days out, and HttpOnly checked. A cookie with your full hostname as the domain, or an expiry a week out, does not meet the requirements above.
  2. In the console, run await qinto.getVisitor() and note the anonId.
  3. Still in developer tools, delete every cookie whose name starts with q_, but keep your own cookie. Reload the page and run await qinto.getVisitor() again. The anonId is the same one. Without recovery it would be a new id.

The second check only passes for a visitor whose first visit happened with your cookie in place. A visitor Qinto already knew before you added the cookie is paired with it on a later visit, and from then on is recognised through it like everyone else.

  • The mechanism is the same. EdgeTag read a server-side cookie you named to recover a visitor, and Qinto does too. The cookie you set for EdgeTag works unchanged. Enter its name in the Blotout platform.
  • WordPress is not automatic. EdgeTag’s WordPress app set the cookie for you. In Blotout, add the plugin from the WordPress tab. For Shopify, use the Blotout Shopify app or the customer id in your custom pixel, see Hosted platforms.
  • The value never becomes an id. EdgeTag mapped your cookie to its own id behind the scenes, and so does Qinto. Your cookie value appears in no event, trait or app.