Skip to main content
A custom function can set cookies on the end-user’s browser the same way any HTTP server does: by adding Set-Cookie headers to the response it returns. The Hub forwards those headers downstream after rewriting the cookie’s Domain attribute to match the externally-visible host, so cookies you set are scoped to the host the browser actually requested. This page covers how to write cookies. For background on response shaping in general, see Response Functions.
Cookie names must start with __ollieshop_ to reach the browser. The storefront only persists cookies in this namespace — anything else is dropped before it leaves the server. Pick descriptive suffixes (__ollieshop_ab_variant, __ollieshop_viewed_promo, etc.) and use them consistently.

The basics

Your handler receives { req, res } and must return a Response. To set a cookie, clone the upstream response (so you preserve its body, status, and existing headers) and append a Set-Cookie header to the new one. The value is a standard cookie string — name, value, and any attributes you want (Path, Max-Age, SameSite, Secure, HttpOnly, etc.).
Function Implementation
export const handler = async ({
  req,
  res,
}: {
  req?: Request;
  res: Response;
}): Promise<Response> => {
  const body = await res.text();
  const newRes = new Response(body, res);

  newRes.headers.append(
    "Set-Cookie",
    "__ollieshop_pref=dark; Path=/; Max-Age=2592000; SameSite=Lax",
  );

  return newRes;
};
After this function runs, the browser stores __ollieshop_pref=dark and includes it on every subsequent request to your host.
The Hub strips and rewrites the Domain attribute on every cookie it forwards. You don’t need to set Domain yourself — if you do, it will be replaced with the host the browser requested (X-Forwarded-HostHost → URL hostname, in that order). This keeps cookies scoped correctly across custom domains.

Setting multiple cookies

To set more than one cookie in the same response, call headers.append once per cookie. Each call adds its own Set-Cookie header on the wire.
Multiple Cookies
export const handler = async ({
  req,
  res,
}: {
  req?: Request;
  res: Response;
}): Promise<Response> => {
  const body = await res.text();
  const newRes = new Response(body, res);

  newRes.headers.append(
    "Set-Cookie",
    "__ollieshop_ab_variant=B; Path=/; Max-Age=86400; SameSite=Lax",
  );
  newRes.headers.append(
    "Set-Cookie",
    "__ollieshop_viewed_promo=summer24; Path=/; Max-Age=3600; SameSite=Lax",
  );

  return newRes;
};
Cookies the browser sends are on the request’s cookie header as a single string. Read it via req.headers.get("cookie") and parse it the same way you would in any HTTP server.
Reading a Cookie
function getCookie(req: Request, name: string): string {
  return (
    req.headers
      .get("cookie")
      ?.split("; ")
      .find((cookie) => cookie.startsWith(`${name}=`))
      ?.split("=")[1] || ""
  );
}

export const handler = async ({
  req,
  res,
}: {
  req?: Request;
  res: Response;
}): Promise<Response> => {
  const variant = req ? getCookie(req, "__ollieshop_ab_variant") : "";

  if (variant) {
    // Use the existing variant; pass the upstream response through unchanged.
    return res;
  }

  // First visit — assign and set the cookie.
  const assigned = Math.random() < 0.5 ? "A" : "B";
  const body = await res.text();
  const newRes = new Response(body, res);

  newRes.headers.append(
    "Set-Cookie",
    `__ollieshop_ab_variant=${assigned}; Path=/; Max-Age=2592000; SameSite=Lax`,
  );

  return newRes;
};
To update a cookie, set it again with the new value — the browser overwrites by name + path + domain. To delete one, set the same name and path with a past Expires (or Max-Age=0) and an empty value.
Deleting a Cookie
const body = await res.text();
const newRes = new Response(body, res);

newRes.headers.append(
  "Set-Cookie",
  "__ollieshop_ab_variant=; Path=/; Max-Age=0",
);

return newRes;

Reserved names

A few cookies in the __ollieshop_ namespace are owned by the platform itself and should not be overwritten by your functions. The current reserved name is:
  • __ollieshop_session — the encrypted session cookie issued by the Hub for state shared across function invocations.
Pick a suffix that’s unlikely to collide (__ollieshop_<your-feature>_<purpose>) and you’ll be safe.