// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // One nonce per request, and the security headers of spec ยง6.4 on every response. // // `proxy.ts` is Next.js 16's name for what used to be `middleware.ts`; the older name still // works and warns, so the template uses the current one. // // The policy is also set on the *request* headers, which is how Next.js finds the nonce for the // scripts it injects itself; `x-nonce` is there for a layout that renders a script of its own. import { type NextRequest, NextResponse } from "next/server" import { contentSecurityPolicy, securityHeaders } from "./src/server/headers" export default function proxy(request: NextRequest): NextResponse { const nonce = Buffer.from(globalThis.crypto.randomUUID()).toString("base64") const secure = request.nextUrl.protocol === "https:" const development = process.env.NODE_ENV === "development" const policy = contentSecurityPolicy({ nonce, development }) const headers = new Headers(request.headers) headers.set("x-nonce", nonce) // Where this request is, for a page that has to send a visitor to sign in and bring them back: a // server component is not given its own URL. headers.set("x-path", `${request.nextUrl.pathname}${request.nextUrl.search}`) headers.set("content-security-policy", policy) const response = NextResponse.next({ request: { headers } }) for (const [name, value] of Object.entries(securityHeaders({ nonce, secure, development }))) { response.headers.set(name, value) } return response } export const config = { matcher: [ { // Everything but the build's own immutable assets, which carry no HTML and no script of their own. source: "/((?!_next/static|_next/image|favicon.svg).*)", // ...and but a request whose body is a stream of bytes (`PUT /api/v1/` of a // procedure declared with `.stream(field)`, the pieces of a file upload). Next.js buffers the // body of every request this proxy runs for and drops what is past `proxyClientMaxBodySize` // (10 MB), so a 30 MB file would reach its route cut short and fail its length check. The // response of that route is JSON, which needs no nonce and no policy for a page; the router // sets the headers that matter for JSON itself (router.ts). missing: [{ type: "header", key: "content-type", value: "application/octet-stream" }], }, ], }