> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ollie.shop/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Browser Cookies

> Set cookies on the end-user's browser from a custom function by appending Set-Cookie headers to the response.

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](/ollie-hub/core-concepts/response-functions).

<Warning>
  **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.
</Warning>

## 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.).

```typescript title="Function Implementation" icon="cookie-bite" theme={"system"}
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.

<Info>
  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-Host` → `Host` → URL hostname, in that order). This keeps cookies scoped correctly across custom domains.
</Info>

## 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.

```typescript title="Multiple Cookies" icon="layer-group" theme={"system"}
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;
};
```

## Reading a cookie back

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.

```typescript title="Reading a Cookie" icon="magnifying-glass" theme={"system"}
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;
};
```

## Updating or deleting a cookie

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.

```typescript title="Deleting a Cookie" icon="trash" theme={"system"}
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.
