Slashing Through the Safety Nets: The go-chi Open Redirect
Jan 15, 2026·6 min read
Executive Summary (TL;DR)
The `RedirectSlashes` middleware in go-chi/chi attempted to clean up URLs by removing trailing slashes but failed to account for backslashes. By sending a request like `/ arget.com/`, attackers can trick the server into issuing a redirect to `/ arget.com`. Most modern browsers interpret this as a protocol-relative URL (`//target.com`), redirecting the victim to an external malicious domain.
A logic error in the popular Go router 'chi' middleware allows attackers to bypass open redirect protections using backslashes.
The Hook: Cleanliness is Next to... Vulnerability?
In the world of Go web development, go-chi/chi is a legend. It is lightweight, idiomatic, and doesn't get in your way. One of its creature comforts is the RedirectSlashes middleware. Its job is simple: SEO hygiene. If a user requests /dashboard/ but your route is defined as /dashboard, the middleware kindly 301 redirects them to the clean path. It keeps your analytics tidy and your users happy.
But here is the thing about writing sanitation code: if you miss a spot, that spot becomes a foothold for attackers. The developers of chi focused heavily on the forward slash (/)—the standard delimiter for HTTP paths. They scrubbed it diligently.
However, they forgot about the forward slash's twisted cousin: the backslash (\). While the HTTP RFCs are pretty clear about slashes, browsers are notoriously forgiving. They try to be helpful. And in this case, that helpfulness, combined with a blind spot in chi, allows us to turn a trusted API endpoint into a launchpad for phishing campaigns.
The Flaw: A Tale of Two Slashes
The vulnerability lies in how we define a "clean" path. The vulnerable code used Go's strings.Trim(path, "/"). This function is obedient. It looks at the start and end of a string and eats every forward slash it finds until it hits a non-slash character.
Here is the logic flaw: strings.Trim is explicitly targeting /. It does not touch \. So, what happens if we send a request like /%5Cevil.com/ (decoded: /\evil.com/)?
- The middleware sees the trailing
/and decides to "clean" the path. - It runs
Trimon/\evil.com/. Trimeats the leading/and the trailing/.Trimstops at\because it isn't in the cut-set.- The result is
\evil.com.
The middleware then helpfully prepends a slash to ensure the path is absolute relative to the root, resulting in Location: /\evil.com.
This is where the browser betrays us. To a web server, /\ is just a filename. But to Chrome and Firefox, /\ or \\ at the start of a URL is treated as a protocol-relative URL (equivalent to //evil.com). The browser interprets this as "keep the current scheme (http/https) but switch the host to evil.com."
The Code: The Smoking Gun
Let's look at the code responsible for this mess. This comes from middleware/strip.go. It’s a classic example of "filtering bad characters" instead of "normalizing input."
The Vulnerable Code:
// ... inside RedirectSlashes ...
if len(path) > 1 && path[len(path)-1] == '/' {
// Trim all leading and trailing slashes (e.g., "//evil.com", "/some/path//")
// FLAW: This only strips forward slashes!
path = "/" + strings.Trim(path, "/")
// ... redirect logic ...
http.Redirect(w, r, path, 301)
}If we feed this \evil.com/, the strings.Trim returns \evil.com. The code adds a slash, making it /\evil.com.
The Fix:
The maintainers patched this by forcing normalization before trimming. They now convert all backslashes to forward slashes. This effectively neuters the protocol-relative bypass because // gets trimmed down to a single / by the subsequent logic.
// ... patched version ...
if len(path) > 1 && path[len(path)-1] == '/' {
// FIX: Normalize backslashes to forward slashes first
path = strings.ReplaceAll(path, `\`, `/`)
// Now trim. "//evil.com" becomes "evil.com", prepended with "/" -> "/evil.com"
path = "/" + strings.Trim(path, "/")
// ... redirect logic ...
}By flattening the attack surface (converting \ to /), the existing logic correctly handles the input.
The Exploit: Bouncing Users Off the Walls
Exploiting this is trivially easy and requires no authentication. You just need a target running the vulnerable middleware and a browser that respects the /\ quirk (Chrome is a prime candidate).
The Attack Chain:
- Recon: Verify the target uses
go-chi. A simple 404 page or server headers might give it away, or you might just spray-and-pray standardchibehavior. - Crafting the Link: You create a URL:
https://victim-app.com/%5Cattacker.com/. - The Click: A user clicks the link. The browser sends the request.
- The Response: The
chimiddleware sees the trailing slash, trims the path, and responds:HTTP/1.1 301 Moved Permanently Location: /\attacker.com - The Execution: The browser receives the 301. It parses
/\attacker.com. Instead of going tohttps://victim-app.com/\attacker.com(a valid file path), it treats it as//attacker.comand navigates the user tohttps://attacker.com.
At this point, the user is on your domain. You can serve a fake login page, prompt a file download, or run browser exploits. The address bar changed, but the initial trust was established by the link pointing to victim-app.com.
The Impact: Why Should We Care?
Security engineers often roll their eyes at Open Redirects. "It's just a redirect, they can't execute code!" But this dismissal ignores the human element of security. Phishing relies on trust. If I send you a link to shady-site.ru, you won't click it. If I send you a link to your-bank.com/login/..., you probably will.
This vulnerability allows an attacker to borrow the reputation of the vulnerable domain. It defeats the most basic anti-phishing training users receive: "Check the URL domain before clicking."
Furthermore, in complex OAuth flows or SSO implementations, open redirects can sometimes be chained to leak authorization codes or access tokens. If an OAuth provider allows a wildcard redirect URI or if the vulnerable app is itself an identity provider, this "low severity" bug can quickly become a relentless account takeover mechanism.
The Mitigation: Patching the Hole
The remediation is straightforward: Update. The fix was pushed in mid-January 2026. You need to pull the latest version of github.com/go-chi/chi (specifically version v5.2.2 containing the fix commit 6eb3588).
If you cannot update immediately, you have two options:
- Disable the Middleware: Remove
r.Use(middleware.RedirectSlashes)from your router configuration. This might break some SEO links, but it stops the bleeding. - Custom Wrapper: Write your own middleware that wraps the request path normalization. Before passing the request to
RedirectSlashes, ensure the path contains no backslashes, or simply implement your own slash-stripper that correctly handlesstrings.ReplaceAll(path, "\\", "/").
[!NOTE] Always verify your WAF rules. A generic rule blocking
%5C(encoded backslash) in the URL path can provide a temporary shield while you deploy the code fix.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
github.com/go-chi/chi go-chi | >= 5.2.2, < 5.2.3 | v5.2.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-601 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 4.7 (Medium) |
| Complexity | Low (AC:L) |
| Privileges | None (PR:N) |
| User Interaction | Required (UI:R) |
| Patch Status | Available |
MITRE ATT&CK Mapping
URL Redirection to Untrusted Site ('Open Redirect')
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.