Skip to content

🔥 feat: add SkipUnmatchedRoutes config option#4411

Open
muzzii255 wants to merge 12 commits into
gofiber:mainfrom
muzzii255:feature/skip-unmatched-routes
Open

🔥 feat: add SkipUnmatchedRoutes config option#4411
muzzii255 wants to merge 12 commits into
gofiber:mainfrom
muzzii255:feature/skip-unmatched-routes

Conversation

@muzzii255

@muzzii255 muzzii255 commented Jun 7, 2026

Copy link
Copy Markdown

Description

Adds a SkipUnmatchedRoutes config option that short-circuits requests to unregistered paths — returning 404 immediately without running through the middleware chain. Useful for cutting unnecessary processing from bots, scanners, and bad URLs.

Fixes #4403

Changes introduced

SkipUnmatchedRoutes on app.Config, adds extra lookup costing 150-200ns on matched routes but saving same on unmatched routes

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD Ryzen 7 9700X 8-Core Processor             
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4276250	       280.2 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4258561	       281.4 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4216581	       284.7 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4141472	       281.5 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4173716	       283.6 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	20101112	        59.06 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	21355640	        56.42 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	19912690	        59.81 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	21089865	        56.41 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	21032264	        56.59 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4840177	       246.3 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4927646	       243.0 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4730292	       255.3 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4752334	       251.9 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4898761	       242.6 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 2848100	       422.5 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 2911087	       413.6 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 2911100	       406.0 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 2843078	       416.9 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 2911432	       415.4 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/gofiber/fiber/v3	23.896s
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

@muzzii255 muzzii255 requested a review from a team as a code owner June 7, 2026 20:59
@welcome

welcome Bot commented Jun 7, 2026

Copy link
Copy Markdown

Thanks for opening this pull request! 🎉 Please check out our contributing guidelines. If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds SkipUnmatchedRoutes to fiber.Config; implements App.routeExists and integrates an early-404 short-circuit into both default and custom request handlers when enabled; updates docs and adds tests plus two benchmarks.

Changes

SkipUnmatchedRoutes Feature

Layer / File(s) Summary
Config field declaration
app.go, docs/api/fiber.md
Adds SkipUnmatchedRoutes bool to fiber.Config with JSON tag and documentation entry describing immediate 404 for unmatched paths when enabled.
Route detection helper and handler short-circuit
router.go
Adds internal App.routeExists and integrates early 404 short-circuit checks into defaultRequestHandler and customRequestHandler when SkipUnmatchedRoutes is true and no endpoint route matches.
Tests and benchmarks
router_test.go
Adds Test_App_SkipUnmatchedRoutes validating middleware skipping and matching behavior (including CaseSensitive/StrictRouting), plus two benchmarks comparing matched/unmatched handler costs with and without the option.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Handler as defaultRequestHandler/customRequestHandler
  participant Config as app.config.SkipUnmatchedRoutes
  participant RouteChecker as App.routeExists
  participant Middleware as middleware chain
  participant Endpoint as matched endpoint route

  Client->>Handler: incoming HTTP request
  Handler->>Config: read SkipUnmatchedRoutes
  alt SkipUnmatchedRoutes == true
    Handler->>RouteChecker: routeExists(method, path)
    alt routeExists == false
      Handler->>Handler: return 404 StatusNotFound
    else routeExists == true
      Handler->>Middleware: enter middleware chain
      Middleware->>Endpoint: execute matched endpoint
    end
  else SkipUnmatchedRoutes == false
    Handler->>Middleware: enter middleware chain
    Middleware->>Endpoint: scan & execute matched endpoint
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • gofiber/fiber#4233: Prior modifications to request handler selection and invocation paths used by this short-circuit integration.
  • gofiber/fiber#3261: Earlier refactor of default/custom request handlers that this change builds upon.
  • gofiber/fiber#3817: Related routing behavior changes affecting endpoint detection and 404 outcomes.

Possibly related issues

Suggested labels

⚡️ Performance

Suggested reviewers

  • sixcolors
  • efectn
  • gaby

