Skip to content

fix(error): don't throw on unparsable Host header in default error handlers - #4640

Open
nikolas-sapa wants to merge 1 commit into
nitrojs:v2from
nikolas-sapa:fix/4555-error-handler-host-header
Open

nikolas-sapa wants to merge 1 commit into
nitrojs:v2from
nikolas-sapa:fix/4555-error-handler-host-header

Conversation

@nikolas-sapa

Copy link
Copy Markdown

🔗 Linked issue

Resolves #4555

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

The default error handlers build the request URL from the raw Host header via getRequestURL(event, { xForwardedHost: true, xForwardedProto: true }). For a header that new URL() cannot parse — Host: a b from the issue's reproduction — that call throws inside the error handler itself, and the throw escapes as an unhandled rejection: every such request logs a full stack trace through trapUnhandledNodeErrors and the client receives a reduced body instead of the proper error payload. The Host header is unvalidated input, so this is reachable by anyone with curl -H 'Host: a b'.

Both handlers are affected identically (src/runtime/internal/error/prod.ts and dev.ts), so the fix adds one shared helper, getRequestURLOrFallback, in internal/error/utils.ts: it resolves the URL exactly as before, and on an unparsable authority falls back to a synthetic http://localhost base while preserving path and query. That keeps all three URL consumers working in the degraded case — the 404 baseURL redirect logic (pathname/search), the [request error] log line, and the response body's url field.

For every request with a valid Host or x-forwarded-host, behaviour is byte-identical to before; the handler only diverges where it previously threw.

Testing. test/unit/error-handler.test.ts covers: valid Host (unchanged URL), the malformed-Host degradation (no throw, fallback authority, status intact), path+query preservation, x-forwarded-host precedence (also unchanged), and the dev handler. Reverting the source change turns the three degradation tests red — the handler throws TypeError [ERR_INVALID_URL] before reaching a response — while the two valid-header tests stay green either way. test/unit passes (46 tests, including the pre-existing files), and tsc --noEmit / eslint / prettier are clean.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

…ndlers

The default error handlers build the request URL from the raw Host
header via `getRequestURL`. For a header that `new URL()` cannot parse
(e.g. `Host: a b`), that call throws inside the handler itself, and the
throw escapes as an unhandled rejection: every such request logs a full
stack trace and the client receives a reduced body instead of the proper
error payload.

Add `getRequestURLOrFallback` and use it in both the production and dev
handlers: on an unparsable authority it falls back to a synthetic
`http://localhost` base, preserving path and query so the 404 redirect
logic, the log line and the response body keep working. Valid Host and
x-forwarded-host values are resolved exactly as before.

Fixes nitrojs#4555
@nikolas-sapa
nikolas-sapa requested a review from pi0 as a code owner September 19, 2026 09:56
@vercel

vercel Bot commented Sep 19, 2026

Copy link
Copy Markdown

@nikolas-sapa is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0da02e94-676c-4e77-8fa8-85d3e37d7879

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant