diff --git a/src/__tests__/proxy-body-limit.test.ts b/src/__tests__/proxy-body-limit.test.ts index 07bb01a09..f9fab8f7a 100644 --- a/src/__tests__/proxy-body-limit.test.ts +++ b/src/__tests__/proxy-body-limit.test.ts @@ -102,9 +102,18 @@ describe("proxy matcher", () => { "/api/import/csv/preview", // The composition PUT beside the label upload stays behind the proxy. "/api/medications/abc/ingredients", + // An API path ending in a static-file extension is still an API call: + // the last segment is the caller's choice. + "/api/mood/tags/custom/x.png", + "/api/documents/inbound/abc.txt", + "/api/measurements/x.svg", ]) { expect(re.test(path), path).toBe(true); } + // Static files outside the API still skip it. + for (const path of ["/icons/icon-192.png", "/og.jpg", "/robots.txt"]) { + expect(re.test(path), path).toBe(false); + } }); }); diff --git a/src/proxy.ts b/src/proxy.ts index 132e9c31a..d5227364c 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -857,14 +857,14 @@ export function proxy(request: NextRequest) { export const config = { matcher: [ - // Apply to all routes except static files, Next.js internals, SW, and - // manifest — and except the large-upload API routes listed in + // Apply to all routes except static files outside `/api/`, Next.js + // internals, SW, and manifest — and except the large-upload API routes in // `src/lib/http/proxy-bypass-routes.ts`. Next reads a request body into // memory for the proxy before the route runs, so a route whose body may // exceed `proxyClientMaxBodySize` (1 MB, next.config.ts) is left out of // the matcher and `apiHandler` performs the proxy's duties for it. The // alternation must stay equal to that list; `proxy-body-limit.test.ts` // compiles this exactly as Next does and checks. - "/((?!_next/static|_next/image|favicon.ico|sw\\.js|manifest\\.json|robots\\.txt|sitemap\\.xml|api/(?:import(?:/csv|/labs-csv|/medications-csv|/apple-health-export|/health-connect-export)?|admin/import-apple-health-export|admin/backups/upload|documents/inbound|medications/intake/(?:dose-history-import|bulk)|labs/ocr/extract|medications/[^/]+/ingredients/extract|user/avatar|measurements/batch|workouts/batch|insights/ecg|mood-entries/bulk|cycle/day-logs/bulk)/?$|.*\\.(?:svg|png|jpg|jpeg|gif|webp|txt|xml)$).*)", + "/((?!_next/static|_next/image|favicon.ico|sw\\.js|manifest\\.json|robots\\.txt|sitemap\\.xml|api/(?:import(?:/csv|/labs-csv|/medications-csv|/apple-health-export|/health-connect-export)?|admin/import-apple-health-export|admin/backups/upload|documents/inbound|medications/intake/(?:dose-history-import|bulk)|labs/ocr/extract|medications/[^/]+/ingredients/extract|user/avatar|measurements/batch|workouts/batch|insights/ecg|mood-entries/bulk|cycle/day-logs/bulk)/?$|(?!api/).*\\.(?:svg|png|jpg|jpeg|gif|webp|txt|xml)$).*)", ], };