diff --git a/web/app/manifest.ts b/web/app/manifest.ts index 107e4f0..49df23e 100644 --- a/web/app/manifest.ts +++ b/web/app/manifest.ts @@ -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" }, ], }; } diff --git a/web/middleware.ts b/web/middleware.ts index f27cde4..2378817 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -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).*)", ], };