fix(web): unblock PWA icon under trailingSlash routing

next.config.ts has trailingSlash: true, so Next.js 308-redirects /icon to
/icon/. The middleware matcher only excluded the no-slash form, so after
the redirect the auth gate kicked in and bounced /icon/ to /cm-auth — the
browser got an HTML page where it expected a PNG, and the manifest icon
failed to install ('Download error or resource isn't a valid image').

- middleware: matcher now allows the optional slash on icon and apple-icon.
- manifest: point icons at the canonical /icon/ and /apple-icon/ URLs so
  the browser fetches the PNG directly without a redirect round-trip.
This commit is contained in:
yiekheng 2026-05-03 10:26:09 +08:00
parent eb297e977e
commit 626344cc16
2 changed files with 12 additions and 3 deletions

View File

@ -11,8 +11,12 @@ export default function manifest(): MetadataRoute.Manifest {
background_color: "#fafafa",
theme_color: "#18181b",
icons: [
{ src: "/icon", sizes: "any", type: "image/png" },
{ src: "/apple-icon", sizes: "180x180", type: "image/png" },
// Trailing slash on /icon/ and /apple-icon/ matches the canonical URL
// Next.js serves under `trailingSlash: true`. Without the slash the
// browser would hit a 308 redirect, then the gated /icon/ path, then
// get HTML back instead of the PNG.
{ src: "/icon/", sizes: "any", type: "image/png" },
{ src: "/apple-icon/", sizes: "180x180", type: "image/png" },
],
};
}

View File

@ -36,7 +36,12 @@ export async function middleware(req: NextRequest) {
}
export const config = {
// next.config.ts sets `trailingSlash: true`, so /icon redirects to /icon/.
// The icon$/apple-icon$ alternatives below allow the optional slash so the
// canonical (slashed) URL bypasses the auth gate too — otherwise the
// browser hits the redirect, follows it to the slashed form, and the gate
// refuses to serve the image and bounces to /cm-auth.
matcher: [
"/((?!_next|icon$|apple-icon$|manifest.webmanifest|favicon.ico).*)",
"/((?!_next|icon/?$|apple-icon/?$|manifest.webmanifest|favicon.ico).*)",
],
};