Common .htaccess URL Rewrite Rules
URL rewriting with mod_rewrite is one of the most powerful features of the Apache .htaccess file. It allows you to create clean, SEO-friendly URLs, set up redirects, and control how your server responds to requests. This guide provides ready-to-use rewrite rules for the most common scenarios.
Understanding mod_rewrite Basics
Before diving into rules, understand the key components:
RewriteEngine
Every .htaccess file using rewrite rules must start with:
RewriteEngine OnThis enables the rewrite engine for the directory.
RewriteRule Syntax
RewriteRule Pattern Substitution [Flags]- Pattern: A regular expression matched against the URL path
- Substitution: The URL to serve or redirect to
- Flags: Modifiers that control behavior (e.g., [L], [R=301], [NC])
Common Flags
| Flag | Meaning |
|---|---|
| [L] | Last rule — stop processing further rules |
| [R=301] | Permanent redirect (301) |
| [R=302] | Temporary redirect (302) |
| [NC] | Case-insensitive matching |
| [QSA] | Append query string to substitution |
| [F] | Forbidden — return 403 error |
| [P] | Proxy — internally proxy the request |
RewriteCond Syntax
RewriteCond TestString ConditionPattern [Flags]Conditions must be met before the following RewriteRule is applied.
Important: Always test rewrite rules on a staging environment or subdirectory before applying them to your live site.
Domain and Protocol Redirects
Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]Force HTTPS and WWW Together
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [L,R=301]Force HTTPS and Remove WWW
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^(.*)$ https://example.com/$1 [L,R=301]Redirect Entire Domain to New Domain
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?olddomain\.com$ [NC]
RewriteRule ^(.*)$ https://www.newdomain.com/$1 [L,R=301]URL Cleanup and SEO
Remove Trailing Slash
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]Add Trailing Slash
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)\.(.*)$
RewriteRule ^(.*)([^/])$ /$1$2/ [L,R=301]Remove .html Extension
Serve clean URLs without the .html extension:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html [L]Also redirect .html URLs to clean versions:
RewriteRule ^(.+)\.html$ /$1 [L,R=301]Remove .php Extension
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]
RewriteRule ^(.+)\.php$ /$1 [L,R=301]Remove index.html or index.php from URLs
RewriteEngine On
RewriteRule ^(.*)index\.(html|php)$ /$1 [L,R=301]Page and Path Redirects
Redirect a Single Page
Redirect 301 /old-page.html /new-page.htmlOr using RewriteRule:
RewriteRule ^old-page\.html$ /new-page.html [L,R=301]Redirect a Directory to Another Directory
RewriteRule ^old-folder/(.*)$ /new-folder/$1 [L,R=301]Redirect All Pages to a Single Page (Maintenance Mode)
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/maintenance\.html$
RewriteCond %{REMOTE_ADDR} !^123\.456\.789\.000$
RewriteRule ^(.*)$ /maintenance.html [L,R=302]Replace 123.456.789.000 with your IP address to exempt yourself from the redirect.
Redirect Based on Query String
RewriteCond %{QUERY_STRING} ^id=123$
RewriteRule ^product\.php$ /products/widget-name? [L,R=301]The trailing ? removes the old query string.
Clean URL Routing
Front Controller Pattern (Used by Many CMS/Frameworks)
Route all non-file, non-directory requests to index.php:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]This is the basis for URL routing in WordPress, Laravel, CodeIgniter, and many other frameworks.
Pretty URLs for a Custom Application
Convert URLs like /products/widgets to product.php?category=widgets:
RewriteRule ^products/([a-zA-Z0-9_-]+)/?$ product.php?category=$1 [L,QSA]
RewriteRule ^products/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/?$ product.php?category=$1&item=$2 [L,QSA]Blog-Style URLs
Convert /2026/03/my-article to blog.php?year=2026&month=03&slug=my-article:
RewriteRule ^([0-9]{4})/([0-9]{2})/([a-zA-Z0-9_-]+)/?$ blog.php?year=$1&month=$2&slug=$3 [L,QSA]Security Rules
Block Access to Hidden Files
RewriteRule (^\.|/\.) - [F]Block Specific User Agents (Bots)
RewriteCond %{HTTP_USER_AGENT} (BadBot|EvilScraper|SpamCrawler) [NC]
RewriteRule .* - [F,L]Block Hotlinking (Prevent Image Theft)
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F,L]Block Access by IP Address
Order Deny,Allow
Deny from 192.168.1.100
Deny from 10.0.0.0/24
Allow from allPerformance Optimization
Enable Gzip Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/xml text/xml
AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>Browser Caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/font-woff2 "access plus 1 year"
</IfModule>Troubleshooting Rewrite Rules
- 500 Internal Server Error: Check for syntax errors. Comment out rules one by one to find the culprit.
- Redirect loops: Ensure your conditions properly exclude the target URL from the pattern.
- Rules not working: Verify that
RewriteEngine Onis present and mod_rewrite is enabled. - Order matters: Rules are processed top-to-bottom. More specific rules should come before general ones.
- Check the [L] flag: Without it, subsequent rules may modify the rewritten URL.
- Test with [R=302] first: Use temporary redirects during testing, then switch to [R=301] when confirmed working. Browsers aggressively cache 301 redirects.
Tip: Use online .htaccess testing tools to validate your rules before deploying to production.
Related Articles
- Introduction to .htaccess Files
- How to Force HTTPS on Your Website
- Troubleshooting 500 Internal Server Errors
Need help with URL rewrites or .htaccess configuration? Contact our support team at {{SUPPORT_EMAIL}} or visit {{SUPPORT_URL}}.