Gin Route Param Parsing Failing When URL Contains Special Characters or Slashes
Quick answer
A Gin route parameter that appears truncated or missing entirely when its value contains a slash almost always comes down to how Gin's router matches path...
A Gin route parameter that appears truncated or missing entirely when its value contains a slash almost always comes down to how Gin's router matches path segments — a standard named parameter (:param) only captures a single path segment, and a literal slash inside the value is interpreted as a segment boundary, not as part of the parameter's value.
The Problem
A route parameter expected to capture a value containing a slash (like a file path, or a URL-encoded value that wasn't actually encoded) doesn't work as expected:
router.GET("/files/:filename", getFile)
// Request to /files/documents/report.pdf
// c.Param("filename") returns "documents", not "documents/report.pdf"
// and the request actually 404s, since "/report.pdf" doesn't match any route
Why It Happens
Gin's standard named parameter syntax (:name) is specifically designed to match exactly one path segment — it stops at the next literal / in the URL, since that's how Gin's router distinguishes one path segment from the next when matching against the route tree. Common causes of running into this:
- Expecting a route parameter to naturally capture a value that itself contains slashes (a file path, a nested resource identifier) without realizing the standard parameter syntax doesn't support this
- A client sending a value that should have been URL-encoded (with
/encoded as%2F) but wasn't, resulting in a literal slash reaching the server and being interpreted as a path segment boundary rather than part of the intended parameter value - Special characters other than slashes (like
?,#, or certain Unicode characters) causing unexpected behavior if the client isn't correctly URL-encoding the value before it's included in the request path at all
The Fix
If you genuinely need to capture a path segment that includes slashes, use Gin's wildcard parameter syntax (*name) instead of the standard named parameter, which captures everything remaining in the path, including any further slashes:
router.GET("/files/*filepath", getFile)
func getFile(c *gin.Context) {
filepath := c.Param("filepath")
// for a request to /files/documents/report.pdf,
// filepath is "/documents/report.pdf" (note the leading slash Gin includes)
}
Note the wildcard parameter's captured value includes a leading slash, which is a detail worth trimming or accounting for explicitly in your handler code, since it differs slightly from how a standard named parameter's value is returned without any such prefix.
If the value genuinely should be treated as a single, opaque identifier (not a real nested path) but happens to contain characters like slashes, ensure the client properly URL-encodes it before sending, and correctly decode it server-side if needed:
// Client-side: properly encode before including in the URL
const encoded = encodeURIComponent("some/value/with/slashes");
fetch(`/api/items/${encoded}`);
// Server-side: Gin automatically URL-decodes path parameters by default,
// so a correctly encoded value arrives already decoded in c.Param()
func getItem(c *gin.Context) {
id := c.Param("id") // already decoded, if the client encoded it correctly
}
If you're unsure whether a client is correctly encoding values, add logging in your handler to inspect exactly what value Gin actually parsed, which quickly reveals whether the issue is client-side encoding or server-side route configuration:
func getItem(c *gin.Context) {
log.Printf("raw param value: %q", c.Param("id"))
}
For query parameters (rather than path parameters) containing special characters, ensure they're passed as proper query string values rather than embedded directly in the path, since query parameters handle a broader range of characters more naturally through standard URL encoding than trying to fit arbitrary values into path segments:
router.GET("/search", searchHandler)
// Request: /search?query=some%2Fvalue%2Fwith%2Fslashes
func searchHandler(c *gin.Context) {
query := c.Query("query") // Gin handles the URL decoding automatically
}
Still Not Working?
If switching to a wildcard parameter causes routing conflicts with other more specific routes under the same path prefix, review Gin's rules around wildcard and static route conflicts (covered in more detail in a related routing-conflict topic) — a wildcard route consumes everything below its mount point, which can conflict with other, more specific routes registered under that same prefix if they're not structured carefully to avoid ambiguity between the wildcard's broad match and other routes' narrower, more specific matches.