@marcel91200 / caddy Mirror · read-only / commits / c96bca12
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 08:30 UTC

rewrite: preserve non-canonical path encoding after uri replace (#7907)

changePath cleared RawPath whenever it equalled url.Path, which discarded a valid non-canonical percent-encoding produced by a replacement. This compare `RawPath` against the default escaping of Path instead.

Saleh committed Jul 26, 2026 at 01:49 UTC c96bca12693419ac5be751a0cbbae2c3cbf89c58
2 files changed +24 -2
modules/caddyhttp/rewrite/rewrite.go
+11 -2
@@ -513,12 +513,21 @@ func changePath(req *http.Request, newVal func(pathOrRawPath string) string) {
513 } else {
514 req.URL.Path = newVal(req.URL.Path)
515 }
516 - // RawPath is only set if it's different from the normalized Path (std lib)
517 - if req.URL.RawPath == req.URL.Path {
516 + // RawPath is only needed if it is a valid, non-canonical encoding of Path;
517 + // (see #6578). Mirror net/url.URL.setPath by comparing against the default
518 + // escaping of Path instead.
519 + if req.URL.RawPath == defaultEscapedPath(req.URL.Path) {
520 req.URL.RawPath = ""
521 }
522 }
523
524 +// defaultEscapedPath returns the canonical percent-encoding of p, matching
525 +// what net/url.URL.EscapedPath() produces when RawPath is empty. It mirrors
526 +// the comparison net/url.URL.setPath uses to decide whether RawPath is needed.
527 +func defaultEscapedPath(p string) string {
528 + return (&url.URL{Path: p}).EscapedPath()
529 +}
530 +
531 // queryOps describes the operations to perform on query keys: add, set, rename and delete.
532 type queryOps struct {
533 // Renames a query key from Key to Val, without affecting the value.
modules/caddyhttp/rewrite/rewrite_test.go
+13
@@ -351,6 +351,19 @@ func TestRewrite(t *testing.T) {
351 input: newRequest(t, "GET", "/foo/findme%2Fbar"),
352 expect: newRequest(t, "GET", "/foo/replaced%2Fbar"),
353 },
354 + {
355 + rule: Rewrite{URISubstring: []substrReplacer{{Find: "%28", Replace: "("}}},
356 + input: newRequest(t, "GET", "/hello/%28%29"),
357 + expect: newRequest(t, "GET", "/hello/(%29"),
358 + },
359 + {
360 + rule: Rewrite{URISubstring: []substrReplacer{
361 + {Find: "%28", Replace: "("},
362 + {Find: "%29", Replace: ")"},
363 + }},
364 + input: newRequest(t, "GET", "/hello/%28%29"),
365 + expect: newRequest(t, "GET", "/hello/()"),
366 + },
367
368 {
369 rule: Rewrite{PathRegexp: []*regexReplacer{{Find: "/{2,}", Replace: "/"}}},