Skip to content

fix(v1): preserve encoded req.url for proxied requests#1356

Closed
guoyangzhen wants to merge 1 commit intoh3js:v1from
guoyangzhen:fix/v1-req-url-encoding
Closed

fix(v1): preserve encoded req.url for proxied requests#1356
guoyangzhen wants to merge 1 commit intoh3js:v1from
guoyangzhen:fix/v1-req-url-encoding

Conversation

@guoyangzhen
Copy link
Copy Markdown

Fixes #1354

Problem

Since v1.15.7, createAppEventHandler decodes percent-encoded URL paths and writes the decoded result back to event.node.req.url. This causes HTTP proxies (like Nitro's dev server using httpxy) to forward requests with raw UTF-8 characters instead of percent-encoded form, resulting in 400 Bad Request from downstream servers.

Example:

Incoming:  /api/file/%C3%A9.txt
req.url:   /api/file/é.txt  (decoded — breaks proxy)
Expected:  /api/file/%C3%A9.txt  (encoded — correct)

Root cause

_decodePath decodes %C3%A9 → é, then line 174 (event.node.req.url = _layerPath) overwrites req.url with the decoded path.

Fix

  1. Preserve the original encoded req.url before any modifications
  2. For the common case (no route prefix), keep req.url unchanged
  3. For mounted sub-apps with route prefix, strip prefix from the encoded URL (not the decoded one)
  4. Decoded path is still used for internal route matching — security fix from v1.15.7 is preserved

Key changes

  • Added _originalReqUrl to preserve the incoming encoded URL
  • Removed event.node.req.url = _layerPath (the decoded assignment)
  • For prefix stripping, compute from encoded URL instead
  • event._path (used by event.path getter) still uses decoded path for correct route matching

When a request arrives with percent-encoded characters (e.g., /api/%C3%A9),
h3 v1.15.7+ decodes the path for internal routing but also overwrites
 with the decoded result. This breaks HTTP proxies (like Nitro's
dev server using httpxy) because they forward the decoded raw UTF-8 characters
instead of the percent-encoded form, causing 400 Bad Request from downstream.

This fix:
- Preserves the original encoded  for the common case (no prefix)
- For mounted sub-apps with route prefix, strips prefix from the encoded URL
  (not the decoded one)
- Decoded path is still used for internal route matching (security fix preserved)
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

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: Pro

Run ID: f0622254-e8ae-487c-bae1-2cf29f34e32e

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@pi0
Copy link
Copy Markdown
Member

pi0 commented Mar 22, 2026

fixed by #1355

@pi0 pi0 closed this Mar 22, 2026
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.

2 participants