Next.js middleware
The storefront middleware (src/middleware.ts) runs on every
navigation request and performs four tasks in sequence: token refresh,
B2B route rewriting, internationalization, and security headers.
Token refresh
The middleware reads the JOSE header from the userToken cookie using
jose.decodeProtectedHeader() and checks the exp claim. If the
token has been expired for more than 5 minutes and a
userRefreshTokenExists cookie is present, the request is redirected
to /account/refresh-session which exchanges the refresh token for a
new access token.
This cleans up stale sessions so server-rendered pages don't attempt to use long-expired tokens.
B2B routing
When the userData JWT contains a businessContext object (checked
via readUserData()), the middleware redirects /account/* requests
to /account-b2b/account/*. This lets B2B customers see a different
account section with company management, quotes, and approval
workflows.
See B2B architecture for how business context is established.
Internationalization
The next-intl middleware handles locale detection and prefix
routing. Every URL must include a locale segment (e.g. /en/cart,
/nl/winkelwagen). The locale list and custom prefix mapping are
defined in packages/site-config/src/i18n/config.ts.
Security headers
The setResponseHeaders() function from src/lib/secure-headers.ts
adds security headers to every response:
| Header | Value |
|---|---|
Strict-Transport-Security | max-age=63072000; includeSubDomains; preload |
X-Content-Type-Options | nosniff |
Referrer-Policy | same-origin |
Permissions-Policy | Blocks camera, microphone, geolocation |
Content-Security-Policy | Per-source allowlists (see below) |
Content Security Policy
The CSP is assembled dynamically based on environment variables. Key source allowlists:
- connect-src: GraphQL gateway, OpenTelemetry endpoint, Sentry, Google Analytics, Adyen
- script-src:
self, GTM, Stripe, Sentry, Storyblok bridge - frame-src: Stripe, YouTube, Adyen checkout
- img-src:
self, HTTPS, data URIs, Contentful assets
In production, upgrade-insecure-requests is added to the CSP
directives. Sentry CSP violation reports are configured through the
Report-To header.
Matcher
The middleware intercepts all routes except API routes and static files (anything with a file extension):
export const config = {
runtime: "nodejs",
matcher: ["/", "/((?!api|_next|.*\\..*).*)" ],
};