matching a URL with a regular expression does not automatically replace $1 in the redirect destination. A custom redirect handler must explicitly perform capture substitution.
Why it happens
A regex capture group records part of a matched path. For example, ([0-9]+) can capture a numeric item ID:
^item-([0-9]+)/?$
If the requested path is item-42/, the first capture contains 42. However, a simple match check only answers whether the pattern matched. It does not transform this destination:
/archive/item-$1/
Without substitution logic, a custom parser may redirect to the literal text $1.
Match and substitution are separate jobs
In PHP, preg_match() checks a pattern and can return captured values. preg_replace() applies a replacement template and expands backreferences such as $1.
A small redirect engine therefore needs to handle both jobs:
- Match the current request path against the source pattern.
- Build the destination by applying the replacement pattern.
- Validate that the destination is allowed.
-
Send the intended HTTP status code.
-
Stop execution after the redirect.
A practical PHP shape
if (preg_match($pattern, $requestPath)) {
$destination = preg_replace($pattern, $replacement, $requestPath);
if (is_string($destination) && wp_safe_redirect($destination, 301)) {
exit;
}
}
This is only a structural example. A production parser should also validate its stored patterns, restrict destinations appropriately, and define what happens when replacement fails.
Common mistakes
- Cause: The code uses
preg_match()and then passes the destination through unchanged. Fix: perform explicit capture substitution. -
Cause: The pattern expects a leading slash, but the framework provides a path without one. Fix: normalize the path before matching.
-
Cause: The regex is not anchored. Fix: use
^and$when the whole path must match. -
Cause: A configuration line includes a status code the parser never reads. Fix: define and validate the rule format before using it.
-
Cause: External destinations are accepted without checks. Fix: use safe redirect validation or an explicit allowlist.
Test the complete redirect
Do not test only whether the regex matches. Verify the final location and status code with representative values, optional trailing slashes, invalid paths, and destinations containing captured text.
Treat regular expressions as configuration
Distinguish a non-match from an invalid pattern: preg_match() can return false on error. Check replacement failures too, and log a safe rule identifier rather than the full request when it may contain personal data. Trial a changed redirect with a temporary status before committing to a cacheable permanent redirect.
A regex capture is data, not an automatic rewrite. The next action is to add one test that confirms a captured value appears in the final redirect URL rather than as the literal string $1.
