Redirects, Rewrites, and Edge Policy

LESSON

HTTP Protocol and Content Delivery

022 25 min intermediate

Redirects, Rewrites, and Edge Policy

The core idea: redirects, rewrites, and edge rules are HTTP behavior, not just deployment configuration: each rule changes what the client sees, what the origin receives, or which policy wins before application code runs.

Core Insight

Imagine a checkout incident after a "simple" canonical-domain cleanup. The team wants every request for http://shop.test to become https://www.shop.test. Static pages work. Product pages work. Checkout conversion drops. A few support tickets show URLs like:

http://shop.test/checkout?cart=A123&coupon=SUMMER

The edge redirects them to:

https://www.shop.test/checkout

The canonical host is correct, but the query string vanished. The cart id and coupon were carried in the URL. The application never had a chance to fix it because the edge sent the browser somewhere else before the request reached the origin.

That is the key mental model. A redirect tells the client to make a new request. A rewrite changes where the edge sends the request internally while the browser URL may stay the same. Edge policy is the ordered set of rules that decides whether to redirect, rewrite, add headers, bypass cache, rate-limit, or choose an origin. These rules are part of the externally visible HTTP contract because clients, caches, crawlers, browsers, and applications observe their consequences.

The trade-off is central policy control versus surprising client-visible behavior. Edge rules are powerful because one configuration can enforce HTTPS, canonical hosts, path normalization, cache bypass, security headers, and regional routing. They are risky because rule order, status codes, method preservation, query handling, and caching can turn a small configuration change into a production behavior change.

Redirects Tell the Client to Move

A redirect is an HTTP response with a 3xx status code and usually a Location header. The client receives the response and decides to make a new request to the target location:

HTTP/1.1 308 Permanent Redirect
Location: https://www.shop.test/checkout?cart=A123&coupon=SUMMER

From the client's point of view, the URL changed. That means redirects are visible to browsers, mobile apps, bots, caches, analytics, and users. A redirect can cross hosts, schemes, and paths. It can also accidentally remove query parameters, fragments, or path segments if the rule builds the target incorrectly.

Status code choice matters. 301 and 308 mean permanent redirect. 302 and 307 mean temporary redirect. The difference between 301/302 and 307/308 is especially important for non-GET requests. Historically, many clients changed a redirected POST into a GET for 301 or 302. 307 and 308 preserve method and body semantics. If a payment or login flow redirects a POST, method preservation is not trivia.

Use a simple rule of thumb:

GET page moved permanently       -> 301 or 308, with cache implications understood
temporary experiment or routing  -> 302 or 307
preserve method and request body -> 307 or 308

Permanent redirects can be cached by browsers and intermediaries. That is useful for stable canonical host or HTTP-to-HTTPS migrations. It is dangerous for experiments, temporary maintenance, or rules that might be rolled back quickly. A bad permanent redirect can keep affecting users after the edge config is fixed.

Redirects also compose. A user can hit http://shop.test/products/42, get redirected to HTTPS, then redirected to www, then redirected again for a trailing slash. Each hop adds latency and another chance to lose method, query, headers, or context. A better edge policy usually computes the final canonical URL once.

Rewrites Change the Internal Path

A rewrite is different. The client asks for one URL, but the edge sends the request to a different internal path or origin. The browser does not necessarily see the internal mapping.

For example:

client URL:  https://www.shop.test/api/orders/A123
edge rewrite: /api/orders/A123 -> /v2/orders/A123
origin sees:  /v2/orders/A123
browser sees: https://www.shop.test/api/orders/A123

This is useful for migrations, routing, multi-origin deployments, and hiding internal service layouts. It lets the public URL stay stable while backend ownership changes.

The risk is that the application may need to know both identities: the public URL the client used and the internal route selected by the edge. Logs, traces, authorization checks, cache keys, and error messages can become confusing if only one side is visible. A rewrite can also create a mismatch between what the client thinks it requested and what the origin code assumes about path prefixes, base URLs, or tenant boundaries.

Rewrites should answer:

What public URL did the client request?
What internal route or origin did the edge choose?
Which headers preserve the original host, path, and scheme?
Which logs show the rewrite decision?

Rewrites are often safer than redirects for internal migrations because they do not force clients to learn a new URL. They are worse when the client really should update its bookmark, link, or API target. The distinction is not cosmetic. Redirect means "client, ask somewhere else." Rewrite means "edge, deliver this public request through a different internal route."

Edge Policy Is Ordered Behavior

Most edge systems evaluate rules in some order. A simplified request path might be:

1. terminate TLS
2. normalize host and scheme
3. apply security and bot rules
4. apply redirects
5. compute cache key or bypass cache
6. apply rewrites and origin selection
7. add forwarding headers
8. send to origin

Real products differ, but the lesson is stable: policy order decides behavior. A cache rule before a redirect rule can produce a different result than a redirect before a cache rule. A rewrite before authorization can expose an internal route if authorization expects the public path. A rate-limit rule before host canonicalization might count shop.test and www.shop.test separately.

Rules also need matching boundaries. A path prefix match such as /api also matches /apiary unless the pattern is precise. A redirect rule that captures (.*) may accidentally include an empty path, double slash, or encoded character. A query-preserving rule may need to preserve only some parameters, because tracking parameters might be removed but checkout parameters must survive.

The safest edge policies are explicit about:

