Files
klz-cables.com/middleware.ts

79 lines
2.5 KiB
TypeScript

import createMiddleware from 'next-intl/middleware';
import { NextRequest } from 'next/server';
// Create the internationalization middleware
const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales: ['en', 'de'],
// Used when no locale matches
defaultLocale: 'en',
});
export default function middleware(request: NextRequest) {
const { method, url, headers } = request;
const { pathname } = request.nextUrl;
// Explicit bypass for infrastructure routes to avoid locale redirects/interception
if (
pathname.startsWith('/stats') ||
pathname.startsWith('/errors') ||
pathname.startsWith('/health') ||
pathname.startsWith('/api/og') ||
pathname.includes('opengraph-image')
) {
return;
}
// Build header object for logging
const headerObj: Record<string, string> = {};
headers.forEach((value, key) => {
headerObj[key] = value;
});
// Defensive URL correction for internal container leakage (0.0.0.0, klz-app, localhost)
// This prevents hydration mismatches and host poisoning in generated links/metadata.
const urlObj = new URL(url);
const internalHosts = ['0.0.0.0', 'klz-app', 'localhost', '127.0.0.1'];
let effectiveRequest = request;
if (internalHosts.includes(urlObj.hostname)) {
const proto = headers.get('x-forwarded-proto') || 'https';
// Prioritize x-forwarded-host (passed by Traefik) over the local Host header
const hostHeader =
headers.get('x-forwarded-host') || headers.get('host') || 'testing.klz-cables.com';
hostHeader.split(':');
urlObj.protocol = proto;
// urlObj.hostname = publicHostname; // Don't rewrite hostname yet as it breaks internal fetches in dev
// urlObj.port = ''; // DON'T clear internal port (3000) anymore
effectiveRequest = new NextRequest(urlObj, {
headers: request.headers,
method: request.method,
body: request.body,
});
console.log(
`🛡️ Middleware: Fixed internal URL leak: ${url} -> ${urlObj.toString()} | Proto: ${proto} | Host: ${hostHeader}`,
);
}
try {
// Apply internationalization middleware
const response = intlMiddleware(effectiveRequest);
return response;
} catch (error) {
console.error(
`Request failed: method=${method} url=${url} headers=${JSON.stringify(headerObj)}`,
error,
);
throw error;
}
}
export const config = {
// Match only internationalized pathnames
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)', '/', '/(de|en)/:path*'],
};