Handling Requests for Non-existent Static Files in a Multilingual Next.js App (App Router)
Unanswered
bardaxx™ posted this in #help-forum
bardaxx™OP
Hi everyone,
I’m building a multilingual Next.js 14 app using the App Router (the app/ directory with dynamic [language] and [slug] folders). My folder structure looks like this:
The issue:
When someone requests a path like: https://mydomain.com/file.zip
Next.js tries to interpret file.zip as the [language] parameter. Because the string contains a . it fails to match the route parameter pattern, throwing:
In development, I tried to catch these “bad” requests in layout file logic:
This seemed to work locally (redirecting /file.zip to my custom /en/not-found page), but in production on Vercel it doesn’t—requests still hit the [language] route and crash before middleware can run. I've tried also to implement something like this in middleware with no luck. Any hint?
How can I intercept or short-circuit these requests—whether via middleware, custom route handlers so they return a 404 (or redirect to my not-found page) instead? What’s the recommended pattern?
I’m building a multilingual Next.js 14 app using the App Router (the app/ directory with dynamic [language] and [slug] folders). My folder structure looks like this:
app/
├─ [language]/
│ ├─ [slug]/
│ │ ├─ page.tsx
│ │ └─ not-found.tsx
│ └─ page.tsx ← language root (e.g. `/en`)
└─ page.tsx ← fallback for `/`
The issue:
When someone requests a path like: https://mydomain.com/file.zip
Next.js tries to interpret file.zip as the [language] parameter. Because the string contains a . it fails to match the route parameter pattern, throwing:
[Error]: Attribute name "file.zip" contains invalid characters;
it must match the pattern "^\$?[a-zA-Z0-9_-]+$"
In development, I tried to catch these “bad” requests in layout file logic:
// middleware.ts (development)
const ALLOWED_PATHS = [
'/favicon.svg',
'/favicon.ico',
'/images/',
// etc.
];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Only allow static assets in the whitelist
const isAllowedPath = ALLOWED_PATHS.some((allowed) =>
allowed.endsWith('/')
? pathname.startsWith(allowed)
: pathname === allowed
);
if (isAllowedPath) {
return NextResponse.next();
}
// Block anything with unusual characters (dots, zip, php, etc.)
if (/[^a-zA-Z0-9-]/.test(pathname)) {
return NextResponse.redirect(new URL('/en/not-found', request.url));
}
return NextResponse.next();
}
This seemed to work locally (redirecting /file.zip to my custom /en/not-found page), but in production on Vercel it doesn’t—requests still hit the [language] route and crash before middleware can run. I've tried also to implement something like this in middleware with no luck. Any hint?
How can I intercept or short-circuit these requests—whether via middleware, custom route handlers so they return a 404 (or redirect to my not-found page) instead? What’s the recommended pattern?