Documentation
¶
Overview ¶
Package rewrite provides URL rewrite middleware for celeris using regular expression pattern matching.
The middleware matches the request path against a set of regex rules and either rewrites the path in-place (silent rewrite) or sends an HTTP redirect response.
Basic usage (silent rewrite -- use anchored patterns):
server.Pre(rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: "^/old$", Replacement: "/new"},
},
}))
Redirect mode:
server.Pre(rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: "^/old$", Replacement: "/new"},
},
RedirectCode: 301,
}))
Regex and Capture Groups ¶
Rule patterns are Go regular expressions compiled with regexp.MustCompile. The replacement string supports capture group substitution ($1, $2, ...) using regexp.Regexp.ReplaceAllString semantics:
server.Pre(rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: `/users/(\d+)/posts`, Replacement: "/api/v2/users/$1/posts"},
},
}))
First-Match-Wins ¶
Rules are evaluated in the order provided. The first matching regex wins and subsequent rules are not checked:
Rules: []rewrite.Rule{
{Pattern: "/a/.*", Replacement: "/alpha"}, // checked first
{Pattern: "/b/.*", Replacement: "/beta"}, // checked second
}
Silent Rewrite vs Redirect ¶
When RedirectCode is 0 (default), the middleware calls celeris.Context.SetPath to modify the request path in-place. The client URL remains unchanged and downstream handlers see the rewritten path.
When RedirectCode is a valid redirect status (301, 302, 303, 307, 308), the middleware sends an HTTP redirect response. The redirect URL preserves the original scheme, host, and query string. Downstream handlers are not executed.
Pre-Routing Middleware ¶
This middleware is designed to run via celeris.Server.Pre so that URL rewriting occurs before route lookup:
server.Pre(rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: "/old", Replacement: "/new"},
},
}))
Query String Preservation ¶
In redirect mode, the original query string is appended to the redirect URL. In silent rewrite mode, the query string is unmodified since only the path is rewritten.
Conditional Rewriting ¶
Rules can be restricted to specific HTTP methods or hosts:
server.Pre(rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{
Pattern: "^/api/v1/(.*)$",
Replacement: "/api/v2/$1",
Methods: []string{"GET", "HEAD"},
},
{
Pattern: "^/admin/(.*)$",
Replacement: "/internal/$1",
Host: "admin.example.com",
},
},
}))
When Methods is empty, the rule matches all methods. When Host is empty, the rule matches all hosts.
Init-Time Validation ¶
New panics if Rules is empty, if RedirectCode is non-zero and not a valid redirect status, or if any rule pattern is an invalid regex (via regexp.MustCompile).
Security ¶
Regex patterns are compiled once at init time via regexp.MustCompile, not per-request. Avoid catastrophic backtracking patterns (e.g., `(a+)+$`) which can cause high CPU during compilation.
In redirect mode, the redirect URL is constructed from celeris.Context.Host and celeris.Context.Scheme, which are derived from request headers. Without a reverse proxy that validates the Host header, an attacker can send a crafted Host to produce an open redirect. Ensure your deployment validates the Host header upstream (e.g., via the proxy middleware) or use silent rewrite mode (RedirectCode: 0) which only modifies the internal path.
Skipping ¶
Use Config.Skip for dynamic skip logic or Config.SkipPaths for exact-match path exclusions. Skipped requests call c.Next() without any rewrite.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(config ...Config) celeris.HandlerFunc
New creates a rewrite middleware with the given config. Rules are compiled into regular expressions at init time and evaluated in the order provided. The first matching regex wins and subsequent rules are not checked.
Panics if Rules is empty or contains an invalid regex pattern (via regexp.MustCompile).
Example ¶
package main
import (
"github.com/goceleris/celeris/middleware/rewrite"
)
func main() {
// Silent rewrite: /old is rewritten to /new before route lookup.
// s := celeris.New()
// s.Pre(rewrite.New(rewrite.Config{...}))
_ = rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: "^/old$", Replacement: "/new"},
},
})
}
Output:
Example (CaptureGroups) ¶
package main
import (
"github.com/goceleris/celeris/middleware/rewrite"
)
func main() {
// Capture groups: extract user ID and rewrite to an API path.
_ = rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: `^/users/(\d+)/posts$`, Replacement: "/api/v2/users/$1/posts"},
},
})
}
Output:
Example (Conditional) ¶
package main
import (
"github.com/goceleris/celeris/middleware/rewrite"
)
func main() {
// Method-restricted rewriting: only rewrite GET and HEAD requests.
_ = rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{
Pattern: "^/api/v1/(.*)$",
Replacement: "/api/v2/$1",
Methods: []string{"GET", "HEAD"},
},
},
})
}
Output:
Example (Redirect) ¶
package main
import (
"github.com/goceleris/celeris/middleware/rewrite"
)
func main() {
// Redirect mode: send a 301 redirect instead of a silent rewrite.
_ = rewrite.New(rewrite.Config{
Rules: []rewrite.Rule{
{Pattern: "^/old$", Replacement: "/new"},
},
RedirectCode: 301,
})
}
Output:
Types ¶
type Config ¶
type Config struct {
// Skip defines a function to skip this middleware for certain requests.
Skip func(c *celeris.Context) bool
// SkipPaths lists paths to skip (exact match).
SkipPaths []string
// Rules defines the rewrite rules. First match wins.
// Rules are evaluated in the order provided (not sorted).
Rules []Rule
// RedirectCode controls the rewrite behavior:
// - 0 (default): silent rewrite via SetPath (path is modified in-place)
// - 301, 302, 303, 307, 308: sends an HTTP redirect response
RedirectCode int
}
Config defines the rewrite middleware configuration.
type Rule ¶
type Rule struct {
// Pattern is a Go regular expression matched against the request path.
// Use ^ and $ anchors for exact path matching.
Pattern string
// Replacement is the replacement string with capture group support ($1, $2).
Replacement string
// RedirectCode overrides [Config].RedirectCode for this rule.
// When non-zero, this rule sends an HTTP redirect instead of a silent rewrite.
// When zero (default), the config-level RedirectCode is used.
RedirectCode int
// Methods restricts this rule to specific HTTP methods.
// When empty, the rule matches all methods.
// Example: []string{"GET", "HEAD"} — only rewrite GET and HEAD requests.
Methods []string
// Host restricts this rule to a specific Host header value.
// When empty, the rule matches all hosts. Supports exact match only.
Host string
}
Rule defines a single rewrite rule.