Implementing Rewrite Rules
1. Using Rewrite Directive
| Directive | Syntax | Description | Notes |
|---|---|---|---|
| rewrite | rewrite <regex> <replacement> [last|break|redirect|permanent] |
Applies a regular expression to the request URI and rewrites it | Can be used in server, location, and if contexts |
Example: Basic Rewrite Rule
location /old-page.html {
rewrite ^/old-page\.html$ /new-page.html last;
}
Example: Rewrite with Capture Groups
location /user/ {
rewrite ^/user/([^/]+)/?$ /profile.php?user=$1 last;
}
Note: The
last flag stops processing and re-evaluates the location block with the new URI.
Warning: Inefficient regex patterns can cause performance issues. Always test rewrite rules carefully.
2. Understanding Rewrite Flags
| Flag | Description | Usage |
|---|---|---|
| last | Stops processing further rewrite rules if this rule matches. | rewrite ^/old-path(.*)$ /new-path$1 last; |
| break | Stops processing further rewrite rules but continues with the current location. | rewrite ^/old-path(.*)$ /new-path$1 break; |
| redirect | Returns a 302 redirect to the new URL. | rewrite ^/old-path(.*)$ /new-path$1 redirect; |
| permanent | Returns a 301 permanent redirect to the new URL. | rewrite ^/old-path(.*)$ /new-path$1 permanent; |
3. Using Return Directive
| Directive | Parameters | Description |
|---|---|---|
| return | code [text] or code url |
Stops processing and returns a response with the specified code and optional content or redirects to a URL |
Note: The
return directive is processed before any other directives in the location block.
Warning: Using
return with a URL that includes variables like $uri can lead to unexpected behavior or infinite loops if not handled carefully.
4. Implementing 301 Redirects
| Directive | Parameters | Description |
|---|---|---|
| rewrite | ^/old-path/(.*)$ /new-path/$1 permanent |
Performs a 301 redirect using the permanent flag |
Note: The
permanent flag ensures the redirect is a 301 (permanent) redirect, which is beneficial for SEO.
5. Implementing 302 Redirects
| Directive | Values | Description |
|---|---|---|
| rewrite | ^/old-path/(.*)$ /new-path/$1 permanent |
Performs a 302 redirect with the permanent flag |
| return | 302 /new-path |
Redirects with 302 status code to a new path |
Note: The
return 302 directive is used for temporary redirects. For permanent redirects, use return 301.
6. Creating Conditional Rewrites
| Directive | Parameters | Description |
|---|---|---|
| if | $variable = value |
Checks if a variable matches a specific value |
| if | $variable != value |
Checks if a variable does not match a specific value |
| if | !$variable |
Checks if a variable is empty or not set |
| if | $variable ~ regex |
Checks if a variable matches a regular expression (case-sensitive) |
| if | $variable !~ regex |
Checks if a variable does not match a regular expression (case-sensitive) |
| if | $variable ~* regex |
Checks if a variable matches a regular expression (case-insensitive) |
Example: Conditional Redirect Based on User Agent
location / {
if ($http_user_agent ~* "Mobile") {
return 301 /mobile;
}
proxy_pass http://backend;
}
Example: Conditional Rewriting Based on IP Address
location / {
if ($remote_addr ~ "^192\.168\.1\.") {
rewrite ^/(.*)$ /internal/$1 permanent;
}
proxy_pass http://backend;
}
Note: The
if directive is evaluated at runtime and can impact performance. Use it sparingly.
Warning: Be cautious with complex regex patterns in
if directives as they can lead to unexpected behavior or performance issues.
7. Using Regex Captures
| Group | Description | Example |
|---|---|---|
| \$1 | First captured group | rewrite ^/user/([^/]+)/posts/(.*)$ /user/$1/posts/$2; |
| \$2 | Second captured group | rewrite ^/([a-z]+)/(\d+)/(.*)$ /$1/$3?id=$2; |
| \$0 | Entire matched string | rewrite ^/old/(.*)$ /new/$0; |
Example: Capturing and Redirecting
location /old/ {
rewrite ^/old/([^/]+)/(.*)$ /new/$1/$2 permanent;
}
Note: The
\$1, \$2, etc., refer to the captured groups in the regex pattern. These are used to reconstruct parts of the URL during rewriting.
8. Rewriting with Query String
| Directive | Description | Example |
|---|---|---|
| rewrite | Performs a rewrite using a regular expression and replacement string. Can include query strings in the replacement. | rewrite ^/old-path/(.*)$ /new-path/$1?arg=value&other=123 last; |
| return | Can be used to redirect with a query string. | return 301 /new-path?arg=value&other=123; |
Example: Rewrite with Query String
location /old-path/ {
rewrite ^/old-path/(.*)$ /new-path/$1?arg=value&other=123 last;
}
Note: Query strings in the replacement are handled by Nginx, but care must be taken to properly escape special characters like
& and ?.
9. Testing Conditions
| Condition | Description | Example |
|---|---|---|
| -f | Tests if the file exists | if (-f $request_filename) { return 403; } |
| -d | Tests if the directory exists | if (-d $document_root$uri) { return 403; } |
| -e | Tests if the file or directory exists | if (-e $request_filename) { return 200; } |
| -x | Tests if the file is executable | if (-x $request_filename) { return 403; } |
| -s | Tests if the file size is greater than zero | if (-s $request_filename) { return 403; } |
| -z | Tests if the file size is zero | if (-z $request_filename) { return 403; } |
| -r | Tests if the file is readable | if (-r $request_filename) { return 403; } |
| -w | Tests if the file is writable | if (-w $request_filename) { return 403; } |
| -o | Tests if the file is owned by the current user | if (-o $request_filename) { return 403; } |
| -l | Tests if the file is a symbolic link | if (-l $request_filename) { return 403; } |
| -L | Tests if the file is a symbolic link (alias for -l) | if (-L $request_filename) { return 403; } |
| -N | Tests if the file is not a regular file (e.g., directory or symlink) | if (-N $request_filename) { return 403; } |
| -T | Tests if the file is a text file | if (-T $request_filename) { return 403; } |
| -B | Tests if the file is a binary file | if (-B $request_filename) { return 403; } |
| -u | Tests if the file is owned by a user with UID | if (-u $request_filename) { return 403; } |
| -g | Tests if the file is owned by a group with GID | if (-g $request_filename) { return 403; } |
| -h | Tests if the file is a hidden file (starts with dot) | if (-h $request_filename) { return 403; } |
| -p | Tests if the file is a pipe or FIFO | if (-p $request_filename) { return 403; } |
| -S | Tests if the file is a socket | if (-S $request_filename) return 403; } |
| -D | Tests if the file is a directory | if (-D $request_filename) { return 403; } |
| -F | Tests if the file is a regular file | if (-F $request_filename) { return 403; } |
| -U | Tests if the file is a Unix domain socket | if (-U $request_filename) { return 403; } |
| -X | Tests if the file is executable (alias for -x) | if (-X $request_filename) { return 403; } |
| -R | Tests if the file is readable (alias for -r) | if (-R $request_filename) { return 403; } |
| -W | Tests if the file is writable (alias for -w) | if (-W $request_filename) { return 403; } |
| -O | Tests if the file is owned by the current user (alias for -o) | if (-O $request_filename) { return 403; } |
| -E | Tests if the file or directory exists (alias for -e) | if (-E $request_filename) { return 403; } |
| -C | Tests if the file is a character device | if (-C $request_filename) { return 403; } |
| -M | Tests if the file is a block device | if (-M $request_filename) { return 403; } |
| -A | Tests if the file is a named pipe (FIFO) | if (-A $request_filename) { return 403; } |
| -Z | Tests if the file is a zero-length file | if (-Z $request_filename) { return 403; } |
| -G | Tests if the file is a group-readable file | if (-G $request_filename) { return 403; } |
| -P | Tests if the file is a pipe or FIFO (alias for -p) | if (-P $request_filename) { return 403; } |
| -Q | Tests if the file is a queue (not supported in most systems) | if (-Q $request_filename) { return 403; } |
| -V | Tests if the file is a virtual file (e.g., /dev/null) | if (-V $request_filename) { return 403; } |
| -K | Tests if the file is a kernel module | if (-K $request_filename) { return 403; } |
| -I | Tests if the file is an image (not supported in most systems) | if (-I $request_filename) { return 403; } |
| -J | Tests if the file is a journal file (not supported in most systems) | if (-J $request_filename) { return 403; } |
| -H | Tests if the file is a hard link (not supported in most systems) | if (-H $request_filename) { return 403; } |
| -F | Tests if the file is a regular file (alias for -f) | if (-F $request_filename) { return 403; } |
| -T | Tests if the file is a text file (alias for -T) | if (-T $request_filename) { return 403; } |
| -B | Tests if the file is a binary file (alias for -B) | if (-B $request_filename) { return 403; } |
| -N | Tests if the file is not a regular file (alias for -N) | if (-N $request_filename) { return 403; } |
| -L | Tests if the file is a symbolic link (alias for -l) | if (-L $request_filename) { return 403; } |
| -S | Tests if the file is a socket (alias for -S) | if (-S $request_filename) { return 403; } |
| -D | Tests if the file is a directory (alias for -d) | if (-D $request_filename) { return 403; } |
| -U | Tests if the file is a Unix domain socket (alias for -U) | if (-U $request_filename) { return 403; } |
| -X | Tests if the file is executable (alias for -x) | if (-X $request_filename) { return 403; } |
| -R | Tests if the file is readable (alias for -r) | if (-R $request_filename) { return 403; } |
| -W | Tests if the file is writable (alias for -w) | if (-W $request_filename) { return 403; } |
| -O | Tests if the file is owned by the current user (alias for -o) | if (-O $request_filename) { return 403; } |
| -E | Tests if the file or directory exists (alias for -e) | if (-E $request_filename) { return 403; } |
| -C | Tests if the file is a character device (alias for -C) | if (-C $request_filename) { return 403; } |
| -M | Tests if the file is a block device (alias for -M) | if (-M $request_filename) { return 403; } |
| -A | Tests if the file is a named pipe (FIFO) (alias for -A) | if (-A $request_filename) { return 403; } |
| -Z | Tests if the file is a zero-length file (alias for -Z) | if (-Z $request_filename) { return 403; } |
| -G | Tests if the file is a group-readable file (alias for -G) | if (-G $request_filename) { return 403; } |
| -P | Tests if the file is a pipe or FIFO (alias for -p) | if (-P $request_filename) { return 403; } |
| -Q | Tests if the file is a queue (not supported in most systems) (alias for -Q) | if (-Q $request_filename) { return 403; } |
| -V | Tests if the file is a virtual file (e.g., /dev/null) (alias for -V) | if (-V $request_filename) { return 403; } |
| -K | Tests if the file is a kernel module (alias for -K) | if (-K $request_filename) { return 403; } |
| -I | Tests if the file is an image (not supported in most systems) (alias for -I) | if (-I $request_filename) { return 403; } |
| -J | Tests if the file is a journal file (not supported in most systems) (alias for -J) | if (-J $request_filename) { return 403; } |
| -H | Tests if the file is a hard link (not supported in most systems) (alias for -H) | if (-H $request_filename) { return 403; } |
Example: Testing file executable permissions
location / {
if (!-x $request_filename) {
return 403;
}
}
Example: Testing file writable permissions
location / {
if (!-w $request_filename) {
return 403;
}
}
Example: Testing file readable permissions
location / {
if (!-r $request_filename) {
return 403;
}
}
Example: Testing file not owned by current user
location / {
if (-O $request_filename) {
return 403;
}
}
10. Preserving Query Strings
| Directive | Description | Example |
|---|---|---|
| rewrite | Applies a rewrite rule; use $args to preserve query strings |
rewrite ^/old-path/(.*)$ /new-path/$1?$args? permanent; |
| return | Redirects with query string preservation using $args |
return 301 /new-path/$1?$args?; |
Example: Rewrite with Query String Preservation
location /old-path/ {
rewrite ^/old-path/(.*)$ /new-path/$1?$args? permanent;
}
Note: The
$args variable contains the query string. Appending ? after $args ensures it's preserved in the rewrite.
11. Using Rewrite Log
Example: Enabling Rewrite Logging
server {
listen 80;
server_name example.com;
error_log /var/log/nginx/rewrite.log debug;
location / {
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
}
}
| Directive | Description | Default |
|---|---|---|
| error_log | Specifies the log file and level for rewrite logging | error_log /var/log/nginx/error.log error; |
| debug | Log level to capture rewrite operations | error_log /var/log/nginx/error.log debug; |
Note: The
debug level logs all rewrite operations, which can be very verbose and impact performance in production environments.
12. Understanding Rewrite vs Return
| Directive | Description | Behavior | Use Case |
|---|---|---|---|
| rewrite | Modifies the URI internally and continues processing | Internal redirect, can be used in server/location context | URL normalization, path manipulation |
| return | Stops processing and sends a response to the client | Immediate HTTP response (301, 302, etc.) | Permanent redirects, custom error pages |