Poem

🐰 I hopped through routes with whiskers keen,
I checked each path for places unseen.
When none replied, I paused, then fled—
A tiny 404, then off to my bed.
🥕 Hop, code, hop — the router's clean.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature being added: a new SkipUnmatchedRoutes config option. It accurately reflects the primary change across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description includes the required summary, linked issue, and benchmark details, matching the template overall.
✨ 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 Jun 7, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Jun 7, 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)
router_test.go (1)

2570-2665: ⚡ Quick win

Add a custom-context variant for SkipUnmatchedRoutes to cover both handler paths.

These tests validate the default handler path well, but they don’t explicitly assert behavior through newCustomApp()/custom request handling. Adding one matched + one unmatched subtest there would guard against regressions in customRequestHandler too.

Based on learnings from provided context: SkipUnmatchedRoutes short-circuit logic is present in both default and custom handlers (router.go:449-502).

🤖 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 `@router_test.go` around lines 2570 - 2665, Add companion subtests to
Test_App_SkipUnmatchedRoutes that exercise the custom-context path by creating
an app via newCustomApp() (or the equivalent factory used in custom request
handling) and invoking its Test helper with both a matched route ("/users") and
an unmatched route ("/notfound" or case/strict variants) while
SkipUnmatchedRoutes is true; assert middlewareCalled semantics match the
existing default-app cases so the short-circuit logic in customRequestHandler
(and related code handling SkipUnmatchedRoutes) is covered and won’t regress.
🤖 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 `@router_test.go`:
- Around line 2570-2665: Add companion subtests to Test_App_SkipUnmatchedRoutes
that exercise the custom-context path by creating an app via newCustomApp() (or
the equivalent factory used in custom request handling) and invoking its Test
helper with both a matched route ("/users") and an unmatched route ("/notfound"
or case/strict variants) while SkipUnmatchedRoutes is true; assert
middlewareCalled semantics match the existing default-app cases so the
short-circuit logic in customRequestHandler (and related code handling
SkipUnmatchedRoutes) is covered and won’t regress.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f733da5b-bbe5-4e94-bbd5-fc8bf48a7b6b

📥 Commits

Reviewing files that changed from the base of the PR and between 5c45bce and e94a5b9.

📒 Files selected for processing (3)
  • app.go
  • router.go
  • router_test.go

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.28571% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.00%. Comparing base (3b7edda) to head (b665b74).

Files with missing lines Patch % Lines
router.go 93.10% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4411      +/-   ##
==========================================
+ Coverage   91.97%   92.00%   +0.02%     
==========================================
  Files         138      138              
  Lines       13486    13521      +35     
==========================================
+ Hits        12404    12440      +36     
+ Misses        687      686       -1     
  Partials      395      395              
Flag Coverage Δ
unittests 92.00% <94.28%> (+0.02%) ⬆️

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.

@ReneWerner87

Copy link
Copy Markdown
Member

@muzzii255 pls check linter errors

@ReneWerner87

Copy link
Copy Markdown
Member

@muzzii255 pls add something in the markdown docs for this feature

@muzzii255

Copy link
Copy Markdown
Author

@ReneWerner87 done

@ReneWerner87

Copy link
Copy Markdown
Member

@gaby pls check

@gaby gaby changed the title 🔥 Feature: add SkipUnmatchedRoutes config option 🔥 feat: add SkipUnmatchedRoutes config option Jun 8, 2026

@gaby gaby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need to do the changes in next(), else we are checking the whole router twice for every requests. That defeats the purpose of this feature.

Comment thread router.go Outdated
Comment thread router.go Outdated
@gaby

gaby commented Jun 8, 2026

Copy link
Copy Markdown
Member

@gaby pls check

When SkipUnmatchedRoutes the router matching logic runs twice for every request. This is not ideal.

@muzzii255

Copy link
Copy Markdown
Author

@gaby @ReneWerner87 i tried multiple approach like defering the middlewares when SkipUnmatchedRoutes to prevent extra lookup or using the routeExists to match the route and send the route to next() which will run the middlewares, none of them worked really well. the only thing left is to rewrite the tree which i am not supporting as it might create more bugs in the future. since its an optional feature, user will know the cost of using it.

