🐛 bug: Fix the normalization of the leading slash in the root path for fs.FS static serving - #4507
Conversation
static.New only mapped an empty root to "." for io/fs.FS backends. A root of "/" (or any leading-slash value such as "/dist") is not a valid io/fs path, so isFile's fs.FS.Open fails, rootCheckErr is set, and PathRewrite rewrites every request to the not-found sentinel, returning 404 for all assets. This worked in v3.2 and regressed in v3.3/v3.4. Strip leading slashes from root when a filesystem is configured and fall back to "." when nothing remains, matching how "" is already handled. The change is guarded by config.FS != nil, so string/os roots are untouched, and only leading slashes are trimmed so a missing root like "missing" still returns 404 without falling back. Closes #4499
WalkthroughThe static middleware now normalizes ChangesStatic FS Root Normalization
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
middleware/static/static.go (1)
161-170: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
utils.TrimLeftinstead ofstrings.TrimLeft.The codebase already uses
github.com/gofiber/utils/v2'sTrimLeft(s string, cutset byte)for this exact same trimming pattern (seesanitizePath, which doesutils.TrimLeft(s, '/')). This new code should follow the same convention for consistency and to benefit from the package's lower-allocation implementation.♻️ Proposed refactor
if config.FS != nil { - root = strings.TrimLeft(root, "/") + root = utils.TrimLeft(root, '/') if root == "" { root = "." } }As per coding guidelines,
**/*.go: "Prefergithub.com/gofiber/utils/v2helpers (for example,utils.Trim) for common operations such as string manipulation whenever it is practical and appropriate."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@middleware/static/static.go` around lines 161 - 170, The trimming logic in the io/fs compatibility block is using strings.TrimLeft, but this codebase standard is to use github.com/gofiber/utils/v2 helpers for common string operations. Update the root normalization in the static middleware path handling to use utils.TrimLeft, matching the existing sanitizePath pattern, and keep the empty-root fallback to "." unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@middleware/static/static.go`:
- Around line 161-170: The trimming logic in the io/fs compatibility block is
using strings.TrimLeft, but this codebase standard is to use
github.com/gofiber/utils/v2 helpers for common string operations. Update the
root normalization in the static middleware path handling to use utils.TrimLeft,
matching the existing sanitizePath pattern, and keep the empty-root fallback to
"." unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b9492458-3917-47df-9cc1-41cde74679a1
📒 Files selected for processing (2)
middleware/static/static.gomiddleware/static/static_test.go
There was a problem hiding this comment.
Pull request overview
Fixes a regression in middleware/static where using an fs.FS backend with a leading-slash root (e.g. "/" or "/dist") causes every request to be rewritten to the not-found sentinel, resulting in 404s on v3.3/v3.4.
Changes:
- Normalize
rootforfs.FSbackends by stripping leading/characters and defaulting to"."when the result is empty. - Add a unit test covering
fs.FSroots of"/"and"/css"to ensure static assets are served correctly.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| middleware/static/static.go | Normalizes leading-slash roots for fs.FS static serving to avoid invalid io/fs paths triggering global 404 behavior. |
| middleware/static/static_test.go | Adds coverage for leading-slash fs.FS roots to prevent regressions. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4507 +/- ##
==========================================
- Coverage 92.98% 92.94% -0.05%
==========================================
Files 139 139
Lines 13934 13936 +2
==========================================
- Hits 12957 12953 -4
- Misses 607 612 +5
- Partials 370 371 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Congrats on merging your first pull request! 🎉 We here at Fiber are proud of you! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord |
Description
Fixes #4499. Serving an
fs.FSwith a leading-slash root (e.g.static.New("/", static.Config{FS: sub})) returns 404 for every asset on v3.3/v3.4, while it worked on v3.2.Newonly normalizes an empty root to"."forfs.FSbackends:A root of
"/"(or"/dist") is not a validio/fspath —fs.ValidPath("/")isfalse— soisFile→fs.FS.Open("/")fails,rootCheckErris set, andPathRewriterewrites every request to the not-found sentinel:In v3.2 the
isFileerror was ignored (if check, err := isFile(...); err == nil) and there was no sentinel, so the request proceeded. The sentinel added afterwards turned the ignored error into a hard 404, which is the regression reported in #4499.Reproduction
Changes introduced
When a filesystem is configured, strip leading slashes from
rootand fall back to"."when nothing remains, so"/"behaves like""and"/dist"like"dist".The change is guarded by
config.FS != nil, so string/osroots are untouched. Only leading slashes are trimmed, so a genuinely missing root like"missing"still returns 404 without falling back to the fs root (Test_Static_FS_MissingRootDoesNotFallbackandTest_Static_FS_RootDirectoryEnforcedstill pass).Documentation Update: not needed — the docs already recommend
""; this makes the equivalent"/"work instead of silently 404ing.Type of change
Checklist
Test_Static_FS_RootLeadingSlash) that fails before and passes after the change.go test ./middleware/static/...,go vet,gofmt, andgolangci-lint run ./middleware/static/...(0 issues) all pass locally.