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
/foo and /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 = true
When enabled:
/health and /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.Path should 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
/path and /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:
findRoute
getValue
findCaseInsensitivePath
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
- Related Gin issues: #388, #2415
- Similar feature in Echo:
Echo#TrailingSlashRedirect
Benefit
This feature provides flexibility, avoids unwanted redirects, and improves developer experience for APIs that treat /path and /path/ equally.
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
/healthand/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
/healthis 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:
/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(orRouterGroup) option to enable this behavior, for example:or via a configuration flag:
When enabled:
/healthand/health/both match the same route, without redirecting.Request.URL.Pathshould remain unchanged.Example
Expected behavior:
GET /healthGET /health/Alternatives Considered
RedirectTrailingSlash(false)disables redirects but results in 404 for the alternate path./pathand/path/.Implementation Notes
This feature would require a small change in the router tree’s lookup logic (
tree.go,node.go), particularly in:findRoutegetValuefindCaseInsensitivePathThe idea is to optionally ignore the trailing slash difference when matching static nodes, for example:
References
Echo#TrailingSlashRedirectBenefit
This feature provides flexibility, avoids unwanted redirects, and improves developer experience for APIs that treat
/pathand/path/equally.