The Redirect plugin provides URL redirection with regex pattern matching and configurable HTTP status codes. It enables URL rewriting, legacy URL support, and conditional redirects based on request characteristics.
URL redirection is a common requirement for web applications - whether for SEO-friendly URLs, maintaining backward compatibility, or routing traffic between different services. This plugin uses regular expressions for flexible pattern matching and supports all standard HTTP redirect status codes. It can handle simple redirects, complex URL rewriting with capture groups, and conditional redirects based on protocol or other factors.
The redirect plugin is configured as part of the plugin pipeline in your host configuration:
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<meta itemprop="redirect_rule_1_pattern" content="^/old-page$">
<meta itemprop="redirect_rule_1_replacement" content="/new-page">
<meta itemprop="redirect_rule_1_status" content="301">
</li>
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
default_status_code |
Integer | No | 302 | Default redirect status code for rules without explicit status |
redirect_rule_N_pattern |
String (Regex) | Yes* | - | Regular expression pattern to match URLs |
redirect_rule_N_replacement |
String | Yes* | - | Replacement URL (supports $1, $2 capture groups) |
redirect_rule_N_status |
Integer | No | default_status_code | HTTP status code for this rule |
redirect_rule_N_condition |
String | No | - | Comma-separated conditions: "https_only", "http_only" |
* At least one redirect rule is required; N is a number starting from 1
Code | Name | Method Preservation | Cacheable | Use Case |
---|---|---|---|---|
301 | Moved Permanently | No (GET/HEAD only) | Yes | Permanent URL changes, SEO |
302 | Found | No (GET/HEAD only) | No | Temporary redirects |
303 | See Other | No (always GET) | No | POST-redirect-GET pattern |
307 | Temporary Redirect | Yes | No | Temporary, preserve method |
308 | Permanent Redirect | Yes | Yes | Permanent, preserve method |
Typical pipeline order:
1. cors.so → CORS headers
2. redirect.so → URL redirects ✓
3. basic-auth.so → Authentication
4. file-handler.so → Content serving
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<meta itemprop="redirect_rule_1_pattern" content="^/about-us\.html$">
<meta itemprop="redirect_rule_1_replacement" content="/about">
<meta itemprop="redirect_rule_1_status" content="301">
</li>
<!-- Redirect /products/123 to /shop/item/123 -->
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<meta itemprop="redirect_rule_1_pattern" content="^/products/(\d+)$">
<meta itemprop="redirect_rule_1_replacement" content="/shop/item/$1">
<meta itemprop="redirect_rule_1_status" content="301">
</li>
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<!-- Old blog URLs to new structure -->
<meta itemprop="redirect_rule_1_pattern" content="^/blog/(\d{4})/(\d{2})/(.+)$">
<meta itemprop="redirect_rule_1_replacement" content="/posts/$1-$2/$3">
<meta itemprop="redirect_rule_1_status" content="301">
<!-- Legacy API endpoints -->
<meta itemprop="redirect_rule_2_pattern" content="^/api/v1/(.*)$">
<meta itemprop="redirect_rule_2_replacement" content="/api/v2/$1">
<meta itemprop="redirect_rule_2_status" content="302">
<!-- Remove trailing slashes -->
<meta itemprop="redirect_rule_3_pattern" content="^(.+)/$">
<meta itemprop="redirect_rule_3_replacement" content="$1">
<meta itemprop="redirect_rule_3_status" content="301">
</li>
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<!-- Redirect all HTTP to HTTPS -->
<meta itemprop="redirect_rule_1_pattern" content="^(.*)$">
<meta itemprop="redirect_rule_1_replacement" content="https://example.com$1">
<meta itemprop="redirect_rule_1_status" content="301">
<meta itemprop="redirect_rule_1_condition" content="http_only">
</li>
<!-- Redirect old domain to new domain preserving paths -->
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/redirect.so</span>
<meta itemprop="redirect_rule_1_pattern" content="^(.*)$">
<meta itemprop="redirect_rule_1_replacement" content="https://newdomain.com$1">
<meta itemprop="redirect_rule_1_status" content="301">
</li>
Pattern | Description | Example Match |
---|---|---|
^/old-page$ |
Exact match | /old-page |
^/blog/.* |
Prefix match | /blog/anything |
^/item/(\d+)$ |
Numeric ID capture | /item/123 → $1=123 |
^/(.+)\.html$ |
Extension removal | /page.html → $1=page |
^/(.*)/index\.html$ |
Index file removal | /dir/index.html → $1=dir |
Use parentheses to capture parts of the URL for use in replacements:
# Pattern: ^/user/(\w+)/post/(\d+)$
# URL: /user/alice/post/42
# Captures: $1=alice, $2=42
# Replacement: /u/$1/p/$2
# Result: /u/alice/p/42
# Test redirect without following
curl -I http://localhost:3000/old-page
# Test redirect and follow
curl -L http://localhost:3000/old-page
# Verbose output to see redirect chain
curl -v -L http://localhost:3000/old-page 2>&1 | grep -E "(< HTTP|< Location)"
Use browser developer tools:
#!/bin/bash
# test-redirects.sh
declare -A redirects=(
["/old-page"]="/new-page"
["/products/123"]="/shop/item/123"
["/blog/2024/01/hello"]="/posts/2024-01/hello"
)
for old_url in "${!redirects[@]}"; do
expected="${redirects[$old_url]}"
# Get redirect location
location=$(curl -s -I "http://localhost:3000$old_url" | grep -i "location:" | awk '{print $2}' | tr -d '\r')
if [[ "$location" == "$expected" ]]; then
echo "✓ $old_url → $expected"
else
echo "✗ $old_url → $location (expected: $expected)"
fi
done
Issue | Cause | Solution |
---|---|---|
Redirect not working | Pattern doesn't match | Test regex pattern, check for typos |
Infinite redirect loop | Rule redirects to itself | Check replacement doesn't match pattern |
Wrong destination | Capture group mismatch | Verify $1, $2 match parentheses in pattern |
HTTPS condition ignored | Proxy not sending headers | Check X-Forwarded-Proto header |
Rules not loading | Configuration syntax | Check rule numbering is sequential |
Run the server with -v
flag to see redirect matches:
./rusty-beam -v config.html
[Redirect] /old-page → /new-page (301)
<!-- Preserve query strings -->
<meta itemprop="redirect_rule_1_pattern" content="^/search$">
<meta itemprop="redirect_rule_1_replacement" content="/find">
<!-- Query string is automatically preserved -->
<!-- Redirect uppercase URLs to lowercase -->
<!-- Note: Regex is case-sensitive by default -->
<meta itemprop="redirect_rule_1_pattern" content="^/About$">
<meta itemprop="redirect_rule_1_replacement" content="/about">
<!-- Restructure date-based URLs -->
<meta itemprop="redirect_rule_1_pattern" content="^/(\d{4})-(\d{2})-(\d{2})/(.+)$">
<meta itemprop="redirect_rule_1_replacement" content="/archive/$1/$2/$3/$4">
<!-- /2024-01-15/post → /archive/2024/01/15/post -->
Redirects happen before authorization, so you can redirect to login pages for protected resources.
Use redirects to create clean URLs that map to actual files with extensions.
Both the redirect response (301/302) and the final destination request are logged.