Using URL Rewriting (mod_rewrite)
1. Enabling RewriteEngine
| Directive | Values | Description |
|---|---|---|
| RewriteEngine | on | off |
Enables or disables the rewrite engine. Must be set to on to use any rewrite rules. |
Note: The
RewriteEngine directive must be placed in the server config, virtual host, or .htaccess file to take effect.
2. Creating RewriteRule Directives
| Directive | Parameters | Description |
|---|---|---|
| RewriteRule | pattern replacement [flags] |
Defines a rule for rewriting URLs. The pattern is matched against the URL path, and the replacement is used to construct the new URL. |
Note: The
[L] flag indicates that this is the last rule to process if it matches. The [R=301] flag performs a permanent redirect.
Warning: Improper use of RewriteRule can lead to infinite loops or incorrect redirects. Always test rules in a development environment.
3. Setting RewriteCond Conditions
| Condition | Description | Example |
|---|---|---|
| RewriteCond | Defines a condition that must be met for the rule to apply | RewriteCond %{REQUEST_URI} ^/old-path/(.*)$ |
| RewriteCond %{REQUEST_URI} | Checks the requested URI | RewriteCond %{REQUEST_URI} ^/api/v[0-9]+/(.*)$ |
| RewriteCond %{HTTP_HOST} | Checks the host name | RewriteCond %{HTTP_HOST} ^www\.example\.com$ |
| RewriteCond %{REQUEST_METHOD} | Checks the HTTP method | RewriteCond %{REQUEST_METHOD} POST |
| RewriteCond %{QUERY_STRING} | Checks the query string | RewriteCond %{QUERY_STRING} ^id=([0-9]+)$ |
| RewriteCond %{ENV:variable} | Checks environment variables | RewriteCond %{ENV:REDIRECT_STATUS} ^$ |
Example: Matching Request URI
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/old-path/(.*)$
RewriteRule ^/old-path/(.*)$ /new-path/$1 [R=301,L]
Example: Matching Host Name
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
Example: Matching Query String
RewriteEngine On
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^page\.html$ /redirect.php?id=%1 [R=301,L]
Note: The
RewriteCond directive is used to set conditions that must be met for the RewriteRule to execute. These conditions can check various server variables, such as the requested URI, host name, query string, and more.
4. Understanding Rewrite Flags
| Flag | Description | Notes |
|---|---|---|
| [E] | Set environment variable | Can be used to pass data between rewrite rules or to CGI scripts |
| [L] | Last rule | Stop processing any more rewrite rules if this one matches |
| [R] | Redirect | Perform an external redirect to the specified URL |
| [F] | Forbidden | Return a 403 Forbidden status code |
| [G] | Go to gateway | Send the request to a CGI script or other gateway |
| [P] | Pass to proxy | Send the request to a proxy server |
| [NS] | No Substitution | Do not substitute the rewritten URL |
| [NC] | No Case | Case-insensitive pattern matching |
| [QSA] | Query String Append | Append the query string from the original request to the rewritten URL |
| [S] | Skip | Skip the next N rules if this rule matches |
| [E=VAR:val] | Environment Variable Set | Set an environment variable with a specific value |
| [T=MIME_TYPE] | Type | Set the MIME type for the rewritten URL |
| [B] | Backreference | Escape backreferences in the substitution string |
| [N] | Next | Restart the rewrite process with the new URL |
| [D] | Disable | Disable the rewrite engine for this request |
| [H] | Handler | Set the handler for the rewritten URL |
| [PT] | Pass Through | Pass the rewritten URL through to the next handler |
| [CO] | Cookie | Set a cookie in the response |
| [E=VAR:val] | Environment Variable Set | Set an environment variable with a specific value |
| [E=VAR:val] | Environment Variable Set | Set an environment variable with a specific value |
5. Using Regular Expressions
Regular expressions are used in RewriteRule directives to match and capture parts of the URL. The syntax for regex patterns in mod_rewrite is based on the PCRE (Perl Compatible Regular Expressions) library.
| Component | Description | Example |
|---|---|---|
| Pattern | Regular expression to match the URL path or query string | ^/old/(.*)$ |
| Flags | Optional modifiers that affect how the rule is processed | [R=301,L] |
Example: Capturing Groups in RewriteRule
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^/old/(.*)$ /new/$1 [R=301,L]
</IfModule>
Regular expressions in mod_rewrite support the following special characters:
| Character | Description | Example |
|---|---|---|
| ^ | Start of string anchor | ^/path/ |
| $ | End of string anchor | \.html$ |
| . | Matches any single character except newline | test.*html |
| * | Matches zero or more of the preceding character | ab* matches "a", "ab", "abb", etc. |
| + | Matches one or more of the preceding character | ab+ matches "ab", "abb", etc. |
| ? | Matches zero or one of the preceding character | ab? matches "a" or "ab" |
| () | Capturing group for backreferences | (\w+) captures word characters |
| [] | Character class matching any one of the enclosed characters | [abc] matches 'a', 'b', or 'c' |
| | | Alternation operator (OR) | cat|dog matches "cat" or "dog" |
Note: When using regex in
RewriteRule, the pattern is matched against the URL path after any query string has been removed. To match query strings, use RewriteCond with the %{QUERY_STRING} variable.
Example: Matching Query String with Regex
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^id=(\d+)$
RewriteRule ^old-page\.html$ /new-page/%1? [R=301,L]
</IfModule>
6. Capturing Groups and Backreferences
Apache's mod_rewrite supports capturing groups and backreferences using parentheses and $N syntax, where N is the group number.
| Element | Description | Example |
|---|---|---|
| Capturing Group | Groups parts of a pattern using parentheses () |
^/user/([^/]+)/profile$ |
| Backreference | References captured content using $N, where N is the group number |
$1 refers to first capturing group |
Example: Using Capturing Groups in RewriteRule
RewriteRule ^/user/([^/]+)/profile$ /user.php?username=$1 [L]
Note: Group numbers start at 1, not 0.
$0 refers to the entire matched string.
Note: Backreferences in the substitution string are expanded at rewrite time.
7. Performing Redirects with Flags
| Flag | Description | Usage Example |
|---|---|---|
| R | Performs a redirect, sending the client to a new URL. | R=301 |
| R=301 | Permanent redirect (HTTP 301). | R=301 /new-page.html |
| R=302 | Temporary redirect (HTTP 302). | R=302 /temp-page.html |
| R=307 | Temporary redirect (HTTP 307) - preserves HTTP method. | R=307 /temp-page.html |
| R=308 | Permanent redirect (HTTP 308) - preserves HTTP method. | R=308 /new-page.html |
| L | Stops processing rules if matched (last rule). | R=301 /new.html,L |
| NC | Case-insensitive matching. | R=301 /new.html,NC |
| QSA | Appends query string from original request to the redirect. | R=301 /new.html?query=1,QSA |
| P | Proxy the redirect to another server (requires mod_proxy). | R=301 /new.html,P |
| F | Returns a 403 Forbidden status instead of redirecting. | F |
| G | Returns a 410 Gone status instead of redirecting. | G |
| E | Sets environment variables for the redirect. | R=301 /new.html,E=redirected=1 |
Note: The
R flag can be combined with other flags using commas. For example, R=301,L or R=302,NC,QSA.
Warning: Using
R=301 for temporary redirects can cause issues with search engine indexing. Use R=302 for temporary redirects.
8. Rewriting Query Strings
| Directive | Description | Example |
|---|---|---|
| RewriteCond | Condition to test the query string | RewriteCond %{QUERY_STRING} ^id=123&name=foo$ |
| RewriteRule | Rule to rewrite the query string | RewriteRule ^page\.html$ /newpage.html? [R,L] |
Example: Rewrite query string to clean URL
RewriteEngine On
RewriteCond %{QUERY_STRING} ^id=([0-9]+)&name=(.*)$
RewriteRule ^page\.html$ /product/%1/?name=%2? [R,L]
Note: The
? at the end of the RewriteRule prevents the query string from being appended to the new URL.
9. Conditional Rewrites Based on Variables
| Variable | Description | Usage Example |
|---|---|---|
| %{HTTP_HOST} | Current host name | RewriteCond %{HTTP_HOST} ^example\.com$ [NC] |
| %{REQUEST_URI} | Requested URI | RewriteCond %{REQUEST_URI} ^/old-path/(.*)$ |
| %{QUERY_STRING} | Query string of the request | RewriteCond %{QUERY_STRING} ^param=value$ |
| %{REQUEST_METHOD} | HTTP method used (GET, POST, etc.) | RewriteCond %{REQUEST_METHOD} ^POST$ |
| %{HTTP_USER_AGENT} | User agent string | RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*$ |
| %{REMOTE_ADDR} | Client IP address | RewriteCond %{REMOTE_ADDR} ^192\.168\.1\. |
Example: Redirect based on User Agent
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/5\.0.*Mobile
RewriteRule ^(.*)$ /mobile/$1 [R,L]
Example: Rewrite based on Host and Query String
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteCond %{QUERY_STRING} ^lang=fr$
RewriteRule ^(.*)$ /fr/$1 [L]
Note: The
[NC] flag makes the condition case-insensitive. The [L] flag stops processing if the condition matches.
10. Using RewriteMap for Dynamic Rewrites
The RewriteMap directive allows you to define a mapping function that can be used in rewrite rules. This is particularly useful for dynamic rewrites based on external data sources or complex logic.
| Directive | Description | Usage |
|---|---|---|
| RewriteMap | Defines a rewrite map function | RewriteMap mapname "command" |
Example: Using RewriteMap with a shell command
RewriteMap lowercase "sed 's/\(.*\)/\L\1/'"
RewriteRule ^/([A-Z]+)$ /${lowercase:$1} [R,L]
Note: The
RewriteMap directive is processed before any rewrite rules and can be used in .htaccess files if AllowOverride includes RewriteMap.
Warning: Be cautious with external commands in
RewriteMap as they can introduce security vulnerabilities if not properly sanitized.
Key Points
RewriteMapallows for dynamic content transformations.- It supports various map types including
sed,prg, anddbm. - Maps can be used in rewrite rules with the syntax
${mapname:value}.
11. Debugging Rewrite Rules
Example: Enabling Rewrite Logging
<IfModule mod_rewrite.c>
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3
</IfModule>
| Directive | Description | Default | Notes |
|---|---|---|---|
| RewriteLog | Specifies the path to the rewrite log file | None | Must be set with RewriteLogLevel |
| RewriteLogLevel | Controls the verbosity of rewrite logging (0-9) | 0 | Higher numbers provide more detail |
Note: Rewrite logging can significantly impact performance and should only be enabled during debugging.
Debugging Levels Comparison
- Level 0: No logging
- Level 1: Basic rule matching info
- Level 2: Includes condition checks
- Level 3: Full rule and condition trace
- Level 4-9: Verbose debugging with internal processing details
Debugging Workflow
- Enable
RewriteLogand set appropriateRewriteLogLevel - Reproduce the issue or desired behavior
- Check the log file for rule execution details
- Adjust rules based on logged information
- Disable logging to avoid performance impact
12. Implementing Common Rewrite Patterns
| Pattern | Description | Example | Notes |
|---|---|---|---|
| ^/old-page\.html$ | Redirects a specific old page to a new location | RewriteRule ^/old-page\.html$ /new-page.html [R=301,L] |
Use R=301 for permanent redirects |
| ^/category/(.*)$ | Redirects category URLs to a new structure | RewriteRule ^/category/(.*)$ /blog/category/$1 [R=301,L] |
Matches any content after /category/ |
| ^/product/([0-9]+)$ | Redirects numeric product IDs to a new format | RewriteRule ^/product/([0-9]+)$ /item/$1 [R=301,L] |
Uses capture groups to preserve ID |
| ^/user/(.*)$ | Redirects user profile URLs to a new format | RewriteRule ^/user/(.*)$ /profile/$1 [R=301,L] |
Redirects all user paths to a new structure |
| ^/blog/(.*)$ | Redirects blog URLs to a new format | RewriteRule ^/blog/(.*)$ /articles/$1 [R=301,L] |
Redirects all blog paths to a new directory |
Example: Redirecting Old Pages to New Locations
RewriteEngine On
RewriteRule ^/old-page\.html$ /new-page.html [R=301,L]
RewriteRule ^/old-blog\.html$ /blog/index.html [R=301,L]