Skip to content

Conversation

@naaa760
Copy link

@naaa760 naaa760 commented Nov 7, 2025

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?

  • Added encodeHeaderValue()/decodeHeaderValue() utilities for safe encoding/decoding
  • Modified middleware response handling to encode headers
  • Modified route resolution to decode headers
  • Added comprehensive tests

Fixes #85631

@ijjk ijjk added the type: next label Nov 7, 2025
@ijjk
Copy link
Member

ijjk commented Nov 7, 2025

Allow CI Workflow Run

  • approve CI run for commit: 3c04d72

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

1 similar comment
@ijjk
Copy link
Member

ijjk commented Nov 7, 2025

Allow CI Workflow Run

  • approve CI run for commit: 3c04d72

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Encoding issue with middleware rewrites and headers

2 participants