> This page is for Developer.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bigcommerce.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bigcommerce.com/_mcp/server.

# Proxy

> How Catalyst's proxy chain in proxy.ts handles UCP, sessions, analytics, localization, channels, GraphQL proxying, and route resolution before a request reaches its route.

Catalyst uses a Next.js [Proxy](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) to support key features in storefront requests. Proxy is the Next.js 16 name for what earlier versions called Middleware. Catalyst moved from `middleware.ts` to `proxy.ts` in [Catalyst 1.5.0](/developer/docs/storefront/catalyst/release-notes/1-5-0).

> **Note**
>
> On [Native Hosting](/developer/docs/storefront/catalyst/deployment/native-hosting/overview), Commerce Hosting setup renames `proxy.ts` back to `middleware.ts` (exporting `middleware` with the edge runtime), because the OpenNext Cloudflare adapter doesn't support the Node.js-only `proxy.ts`. The chain and configuration described on this page are the same; look for them in `middleware.ts`. See [Native Hosting: Getting Started](/developer/docs/storefront/catalyst/deployment/native-hosting/getting-started#set-up-with-an-existing-project).

## The proxy chain

Proxy code runs before a route is rendered, and provides an entry point for common tasks like authentication, rewrites, and redirects.

[`proxy.ts`](https://github.com/bigcommerce/catalyst/blob/canary/core/proxy.ts) in the storefront root directory composes the individual proxies Catalyst uses into a single chain with `composeProxies`. Each request passes through them in order, and any proxy can return a response early instead of continuing down the chain:

**`proxy.ts`**

```ts title="proxy.ts" showLineNumbers={false}
export const proxy = composeProxies(
  withUcpProxy,
  withAuth,
  withAnalyticsCookies,
  withIntl,
  withChannelId,
  withGraphqlProxy,
  withRoutes,
);
```

| Proxy                                     | Purpose                                                                                                                                                                                                                            |
| :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`withUcpProxy`](#with-ucp-proxy)         | Forwards Universal Commerce Protocol (UCP) requests to BigCommerce. Runs first so no other proxy touches them.                                                                                                                     |
| [`withAuth`](#with-auth)                  | Manages shopper and customer sessions with Auth.js.                                                                                                                                                                                |
| `withAnalyticsCookies`                    | Sets the visitor and visit cookies used for BigCommerce analytics, and sends a visit started event on new visits. Respects the shopper's measurement consent. See [Cookies](/developer/docs/storefront/catalyst/features/cookies). |
| `withIntl`                                | Resolves the request's locale with [next-intl](https://next-intl.dev/) and applies locale routing. See [Localization](/developer/docs/storefront/catalyst/features/localization/overview).                                         |
| `withChannelId`                           | Maps the resolved locale to a BigCommerce channel ID for the rest of the request. See [Multi-Storefront](/developer/docs/storefront/catalyst/features/localization/multi-storefront/overview).                                     |
| [`withGraphqlProxy`](#with-graphql-proxy) | Proxies GraphQL Storefront API requests from allow-listed client-side libraries, such as the BigCommerce Checkout SDK, through the storefront domain.                                                                              |
| [`withRoutes`](#with-routes)              | Resolves custom BigCommerce URLs to Catalyst routes and checks storefront status.                                                                                                                                                  |

If you customize the chain, keep `withUcpProxy` first. Several later proxies depend on headers set by earlier ones (for example, `withChannelId` reads the locale that `withIntl` resolves), so keep their relative order too.

## Exclude paths from the proxy

URL patterns the proxy shouldn't run on are configured in the `config` exported by `proxy.ts`. By default, static files such as sitemaps and `robots.txt` are excluded, along with API routes. UCP endpoints are matched separately, so the `api` exclusion keeps applying to every other API route:

**`proxy.ts`**

```ts title="proxy.ts" showLineNumbers={false}
export const config = {
  matcher: [
    '/((?!api|admin|_next/static|_next/image|_vercel|favicon.ico|xmlsitemap.php|sitemap.xml|robots.txt).*)',
    '/api/ucp/:path*',
  ],
};
```

For example, if you've created a custom route for URLs starting with `/product-configure` that doesn't need the proxy, add it to the exclusions in the first pattern:

**`proxy.ts`**

```ts title="proxy.ts" showLineNumbers={false}
export const config = {
  matcher: [
    '/((?!product-configure|api|admin|_next/static|_next/image|_vercel|favicon.ico|xmlsitemap.php|sitemap.xml|robots.txt).*)',
    '/api/ucp/:path*',
  ],
};
```

## With UCP proxy

AI agents discover and call the [Universal Commerce Protocol](https://ucp.dev/) (UCP) on your storefront's public domain, but UCP is served by the BigCommerce platform, not by Catalyst. The `withUcpProxy` proxy forwards both UCP surfaces to the store's channel on BigCommerce:

* `/.well-known/ucp`: the discovery document
* `/api/ucp/*`: the checkout endpoints, for every HTTP method

It runs first in the chain so that locale rewriting, route resolution, and anonymous session creation are all bypassed for UCP requests. Keep in mind:

* **Headers:** only the headers the [UCP REST transport spec](https://ucp.dev/2026-04-08/specification/checkout-rest/) defines are forwarded in either direction. This includes the agent's credentials and signatures, because UCP authenticates the calling agent at the platform rather than at Catalyst.
* **Trailing slashes:** the trailing slash is stripped before a UCP path is forwarded. BigCommerce answers a trailing slash with a redirect to the bare path, which an agent would resolve against your storefront domain and send straight back.
* **Configuration:** the proxy forwards to the channel identified by `BIGCOMMERCE_STORE_HASH` and `BIGCOMMERCE_CHANNEL_ID`, so no extra environment variables are needed. If either is unset, UCP requests return a `502`.
* **Customized proxies:** if you've customized `proxy.ts`, make sure `withUcpProxy` is the first argument to `composeProxies` and `/api/ucp/:path*` is in `config.matcher`. Without the matcher entry, `/api/ucp/*` requests never reach the proxy and return a `404`.

The UCP proxy was added in [Catalyst 1.12.0](/developer/docs/storefront/catalyst/release-notes/1-12-0#ucp-endpoints-on-the-storefront-domain).

## With auth

The `withAuth` proxy is a wrapper over a minimal implementation of [Auth.js, formerly NextAuth.js](https://authjs.dev/getting-started). This provides Catalyst with session management for shoppers' carts and customers' logged-in states. By default, it uses JWT cookies to avoid obligatory dependency on any persistence layer on the storefront itself. You may wish to extend this functionality to store more information in a session or add a persistence layer yourself for more flexibility and control over shopper sessions. See [Sessions](/developer/docs/storefront/catalyst/development/sessions).

## With GraphQL proxy

Catalyst's own GraphQL requests run server-side, but some client-side libraries need to call the GraphQL Storefront API from the browser. For example, [`checkout-sdk-js`](https://github.com/bigcommerce/checkout-sdk-js) does this to render wallet buttons. The `withGraphqlProxy` proxy lets these libraries send requests through your storefront domain, so no storefront token is exposed in the browser.

The proxy handles a request only when both of the following are true. Any other request continues down the chain unchanged:

* The path is `/graphql`.
* The `x-catalyst-graphql-proxy-requester` header is set to an allow-listed value. Only `checkout-sdk-js` is allowed by default.

Matching requests must use `POST`; any other method gets a `405`.

The proxy forwards the query and variables to BigCommerce, authenticated with the [`BIGCOMMERCE_STOREFRONT_UNAUTHENTICATED_TOKEN`](/developer/docs/storefront/catalyst/reference/environment-variables#bigcommerce_storefront_unauthenticated_token) private token. For signed-in customers, it also passes along the customer access token from the session, so customer-specific data like customer group pricing still resolves. Proxied responses aren't cached.

> **Warning**
>
> The Catalyst CLI doesn't set `BIGCOMMERCE_STOREFRONT_UNAUTHENTICATED_TOKEN`. If your store offers wallet payment methods (such as PayPal) on the cart page, or you allow other libraries to use the proxy, create the token and add it to your environment variables, or requests through the GraphQL proxy fail.

To allow another client-side library to use the proxy, add its identifier to the `ALLOWED_REQUESTERS` list in [`proxies/with-graphql-proxy.ts`](https://github.com/bigcommerce/catalyst/blob/canary/core/proxies/with-graphql-proxy.ts), and have the library send that value in the `x-catalyst-graphql-proxy-requester` header. Keep the list short: every allow-listed requester can run arbitrary GraphQL Storefront API queries with the unauthenticated token.

The GraphQL proxy was added in [Catalyst 1.7.0](/developer/docs/storefront/catalyst/release-notes/1-7-0#client-side-graphql-proxy).

## With routes

The `withRoutes` proxy overrides the default [Next.js file-based routing behavior](https://nextjs.org/docs/app/building-your-application/routing) to provide support for certain BigCommerce features in a way that allows changes users make with the store control panel and APIs to have the expected effect on the storefront.

For example, consider this SEO-friendly URL path:

```text showLineNumbers={false}
/fancy-high-top
```

In a hypothetical store, this is the URL path set on a product, but the path itself doesn't indicate that it belongs to a product. And based on the file path of the route at `app/[locale]/(default)/product/[slug]/page.tsx`, the URL pattern Next.js actually expects is similar to the following example:

```text showLineNumbers={false}
/product/123
```

Despite the param name `[slug]`, what the route expects in the segment is a product entity ID. The `withRoutes` proxy sends the path `/fancy-high-top` to BigCommerce using the GraphQL Storefront API's [route() node](/developer/api-reference/graphql/storefront#definition-Route) to determine the entity (in this case, a product) that corresponds with the supplied path. It then performs an internal Next.js rewrite to the entity-specific path `/product/123`, resulting in the page being rendered by the appropriate route.

As a result, you can use any URL pattern BigCommerce supports, from `/fancy-high-top` to `/product/fancy-high-top` to `/shoes/high-tops/fancy-high-top`. Any URL assigned to an object's [node](/developer/api-reference/graphql/storefront#definition-Node) using either the store control panel or APIs works as expected on the Catalyst storefront, including [301 redirects](https://support.bigcommerce.com/s/article/MSF-301-Redirects#adding).

The `withRoutes` proxy also checks the [storefront status](/developer/api-reference/graphql/storefront#definition-StorefrontStatusType) so that when a control panel or API user takes a storefront down for **MAINTENANCE** or sets it to **LAUNCHED**, the live storefront reflects the change without waiting for a rebuild of the Next.js application.

### Performance and KV store configuration

Using the `withRoutes` proxy introduces a performance cost, since it requires an additional blocking API request to render site pages.

Although path-to-entity mappings can be cached, the proxy can't use the [Data Cache](/developer/docs/storefront/catalyst/development/caching#data-cache) that caches fetched data in other parts of Catalyst. Instead, a separate caching backend must be used. Catalyst uses a [caching implementation](https://github.com/bigcommerce/catalyst/tree/canary/core/lib/kv) that's flexible for multiple KV adapters.

> **Info**
>
> **Route caching on Native Hosting and Vercel**
>
> Route caching works out of the box on both documented hosts, with no external KV store or extra configuration:
>
> * **Native Hosting:** each project gets its own Cloudflare Workers KV namespace, bound to the deployment automatically.
> * **Vercel:** route caching uses the [Vercel Runtime Cache](https://vercel.com/changelog/introducing-the-runtime-cache-api).
>
> On any other host, set [`UPSTASH_REDIS_REST_URL`](/developer/docs/storefront/catalyst/reference/environment-variables#upstash_redis_rest_url) and [`UPSTASH_REDIS_REST_TOKEN`](/developer/docs/storefront/catalyst/reference/environment-variables#upstash_redis_rest_token) to cache routes in [Upstash](https://upstash.com/). Without one of these, route data is cached only in memory, which isn't shared across server instances.

If your Catalyst storefront does not rely on control panel or API-configured URLs, and you do not need storefront status checks, you can safely remove the `withRoutes` proxy to improve performance.

For projects where URL structures need to be tightly controlled, consider defining routes at build time or implementing custom routing strategies.