Regular Expression Building Blocks Apache and Nginx
Regular Expression Building Blocks for Apache & Nginx
We have a separate document for the Nginx and Apache redirect. However, here we can see the meaning of some of the regular expressions.
Although complex rewrite configurations are not officially supported by the Global Linux Rackspace team, we are providing this information as a reference for customers who may choose to implement it at their discretion.
Simple Rules (Beginner)
Redirect old page to new page
Apache:
RewriteRule ^old-page\.html$ /new-page.html [R=301,L]Nginx:
rewrite ^/old-page\.html$ /new-page.html permanent;Breakdown:
RewriteRule/rewrite= The directive to create a rewrite rule.^old-page\.html$= Match exactly this URL.^= Start of the URL pathold-page= Literal text "old-page"\.= Literal dot (escaped because . has special meaning)html= Literal text "html"$= End of the URL path
/new-page.html= The destination URL[R=301,L]/permanent= Permanent redirect (301), Last rule
What it matches:
/old-page.html✓/old-page.html/extra✗ (has extra path)/prefix/old-page.html✗ (doesn't start with it)
Remove .php extension
Apache:
RewriteRule ^(.+)\.php$ /$1 [R=301,L]Nginx:
rewrite ^/(.+)\.php$ /$1 permanent;Breakdown:
^(.+)\.php$= Match any URL ending in .php^= Start of URL(.+)= Capture one or more characters (the filename)( )= Capturing group, saved as $1.= Any character+= One or more times
\.php= Literal .php (dot is escaped)$= End of URL
/$1= Redirect to captured filename without extension$1= Whatever was captured by (.+)
What it matches:
/about.php→/about✓/contact.php→/contact✓/blog/post.php→/blog/post✓/.php✗ (needs at least one character before)
Force secure connection (HTTP to HTTPS)
Apache:
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]Nginx:
if ($scheme != "https") {
return 301 https://$host$request_uri;
}Breakdown (Apache):
RewriteCond %{SERVER_PORT} 80= Condition: only if using port 80 (HTTP)%{SERVER_PORT}= Server variable for port number
^(.*)$= Match the entire URL path^= Start(.*)= Capture everything (even empty).*= Zero or more of any character
$= End
https://%{HTTP_HOST}/$1= Build the new secure URL%{HTTP_HOST}= Current domain name$1= The captured path
What it does:
http://example.com/page → https://example.com/page
http://example.com/blog/post?id=5 → https://example.com/blog/post?id=5Regex Symbol Reference
Anchors
^— Start of string. Ensures matching begins at the first character.$— End of string. Ensures matching ends at the last character.^...$— Exact match from start to end.
Character Matching
.— Any single character (except newline). Must escape for literal dot:\.\.— Literal dot character (for file extensions)./— Literal forward slash (path separator).
Character Classes
[abc]— Match one of: a, b, or c.[a-z]— Match any lowercase letter.[A-Z]— Match any uppercase letter.[0-9]— Match any digit.[a-zA-Z0-9]— Match any alphanumeric character.[^abc]— Match anything EXCEPT a, b, or c.[^/]— Match anything except slash.
Quantifiers
*— Zero or more of the previous token (greedy).+— One or more of the previous token (greedy).?— Zero or one (makes previous token optional).{n}— Exactly n times.{n,}— At least n times.{n,m}— Between n and m times.
Grouping & Capturing
(...)— Capturing group. Saved as $1, $2, $3, etc.(?:...)— Non-capturing group (groups without saving).
Alternation
|— OR operator. Match left OR right side.
Common Combinations
(.*)— Capture everything (zero or more of any character).(.+)— Capture something (one or more of any character).[a-z0-9-]+— Common slug pattern (lowercase, numbers, hyphens).[^/]+— Match one path segment (anything except slash).
Apache vs Nginx Comparison
| Feature | Apache | Nginx |
|---|---|---|
| Directive | RewriteRule | rewrite |
| Conditions | RewriteCond | if statement |
| Backreference | $1, $2, $3 | $1, $2, $3 |
| Path in .htaccess | Often no leading / | Always with leading / |
| Last rule flag | [L] | last |
| Permanent redirect | [R=301] | permanent |
| Temporary redirect | [R=302] | redirect |
| Case insensitive | [NC] | ~* in location |
| Append query string | [QSA] | Automatic in Nginx |
Common Flags/Options
Apache Flags:
[L]— Last rule. Stop processing further rules.[R=301]— External redirect with 301 (permanent).[R=302]— External redirect with 302 (temporary).[NC]— No Case. Case-insensitive matching.[QSA]— Query String Append. Preserve existing URL parameters.
Nginx Options:
last— Stop processing, search for new location block.break— Stop processing current rewrite set.permanent— 301 permanent redirect.redirect— 302 temporary redirect.
Common Mistakes & Fixes
Mistake 1: Forgetting to escape dots
Wrong:
RewriteRule ^file.txt$ /new.txt [L]Problem: The dot (.) matches ANY character (fileXtxt, file9txt, etc.)
Correct:
RewriteRule ^file\.txt$ /new.txt [L]Mistake 2: Not using anchors
Wrong:
RewriteRule admin /admin-panel.php [L]Problem: Matches /administrator, /admin-area, etc.
Correct:
RewriteRule ^admin$ /admin-panel.php [L]Mistake 3: Greedy matching
Wrong:
RewriteRule ^(.+)/(.+)$ /page.php?a=$1&b=$2 [L]Problem: For /a/b/c, captures a/b and c (not what you want)
Correct:
RewriteRule ^([^/]+)/([^/]+)$ /page.php?a=$1&b=$2 [L]This captures exactly two segments: a and b
Mistake 4: Wrong capture reference
Wrong:
RewriteRule ^blog/(.+)$ /post.php?slug=$2 [L]Problem: There's only one capture group, but using $2
Correct:
RewriteRule ^blog/(.+)$ /post.php?slug=$1 [L]Quick Tips
- Start simple, add complexity gradually — Test each piece before combining.
- Always use anchors — ^ and $ prevent unexpected matches.
- Escape special characters — . ? + * ( ) [ ] | \ all need escaping for literal use.
- Test your rules — Use online regex testers or enable Apache/Nginx rewrite logs.
- Order matters — Put specific rules before general ones.
- Use capturing groups wisely — Only capture what you need to reuse.
Updated 21 days ago