-
Notifications
You must be signed in to change notification settings - Fork 29.8k
fix: middleware header encoding for non-ASCII characters #85880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Conversation
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
1 similar comment
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
| const valueKey = 'x-middleware-request-' + key | ||
| const newValue = middlewareHeaders[valueKey] | ||
| const encodedValue = middlewareHeaders[valueKey] | ||
| const newValue = encodedValue === null ? null : decodeHeaderValue(encodedValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const newValue = encodedValue === null ? null : decodeHeaderValue(encodedValue) | |
| const newValue = encodedValue == null ? null : decodeHeaderValue(encodedValue as string) |
The code calls decodeHeaderValue(encodedValue) without checking if encodedValue is undefined, which could occur when a header key doesn't exist in the middleware response. This passes undefined to a function expecting a string.
View Details
Analysis
Null/undefined check for middleware override header values
What fails: resolveRoutes() in packages/next/src/server/lib/router-utils/resolve-routes.ts at line 615 calls decodeHeaderValue(encodedValue) without checking if encodedValue is undefined, violating the function's type contract decodeHeaderValue(value: string): string.
How to reproduce: Create a middleware that deletes a header using headers.delete() and includes that header in x-middleware-override-headers, but the middleware response doesn't include a corresponding x-middleware-request-* header.
Example middleware:
export function middleware(request) {
const headers = new Headers(request.headers)
headers.delete('x-from-client') // Delete a header
return NextResponse.next({
request: { headers }
})
}When the override headers logic processes the deleted header, middlewareHeaders[valueKey] returns undefined (not null), and this undefined value is passed to decodeHeaderValue().
Result: The code passes undefined to a function typed to accept only string, violating the type contract. While decodeHeaderValue() has a try-catch that prevents crashes, it returns undefined instead of the promised string.
Expected: The code should check for both null and undefined using == (or typeof encodedValue === 'string') to properly handle missing header values before calling decodeHeaderValue().
Fix applied: Changed line 615 from:
const newValue = encodedValue === null ? null : decodeHeaderValue(encodedValue)to:
const newValue = encodedValue == null ? null : decodeHeaderValue(encodedValue as string)The == operator catches both null and undefined, and the as string type assertion confirms that at that point, the value is known to be a string (not an array or undefined).
What?
Fixes middleware header encoding issue where non-ASCII characters (like "Montréal") in request headers cause "Header contains non-ASCII characters" errors in Vercel deployments.
Why?
HTTP headers must be ASCII-safe. Middleware was passing non-ASCII characters without encoding, causing deployment failures.
How?
encodeHeaderValue()/decodeHeaderValue()utilities for safe encoding/decodingFixes #85631