fix(proxy): OG-Image und Next-Special-Routes vom Locale-Rewrite ausnehmen

Vorher: /de/projects/hermes/opengraph-image wurde vom Proxy auf
/projekte/hermes (die Page, nicht das OG-Image) redirected. Grund:
toPublicFromInternal('/de/projects/hermes/opengraph-image') hat den
Path '/de/projects/hermes' als 'about' gemappt und '/projekte/hermes'
zurückgegeben — ohne Beachtung des '/opengraph-image'-Suffixes. Result:
Crawler (Twitter, LinkedIn, Slack) konnten die OG-Images nicht mehr
abrufen, sie haben 307 bekommen statt 200.

Fix: Im Proxy eine 'isNextSpecialRoute'-Prüfung VOR dem Locale-Rewrite
eingefügt. Routen mit Suffix wie 'opengraph-image', 'twitter-image',
'sitemap.xml', 'robots.txt', 'icon', 'apple-icon' werden direkt zum
nächsten Handler durchgelassen — kein Redirect, kein Rewrite.

Damit:
- /de/projects/hermes/opengraph-image → 200 (PNG, OG-Image)
- /de/projects/hermes/twitter-image → 200 (PNG, Twitter-Image, wenn vorhanden)
- /sitemap.xml → 200 (statisch, wie vorher)
- /robots.txt → 200 (statisch)
- Normale Locale-Rewrites für /de/... und public → /<locale>/...
  funktionieren weiterhin.
This commit is contained in:
Jan Wagner
2026-08-31 21:21:06 +02:00
parent 6659300c23
commit 3b30694573
+29 -2
View File
@@ -1,10 +1,31 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { defaultLocale, isLocale, resolvePublicPath, toInternalPath, toPublicFromInternal, switchLocale } from "@/i18n/routes";
import { defaultLocale, isLocale, resolvePublicPath, toInternalPath, toPublicFromInternal } from "@/i18n/routes";
import { readCookieFromHeader } from "@/lib/auth/cookie";
import { verifySession } from "@/lib/auth/session";
const PUBLIC_FILE = /\.(?:svg|png|jpg|jpeg|gif|webp|ico|txt|xml|json|css|js|map)$/;
// Next.js-Konvention: opengraph-image/twitter-image/etc. sind Spezial-Routes,
// die unter einer Page hängen aber als eigene URL exposed werden (mit eigenem
// Segment nach dem Slug). Sie dürfen NICHT von unserem Locale-Rewrite zu einer
// anderen URL umgebogen werden — sonst liefert der Proxy 307 auf die Page.
const NEXT_SPECIAL_SUFFIXES = [
"opengraph-image",
"twitter-image",
"sitemap.xml",
"robots.txt",
"icon",
"apple-icon",
"manifest.webmanifest",
];
function isNextSpecialRoute(pathname: string): boolean {
// /de/projects/hermes/opengraph-image → matcht "opengraph-image"
// /api/... → matcht nichts (sollte gar nicht hier landen, der Proxy-Rewrite
// hat eigene Regeln)
const segments = pathname.split("/").filter(Boolean);
const last = segments[segments.length - 1] ?? "";
return NEXT_SPECIAL_SUFFIXES.includes(last);
}
function isAdminLoginPath(pathname: string): boolean {
return pathname === "/admin/login" || pathname.startsWith("/admin/login/");
@@ -46,6 +67,12 @@ export function proxy(request: NextRequest) {
}
}
// Next-Special-Routes (OG-Image, Sitemap, etc.) durchlassen, ohne sie
// durch die Locale-Rewrite zu jagen — die haben ihre eigene URL.
if (isNextSpecialRoute(pathname)) {
return NextResponse.next({ request: { headers } });
}
// /de/... → public
if (pathname === "/de" || pathname.startsWith("/de/")) {
const publicPath = toPublicFromInternal(pathname);