@muzzii255

Copy link
Copy Markdown
Author

any approach will add an extra lookup on the tree except rewriting the tree, right now i am using a middleware from the PR #4406 on my codebase, it will be easier for me to just use the fiber's internal tree.

@gaby

gaby commented Jun 10, 2026

Copy link
Copy Markdown
Member

@muzzii255 I think in next() we can check. IsMatched() and if false call c.SendString(not found)

@muzzii255

Copy link
Copy Markdown
Author

@muzzii255 I think in next() we can check. IsMatched() and if false call c.SendString(not found)

let me test it

@muzzii255

Copy link
Copy Markdown
Author

@muzzii255 I think in next() we can check. IsMatched() and if false call c.SendString(not found)

in next() function this loop executes the middlewares before isMatched() is called, i can try defering the middleware execute by appending the handler to a slice, iterate and execute the middleware after if c.isMatched check

	// Loop over the route stack starting from previous index
	for indexRoute < lenr {
		// Increment route index
		indexRoute++

		// Get *Route
		route := tree[indexRoute]

		if route.mount {
			continue
		}

		// Check if it matches the request path
		if !route.match(detectionPath, path, &c.values) {
			continue
		}

		if c.shouldSkipNonUseRoutes && !route.use {
			continue
		}

		// Pass route reference and param values
		c.route = route
		// Non use handler matched
		if !route.use {
			c.isMatched = true
		}
		// Execute first handler of route
		if len(route.Handlers) > 0 {
			c.indexHandler = 0
			c.indexRoute = indexRoute
			return true, route.Handlers[0](c)
		}

		return true, nil // Stop scanning the stack
	}
	
	// If c.Next() does not match, return 404
	// If no match, scan stack again if other methods match the request
	// Moved from app.handler because middleware may break the route chain
	if c.isMatched {
		return false, ErrNotFound
	}

@muzzii255

Copy link
Copy Markdown
Author

@muzzii255 I think in next() we can check. IsMatched() and if false call c.SendString(not found)

@gaby Do you mean inside next() do the endpoint scan first (before the main loop runs any middleware), and return ErrNotFound if nothing matches? Because checking IsMatched after the loop is too late — middleware has already executed by then, which defeats the feature.

@muzzii255

Copy link
Copy Markdown
Author

@gaby Dropped the second scan in SkipUnmatchedRoutes. routeExists -> bool is now firstEndpointIndex -> int — it returns the stack index of the first matching endpoint instead of just whether one exists. next/nextCustom use that index to skip re-matching the endpoints the lookahead already ruled out (if !route.use && indexRoute < firstMatchIndex), so middleware are never skipped and the chain is unchanged. the matched path no longer matches endpoints twice and unmatched/bot requests still 404 before the middleware chain.
benchmark on matched endpoint dropped from 415 to 280.

goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD Ryzen 7 9700X 8-Core Processor
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4527450	       267.2 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4390478	       272.7 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4406754	       272.7 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4442115	       269.5 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/without_skip-16         	 4516107	       268.6 ns/op	       8 B/op	       1 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	21924752	        53.24 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	22678478	        53.30 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	21954602	        54.07 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	22341717	        52.66 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Unmatched/with_skip-16            	22903267	        52.27 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4972453	       233.9 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 5155741	       240.5 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 5021347	       239.5 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 5073507	       239.6 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/without_skip-16           	 4864011	       246.4 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 4219206	       283.2 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 4405276	       277.8 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 4316749	       278.4 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 4253124	       282.0 ns/op	       0 B/op	       0 allocs/op
Benchmark_SkipUnmatchedRoutes_Matched/with_skip-16              	 4302944	       279.2 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/gofiber/fiber/v3	24.017s

@muzzii255 muzzii255 requested a review from gaby June 22, 2026 23:47

@gaby gaby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

📝 [Proposal]: skip group/prefix middleware when no child route matches

3 participants