Redirect #
HTTP Redirect is a standard mechanism in the web protocol used to automatically direct client browsers (and search engines) from one URL to another. Unlike internal rewrites that hide route changes from users, redirects operate transparently by sending a special HTTP 3xx class response status and a Location header back to the client. The browser then responds to that instruction by making a new HTTP request to the new destination address, so the URL in the browser’s address bar changes. We’ll discuss in depth the redirect handshake mechanism, the semantic differences between status codes (301, 302, 307, 308), how to configure the redir directive in Caddy, canonical domain redirection optimization tactics (www vs non-www), mass URL migration methods (bulk redirects), and the significant impact of redirect handling on your web application’s search engine optimization (SEO) reputation.
Redirect Mechanics Basics and 3xx Status Codes #
When a web server instructs a redirection, the client browser must understand the nature of that redirect: whether the page moved temporarily, or has permanently migrated to a new location. The precision of this status code selection greatly affects browser caching behavior and how search engine robots like Googlebot index your site.
The HTTP protocol defines four main redirect status codes we most often use:
1. HTTP 301 Moved Permanently #
This status indicates the requested resource has been permanently moved to the new URL specified in the Location header. Client browsers aggressively cache this redirect instruction in their local storage. When users try accessing the old URL in the future, browsers locally redirect the route without contacting your server first.
- SEO Impact: The old URL’s ranking authority (PageRank) transfers almost entirely to the new URL.
2. HTTP 302 Found (Temporary Move - Legacy) #
This status indicates the requested resource is temporarily at a different URL. Client browsers are allowed to fetch content from the new location, but must not cache this redirect permanently.
- SEO Impact: Ranking authority stays on the old URL. Search engines keep crawling the old URL.
- Historical Weakness: The original HTTP 302 specification had an ambiguity where browsers often changed the request method from
POSTtoGETwhen redirecting, which could break form data payload transmission.
3. HTTP 307 Temporary Redirect (Modern Temporary Redirect) #
Introduced in the HTTP/1.1 specification to solve HTTP 302’s weakness. This status guarantees the client browser must preserve the original request method (like POST or PUT) along with the entire data payload when redirecting to the new URL.
- Scenario: Used for temporary redirects on form data submission paths or payment APIs.
4. HTTP 308 Permanent Redirect (Modern Permanent Redirect) #
This is the modern version of HTTP 301. This status indicates a permanent redirect with the full guarantee that the client browser must preserve the original request method (e.g., keep sending POST requests to the new endpoint without changing them to GET).
- Scenario: Permanent migration for data-writing API routes (write endpoints).
Let’s look at the communication flow between the browser and the Caddy server when processing a 301 Permanent Redirect response in the following sequence diagram:
sequenceDiagram
participant Browser as "Client Browser"
participant Caddy as "Caddy Web Server"
Note over Browser: The user accesses the old URL
Browser->>Caddy: 1. GET /very-old HTTP/1.1
Caddy-->>Browser: 2. HTTP/1.1 301 Moved Permanently<br/>Location: https://example.com/new<br/>Cache-Control: max-age=3600
Note over Browser: The browser stores this redirect info in the local cache
Note over Browser: The browser changes the URL in the address bar to /new
Browser->>Caddy: 3. GET /new HTTP/1.1 (New Connection)
Caddy-->>Browser: 4. HTTP/1.1 200 OK (Serve the Content)
rect rgb(245, 245, 245)
note over Browser, Caddy: Next Access by the Same User
Note over Browser: The browser detects /very-old is in the local cache
Note over Browser: Does an internal redirect without sending a request to Caddy
Browser->>Caddy: 5. GET /new HTTP/1.1
Caddy-->>Browser: 6. HTTP/1.1 200 OK
endConfiguring the redir Directive in Caddy
#
In Caddy, redirects are configured very easily using the redir directive. Its syntax is very clean, expressive, and doesn’t require complicated regex patterns for basic scenarios.
The basic syntax of the redir directive is:
redir [<matcher>] <to> [<status>]
<to>: The redirect destination URL. You can use Caddy dynamic placeholders like{path}to preserve the original route path, or{query}to include search query parameters.<status>: The HTTP redirect status code (optional). If not specified, Caddy by default uses302 Temporary Redirect.
Basic Usage Examples #
# Caddyfile example for basic route redirects
example.com {
# 1. Static route redirect (Using the default 302 status)
redir /contact-us /contact
# 2. Permanent redirect (Using the 301 status)
redir /old-documentation /docs 301
# 3. Dynamic redirect preserving the client path
# If the client requests: /downloads/v1/app.zip
# Caddy redirects to: https://storage.example.com/downloads/v1/app.zip
redir /downloads/* https://storage.example.com{path} 308
file_server
}
Redirect Patterns in Production Environments #
In industry-scale deployment scenarios, there are several mandatory redirect patterns you must apply to maintain security, data consistency, and your domain’s SEO reputation.
1. HTTP to HTTPS Redirect #
Caddy automatically enables the Automatic HTTPS feature, which includes automatic redirect creation from port 80 (HTTP) to port 443 (HTTPS) for every registered domain. However, if you want to create custom redirect rules — for example when Caddy runs behind an external load balancer handling SSL offloading — you can write them manually:
# Example: Manual HTTP to HTTPS Redirect Configuration
http://example.com {
# ✓ CORRECT: Redirect all requests to the HTTPS scheme on port 443
redir https://example.com{uri} 301
}
2. Domain Canonicalization (www vs non-www) #
Serving the same content at two different domain addresses (e.g., https://example.com and https://www.example.com) is a very bad practice for SEO. Search engines like Google consider it duplicate content that can lower your site’s search ranking. You must choose one as the primary domain (canonical domain) and permanently redirect the other.
Option A: Redirecting www to non-www (Recommended for New Domains) #
This pattern produces cleaner, shorter URLs in users’ address bars.
# 1. Server block for the non-canonical domain (www)
www.example.com {
# Redirect all requests permanently to the main domain
redir https://example.com{uri} 301
}
# 2. Main server block for the canonical domain
example.com {
root * /var/www/html
file_server
}
Option B: Redirecting non-www to www (Recommended if long-indexed) #
# 1. Server block for the non-canonical domain (non-www)
example.com {
redir https://www.example.com{uri} 301
}
# 2. Main server block for the canonical domain
www.example.com {
root * /var/www/html
file_server
}
3. Mass URL Structure Migration (Bulk Redirect) #
When you overhaul an old website to a new platform, your article paths may change massively. Writing hundreds of redir directives manually in the Caddyfile makes it very long and hard to read.
You can handle this mass migration very efficiently using the map directive as an internal lookup table:
# Example: Bulk Redirect Using a Map
example.com {
# Map old paths to new paths
map {path} {new_destination} {
/old-about /about
/old-services /services
/product-a.php /products/a
/category-archive /blog/categories
# Default value if the path isn't in the lookup list
default ""
}
# Run the redirect only if the new_destination variable isn't empty
@has_redirect expression {new_destination} != ""
redir @has_redirect {new_destination} 301
root * /var/www/html
file_server
}
Regex Redirects for Complex Dynamic Patterns #
If you need to move a large set of URLs with certain numbering patterns or dynamic parameters, a static lookup table isn’t enough. You need regular expressions (regex) to capture variables in the old URL and redistribute them in the new URL.
In Caddy, regex redirects are implemented efficiently by combining the path_regexp named matcher with the redir directive:
# Example: Redirecting Old Blog Article Patterns to New Ones with Regex
example.com {
# Old pattern: /archive/2026/06/16/article-title
# New pattern: /articles/article-title
@blog_pattern {
path_regexp archive_match ^/archive/\d{4}/\d{2}/\d{2}/(.+)$
}
# We use the first capturing group {re.archive_match.1}
# to dynamically extract the article title slug
redir @blog_pattern /articles/{re.archive_match.1} 301
root * /var/www/html
file_server
}
Using the static_response Handler for Custom Redirects
#
Although the redir directive is the standard, highly recommended way for its ease of use, Caddy also provides a lower-level writing method using the static_response handler.
Why use static_response? When using redir, Caddy automatically composes a minimal standard HTML body to inform browsers that don’t support automatic redirects. However, with static_response, you have absolute control to insert your own custom response body (e.g., custom JSON text or a beautiful interactive HTML document) along with additional custom response headers:
# Example: Redirect with Custom Headers & JSON payload
api.example.com {
# The old route block that's no longer active
handle /v1/legacy/* {
static_response {
# Set the permanent redirect status
status 301
# Write the destination Location header
header Location "https://api.example.com/v2/new-endpoint"
# Extra custom header for analytics tracking
header X-Migration-Triggered "v1-to-v2"
# Optional response payload if the client is a non-browser API client
body `{"error": "Endpoint has migrated permanently. Please point to /v2/new-endpoint."}`
}
}
}
Browser-Language-Based Redirects (Accept-Language Redirect) #
In internationalization scenarios (i18n), you often want to automatically direct users to the appropriate language subdirectory when they first access the root route /. You can detect users’ language preferences by reading the HTTP Accept-Language request header sent by their browsers:
# Example: Language Automation Based on Browser Headers
example.com {
# Matcher for detecting Indonesian language
@lang_id {
path /
header Accept-Language *id*
}
# Matcher for detecting English language (default)
@lang_en {
path /
header Accept-Language *en*
}
# Run dynamic redirects to the appropriate language sub-folder
redir @lang_id /id/ 302
redir @lang_en /en/ 302
# Default fallback if the language preference isn't detected
redir / /en/ 302
# Routes for serving files
handle /id/* {
root * /var/www/html/id
file_server
}
handle /en/* {
root * /var/www/html/en
file_server
}
}
DNS Redirect vs HTTP Redirect Comparison #
There’s a common misconception among beginner administrators that DNS-level CNAME records can replace HTTP redirect functionality.
DNS CNAME (Domain Name System):
Directs domain A to domain B's IP record directly at the name server level.
✗ DON'T use this if you want to change the URL in the address bar or do SEO 301s.
✗ DNS has no concept of the HTTP protocol (can't send a 301 status or Location header).
HTTP Redirect (Caddy):
Runs at the application protocol level (OSI Layer 7).
✓ Can respond with custom Location headers, 3xx statuses, and preserve query strings.
✓ Must be used for SEO handling and browser address bar usability.
Therefore, when you want to redirect the root domain (Apex Domain) to the www subdomain, you must not rely only on DNS records. You must point the root domain’s A/AAAA records to your Caddy server’s IP address, and let Caddy officially send the HTTP 301 response to client browsers.
Redirect Impact on SEO and Browser Caching #
Careless redirect configuration can destroy your site’s organic search traffic in an instant. Therefore, you must understand the practical implications of browser caching and redirect loops.
1. The Danger of Permanent 301 Status Caching #
As explained earlier, client browsers store 301 Moved Permanently response caches locally on their disks.
[!CAUTION] Don’t use the 301 status if you’re not sure the route is permanent. If you accidentally set
301for a weekly promotion route that should be temporary (302), your customers’ browsers cache that route locally. Even if you remove thatredirline from the Caddyfile and reload the server, your customers’ browsers won’t contact your server and keep redirecting the route on their own. The only way to fix this is asking customers to manually clear their browser cache, which is obviously very impossible at production application scale.
- Practical Solution: During the initial migration phase or local development, always use the
302 Foundor307 Temporary Redirectstatus first. After the system proves stable in staging and production for a few days, only then change the status to301or308for SEO PageRank transfer optimization.
2. Avoiding Redirect Loops #
A redirect loop happens when URL A redirects to URL B, and URL B redirects back to URL A, creating an endless circle that eventually gets blocked by the client browser with a "Too many redirects" error message.
// DON'T do this circular configuration in the Caddyfile
example.com {
# Route A redirects to B
redir /page-a /page-b 301
# Route B redirects back to A
redir /page-b /page-a 301
}
To detect redirect loops at the canonical domain level, you can rely on the following routing logic flowchart when composing domain redirect routes:
flowchart TD
A["1. Request Arrives on Port 443"] --> B{"2. Is the Host = canonical domain?\n(e.g., example.com)"}
B -- "Yes" --> C{"3. Is the Path = an old route?\n(e.g., /old-path)"}
B -- "No" --> D["4. Redirect to the Canonical Domain\n(redir https://example.com{uri} 301)"]
C -- "Yes" --> E["5. Redirect to the New Route\n(redir /new-path 301)"]
C -- "No" --> F["6. Serve the Application Content\n(200 OK)"]
D --> G["Cycle Complete (Client Browser Moves URL)"]
E --> G
F --> GBy following the flowchart above, you ensure the canonical domain authenticity evaluation process is resolved at the outermost layer before internal route evaluation begins, preventing rule collisions that trigger redirect loops.
Summary #
- Status Distinction: Use
301or308for permanent migrations to transfer SEO authority. Use302or307for temporary redirects to avoid the danger of permanent local caching in client browsers.- Data Payload Preservation: Use the
307(temporary) and308(permanent) statuses on form/API routes so browsers don’t changePOSTrequest methods toGETduring redirects.- Canonical Domain: Apply explicit server block separation to redirect www to non-www (or vice versa) for SEO ranking consistency on Google.
- Bulk Redirect Pattern: Use the
mapdirective as a lookup table to simplify hundreds of old URL migration release lists without dirtying the Caddyfile.- Regex Redirection: Combine
path_regexpwith theredirdirective to extract and rearrange dynamic URL structures using capturing group variables ({re.match.1}).- Caching Mitigation: Always test redirects using the
302status during local development before applying them as301in production environments.