Skip to content

🐛 bug: Fix the normalization of the leading slash in the root path for fs.FS static serving - #4507

Merged
ReneWerner87 merged 2 commits into
gofiber:mainfrom
sueun-dev:fix/static-fs-leading-slash-root
Jul 9, 2026
Merged

ReneWerner87 merged 2 commits into
gofiber:mainfrom
sueun-dev:fix/static-fs-leading-slash-root

Conversation

@sueun-dev

Copy link
Copy Markdown
Contributor

Description

Fixes #4499. Serving an fs.FS with 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.

New only normalizes an empty root to "." for fs.FS backends:

if config.FS != nil && root == "" {
    root = "."
}

A root of "/" (or "/dist") is not a valid io/fs path — fs.ValidPath("/") is false — so isFilefs.FS.Open("/") fails, rootCheckErr is set, and PathRewrite rewrites every request to the not-found sentinel:

if rootCheckErr != nil && fileServer.FS != nil {
    return []byte(invalidPathSentinel)
}

In v3.2 the isFile error 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

fsys := fstest.MapFS{"index.html": {Data: []byte("INDEX")}}
app := fiber.New()
app.Use("/", static.New("/", static.Config{FS: fsys}))
// GET /index.html → v3.2: 200 "INDEX"  |  v3.3/v3.4: 404 "Not Found"

Changes introduced

  • When a filesystem is configured, strip leading slashes from root and fall back to "." when nothing remains, so "/" behaves like "" and "/dist" like "dist".

  • The change is guarded by config.FS != nil, so string/os roots 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_MissingRootDoesNotFallback and Test_Static_FS_RootDirectoryEnforced still pass).

  • Documentation Update: not needed — the docs already recommend ""; this makes the equivalent "/" work instead of silently 404ing.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • Added a unit test (Test_Static_FS_RootLeadingSlash) that fails before and passes after the change.
  • go test ./middleware/static/..., go vet, gofmt, and golangci-lint run ./middleware/static/... (0 issues) all pass locally.

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
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The static middleware now normalizes root for config.FS by trimming leading slashes and falling back to "." when needed. A new table-driven test checks serving from "/" and "/css" roots.

Changes

Static FS Root Normalization

Layer / File(s) Summary
Trim leading slash and validate
middleware/static/static.go, middleware/static/static_test.go
New strips leading / from root when config.FS is set, defaulting to "." when empty; a new table-driven test verifies correct index and CSS serving for roots "/" and "/css".

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • gofiber/fiber#3006: Introduced io/fs support to the static middleware, which is the same area affected by this root-handling fix.
  • gofiber/fiber#4277: Also adjusts New() root handling for config.FS-backed static serving and adds related test coverage.

Suggested reviewers: sixcolors, efectn, ReneWerner87

Poem

A slash at the start led paths astray,
But this bunny hopped in to trim it away.
Now "/" and "/css" serve files just right,
Index and styles found in the light.
🐰✂️ Snip goes the slash, hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code and test address #4499 by normalizing leading-slash fs.FS roots so static assets are served correctly.
Out of Scope Changes check ✅ Passed The changes stay focused on fs.FS root normalization and a regression test, with no unrelated edits apparent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly describes the fs.FS leading-slash root normalization bug fix.
Description check ✅ Passed The description covers the issue, reproduction, changes, type of change, and checklist well enough for the template.
✨ 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.

@ReneWerner87 ReneWerner87 added this to v3 Jul 6, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Jul 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
middleware/static/static.go (1)

161-170: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use utils.TrimLeft instead of strings.TrimLeft.

The codebase already uses github.com/gofiber/utils/v2's TrimLeft(s string, cutset byte) for this exact same trimming pattern (see sanitizePath, which does utils.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: "Prefer github.com/gofiber/utils/v2 helpers (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

📥 Commits

Reviewing files that changed from the base of the PR and between 9390f8f and 5dbd08b.

📒 Files selected for processing (2)
  • middleware/static/static.go
  • middleware/static/static_test.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 root for fs.FS backends by stripping leading / characters and defaulting to "." when the result is empty.
  • Add a unit test covering fs.FS roots 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.

Comment thread middleware/static/static.go Outdated
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.94%. Comparing base (9390f8f) to head (8298bab).

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     
Flag Coverage Δ
unittests 92.94% <100.00%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@gaby gaby changed the title 🩹 Fix: normalize leading-slash root for fs.FS static serving 🐛 bug: Fix normalize leading-slash root for fs.FS static serving Jul 7, 2026
@gaby gaby changed the title 🐛 bug: Fix normalize leading-slash root for fs.FS static serving 🐛 bug: Fix the normalization of the leading slash in the root path for fs.FS static serving Jul 7, 2026
@ReneWerner87
ReneWerner87 merged commit a5a8690 into gofiber:main Jul 9, 2026
19 checks passed
@welcome

welcome Bot commented Jul 9, 2026

Copy link
Copy Markdown

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

@github-project-automation github-project-automation Bot moved this to Done in v3 Jul 9, 2026
@ReneWerner87 ReneWerner87 modified the milestones: v3, v3.5.0 Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: static.New works correctly in 3.2 but fails in 3.4

4 participants