Apache HTTPD / Representation, proxying, and capacity / Lesson 8
Redirects, internal rewriting, and rule context
Use the simplest supported mapping contract and preserve method, query, and loop semantics deliberately.
On this page
Lesson goals
- Distinguish a client redirect from internal resource mapping.
- Explain server versus per-directory RewriteRule matching scope.
A redirect changes the client’s next request
A redirect sends a response instructing the client to request another URL. An internal rewrite changes server-side mapping without necessarily changing the browser address. Parcel’s legacy documentation link can need a redirect, while an application front controller can need internal mapping. They have different cache, method, visibility, and authorization consequences. Prefer a simple supported directive when it expresses the intended behavior clearly. Powerful rewriting is not automatically a better solution than Alias, Redirect, or a well-defined application route.
The same pattern can mean something different elsewhere
mod_rewrite uses documented matching rules and PCRE-based regular expressions. Server or virtual-host context differs from per-directory and .htaccess context, where directory prefixes are stripped and leading-slash assumptions change. Substitutions can be processed again through internal request handling, and flags determine important continuation behavior. Copying a rule into another context is therefore a behavior change, not a formatting edit. Review decoded path, query handling, conditions, target form, module support, and selected 2.4 patch requirements before relying on a rule.
Read one exact server-context redirect
The fragment below is explicitly for server or virtual-host context and redirects the exact /legacy path to /docs/ with a temporary status. It is never loaded and is not an .htaccess example. No request is sent. The choice of status is a teaching decision, not a universal production policy; permanent caching and method-preservation requirements must be reviewed for the real route. Query behavior and target authority remain explicit review topics rather than assumptions hidden in a short rule.
RewriteEngine On
RewriteRule "^/legacy$" "/docs/" [R=302,L]
mod_rewrite and appropriate context are required. The fragment does not test encoding, query behavior, nested rewrites, proxy handling, or client cache semantics. It must not be transplanted into per-directory scope unchanged.
A locally reasonable pair can form a redirect loop
One rule can send /legacy to /docs while another inherited rule sends /docs back. Proxy scheme information can also create repeated HTTPS redirects if the effective scheme contract is wrong. Do not fix loops by raising recursion limits or suppressing every redirect. Establish the complete route and rule chain. Untrusted host or path text used to construct an external target can create unintended destinations, so validate target authority and avoid dynamic open-ended forwarding. Preserve relevant sensitive data without copying full query strings into public traces.
Detect a cycle in a declared path graph
The complete local model below follows a two-edge synthetic redirect graph and reports a loop. It is not a PCRE test, mod_rewrite engine, HTTP client, or browser cache simulation. Real rule evaluation includes contexts, conditions, query state, flags, and repeated internal processing beyond this graph. The model illustrates why reviewing individual lines is insufficient. A separately authorized validation plan should include normal, unmatched, trailing-slash, query, method, and loop cases without assuming one successful request proves the whole routing contract.
redirects = {"/legacy": "/docs/", "/docs/": "/legacy"}
seen = set()
path = "/legacy"
while path in redirects and path not in seen:
seen.add(path)
path = redirects[path]
print("loop" if path in seen else "finished")
print(sorted(seen))
loop
['/docs/', '/legacy']
Only declared path strings are traversed. No URL is requested, rule compiled, server invoked, or redirect cached.
Common errors and fixes
A server-context leading-slash rule is copied directly into .htaccess.
- Why it happens
- The matching prefix and evaluation context changed.
- How to fix it
- Rewrite the rule for its documented scope and validate representative cases.
- Prevent it next time
- Keep context and assumptions beside each rule.
A permanent redirect is used without reviewing client caching and method behavior.
- Why it happens
- A status code is chosen as a generic success response.
- How to fix it
- Choose semantics for the actual migration and client contract.
- Prevent it next time
- Review redirect lifetime, method, and target scope explicitly.
Two individually plausible rules create a loop.
- Why it happens
- Only isolated lines were reviewed rather than the effective chain.
- How to fix it
- Trace the full mapping and scheme path and correct the conflicting intent.
- Prevent it next time
- Include cycle and unmatched cases in routing review.
Check your understanding
1. Why does the shown rule not claim to work unchanged in .htaccess?
Show worked solution
Per-directory matching strips a directory prefix and changes leading-slash assumptions. The exact rule belongs to its declared server or virtual-host context. Review documented per-directory semantics and the effective include scope before adapting it.
2. Does the Python loop detector prove that an Apache redirect chain is loop-free?
Show worked solution
No. It checks one declared graph only and actually demonstrates a cycle. Apache conditions, encodings, flags, internal requests, query state, and client behavior are outside the model. Actual route validation requires an authorized suitable process.
Key takeaways
- Redirect and internal rewrite have different client and lifecycle effects.
- Rule context and the complete chain determine behavior.