rewrite

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

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"},
		},
	})
}
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"},
		},
	})
}
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"},
			},
		},
	})
}
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,
	})
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL