-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Description
Feature Description
Summary
Add an optional router configuration that allows Gin to treat routes with and without a trailing slash as equivalent, without performing a redirect.
When this option is enabled, routes like /health and /health/ should both be handled by the same route handler, even if only one of them was explicitly registered.
Motivation
Currently, Gin uses redirection to normalize trailing slashes.
If a route /health is registered, a request to /health/ results in a 301 or 307 redirect (depending on the request method).
While this behavior is correct for many web applications, it can be undesirable in APIs and microservices, where:
- Redirects introduce unnecessary latency.
- Some clients don’t expect or handle redirects well.
- Users want a consistent experience where
/fooand/foo/are logically the same endpoint.
This feature would make Gin more flexible and better suited for these use cases.
Proposal
Introduce a new Engine (or RouterGroup) option to enable this behavior, for example:
r := gin.New()
r.SetTrailingSlashBehavior(gin.TrailingSlashIgnore)or via a configuration flag:
r := gin.Default()
r.IgnoreTrailingSlash = trueWhen enabled:
/healthand/health/both match the same route, without redirecting.- Registered paths in the router tree should match regardless of the trailing slash.
- The original
Request.URL.Pathshould remain unchanged.
Example
r := gin.Default()
r.IgnoreTrailingSlash = true
r.GET("/health", func(c *gin.Context) {
c.String(200, "ok")
})
r.Run()Expected behavior:
| Request | Response | Redirect |
|---|---|---|
GET /health |
200 OK | ❌ |
GET /health/ |
200 OK | ❌ |
Alternatives Considered
RedirectTrailingSlash(false)disables redirects but results in 404 for the alternate path.- Manually registering both
/pathand/path/. - Middleware to normalize paths.
Implementation Notes
This feature would require a small change in the router tree’s lookup logic (tree.go, node.go), particularly in:
findRoutegetValuefindCaseInsensitivePath
The idea is to optionally ignore the trailing slash difference when matching static nodes, for example:
if len(path) == len(prefix)-1 && prefix[len(prefix)-1] == '/' {
if e.ignoreTrailingSlash {
return matchedHandler
}
}References
Benefit
This feature provides flexibility, avoids unwanted redirects, and improves developer experience for APIs that treat /path and /path/ equally.