match condition: host, scheme, path, method, headers, query, country, route
action: redirect, rewrite, bypass cache, add header, block, rate-limit
status: exact redirect code if redirecting
preservation: method, query, path encoding, original host/path headers
order: which rules run before and after
observability: rule id in logs

Treat a rule id like code. If a user reports a bad redirect, you want logs that say which rule fired, not just a final status code.

Worked Path: The Checkout Query Incident

The broken rule says:

if host == "shop.test":
  redirect to "https://www.shop.test" + path
  status = 301

A user requests:

GET /checkout?cart=A123&coupon=SUMMER HTTP/1.1
Host: shop.test

The edge evaluates:

host matches shop.test
target scheme = https
target host = www.shop.test
target path = /checkout
target query = omitted
status = 301

The browser follows:

GET /checkout HTTP/1.1
Host: www.shop.test

The cart is missing. Analytics show a normal redirect. Application logs show a checkout request without a cart id. Nobody sees an exception because the policy did exactly what it was configured to do.

A corrected rule is more explicit:

if host == "shop.test" and method in ["GET", "HEAD"]:
  redirect to "https://www.shop.test" + path + original_query
  status = 308
  rule_id = "canonical-host-preserve-query-v2"

Now the trace is:

input:  http://shop.test/checkout?cart=A123&coupon=SUMMER
rule:   canonical-host-preserve-query-v2
output: https://www.shop.test/checkout?cart=A123&coupon=SUMMER
status: 308

For a POST endpoint, the team might avoid redirecting the submission path entirely, or use 307/308 only after testing clients. For a marketing page, 301 might be fine. For a temporary campaign, 302 or 307 is safer because it avoids teaching clients a permanent rule.

Now add a rewrite:

if host == "www.shop.test" and path starts "/checkout":
  rewrite origin path to "/checkout-v2"
  add X-Original-Path: /checkout

The browser stays on /checkout. The origin receives /checkout-v2. Logs should show both the public path and the internal path. If the rewrite breaks, users should not need to understand the internal route to report the problem; operators should be able to map it.

Cache, Crawlers, and Sensitive Paths

Redirects are not observed only by browsers. Search crawlers, mobile SDKs, API clients, shared caches, and monitoring probes can all learn from them. A permanent redirect on a public documentation page may be desirable because it consolidates links and avoids repeated hops. A permanent redirect on an OAuth callback, webhook target, or checkout submission can be much harder to roll back because clients may remember it or because third-party integrations may not follow the redirect the way browsers do.

That means edge policy usually needs classes of paths:

static pages:       canonical redirects allowed, permanent when stable
checkout/login:     preserve query and method, test carefully, avoid surprise hops
OAuth callbacks:    exact allowlist, preserve state/code, avoid broad rewrites
webhooks:           avoid redirects; make providers call the final URL
realtime paths:     bypass cache, preserve upgrade or stream behavior

Rewrites have similar classes. Rewriting /api/* to a new backend can be a clean migration if the public API contract stays identical. Rewriting /admin/* before authentication can be a security boundary mistake. Rewriting a cacheable page without updating the cache key can make two internal routes share one stored response. The edge rule is therefore not independent from cache policy, auth policy, or observability.

The practical design move is to test policy with examples, not just with rule syntax. Keep a small table of representative URLs and expected behavior: final visible URL, status code, query handling, method behavior, cache status, selected origin, and rule id. This turns edge policy into something reviewable before it becomes production traffic.

Operational Failure Modes

Failure: dropping query parameters. Canonical redirects often rebuild URLs. Preserve required query parameters deliberately, and test checkout, login, OAuth, search, and campaign paths.

Failure: using the wrong redirect code for methods. 301 or 302 may change client behavior for non-GET requests. Use 307 or 308 when method and body preservation matter, or avoid redirecting unsafe submissions.

Failure: permanent redirects for temporary policy. Browsers and caches can remember permanent redirects. Use temporary status codes while validating new policy, especially for migrations with rollback risk.

Failure: rewrite hides the real origin route. If logs show only the public URL or only the internal URL, debugging requires guesswork. Emit rule id, original host/path, rewritten route, selected origin, and response status.

Failure: rule order creates surprises. Redirects, cache bypass, rate limits, auth checks, and rewrites can interact. Review rules as an ordered program, not as independent checkboxes.

Useful signals include redirect status by rule id, redirect chain length, dropped-query detections, method before and after redirect, rewrite target, selected origin, cache status, rate-limit rule id, canonical host mismatch rate, and client errors grouped by edge rule.

Policy Review

Close the lesson and inspect one edge rule set. For each rule, write:

rule id:
match:
action:
visible to client? yes/no
status code if redirect:
query handling:
method handling:
cache interaction:
rewrite target or selected origin:
log fields that prove it fired:
rollback risk:

Then trace one sensitive URL through the rules: checkout, login, OAuth callback, password reset, payment webhook, or realtime upgrade path. The goal is to know exactly whether the client sees a new URL, whether the origin sees a rewritten route, and which policy decision explains the result.

Connections

The WebSockets/SSE lesson showed that realtime endpoints need special timeout and upgrade policy. This lesson generalizes that idea: edge behavior should be designed as HTTP behavior, not left as unnamed configuration.

The next lesson on observability will use the same rule ids, redirect chains, cache statuses, and timing evidence to debug production HTTP symptoms.

Resources

Key Takeaways

PREVIOUS WebSockets, Server-Sent Events, and Long Polling NEXT Observability for HTTP: Logs, Traces, and Wire Symptoms