What's Actually Happening
Apache URL rewrite rules are configured but not working. URLs are not being rewritten as expected, redirects don't happen.
The Error You'll See
```bash $ curl http://example.com/old-page
HTTP/1.1 200 OK # Expected redirect to /new-page but page loads without redirect ```
Rewrite not applied:
RewriteRule ^old-page$ /new-page [R=301,L]
# Rule in .htaccess but no effectModule not loaded:
```bash $ apache2ctl -M | grep rewrite
# No output - module not loaded ```
Internal error:
500 Internal Server Error
.htaccess: Invalid command 'RewriteEngine'Why This Happens
- 1.Module not loaded - mod_rewrite not enabled in Apache
- 2.RewriteEngine off - RewriteEngine directive not set to On
- 3.AllowOverride None - .htaccess not allowed by Apache config
- 4.Rule syntax error - Incorrect RewriteRule syntax
- 5.Rule order wrong - Rules processed in wrong order
- 6.VirtualHost scope - Rules in wrong location
Step 1: Check mod_rewrite Module
```bash # Check if module loaded: apache2ctl -M | grep rewrite
# Or: apachectl -M | grep rewrite_module
# Should show: rewrite_module (shared)
# Enable module (Ubuntu/Debian): sudo a2enmod rewrite sudo systemctl restart apache2
# Enable module (CentOS/RHEL): # Module should be in config, uncomment LoadModule line
# Check module file exists: ls /etc/apache2/mods-available/rewrite.load ls /usr/lib/apache2/modules/mod_rewrite.so
# Check LoadModule directive: grep -r "LoadModule rewrite_module" /etc/apache2/
# Restart Apache: sudo systemctl restart apache2
# Verify module loaded: apache2ctl -M | grep rewrite ```
Step 2: Enable RewriteEngine
```apache # In .htaccess: RewriteEngine On
# In VirtualHost: <VirtualHost *:80> ServerName example.com
RewriteEngine On RewriteRule ^old-page$ /new-page [R=301,L] </VirtualHost>
# Check RewriteEngine: grep -r "RewriteEngine" /etc/apache2/ grep "RewriteEngine" .htaccess
# Must be On: RewriteEngine On # Not: RewriteEngine Off ```
Step 3: Allow .htaccess Overrides
```apache # In Apache config (sites-enabled): <VirtualHost *:80> ServerName example.com DocumentRoot /var/www/html
# Allow .htaccess: <Directory /var/www/html> AllowOverride All # Or specific: AllowOverride FileInfo </Directory> </VirtualHost>
# Check AllowOverride: grep -r "AllowOverride" /etc/apache2/sites-enabled/
# AllowOverride options: # All - All directives allowed # None - .htaccess ignored # FileInfo - Rewrite directives allowed # AuthConfig - Auth directives # Limit - Access control
# Must not be None for rewrite to work in .htaccess
# Restart Apache: sudo systemctl restart apache2 ```
Step 4: Check Rule Syntax
```apache # Basic rewrite syntax: RewriteRule Pattern Substitution [Flags]
# Pattern - regex to match URL # Substitution - target URL or path # Flags - R (redirect), L (last), NC (no case), etc.
# Examples:
# Simple redirect: RewriteRule ^old-page$ /new-page [R=301,L]
# Redirect with pattern capture: RewriteRule ^product/([0-9]+)$ /products?id=$1 [L]
# Case insensitive: RewriteRule ^page$ /new-page [NC,L]
# Redirect entire directory: RewriteRule ^old-dir/(.*)$ /new-dir/$1 [R=301,L]
# Force HTTPS: RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Remove www: RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^(.*)$ https://%1%{REQUEST_URI} [R=301,L]
# Check syntax: apache2ctl configtest # Or: apachectl -t ```
Step 5: Check Rewrite Conditions
```apache # RewriteCond precedes RewriteRule: # Rule only applies if condition matches
# Check HTTPS: RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# Check host: RewriteCond %{HTTP_HOST} ^www\.example\.com$ RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
# Check file existence: RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?q=$1 [L]
# Multiple conditions: # AND (default): all must match # OR: use [OR] flag
RewriteCond %{HTTP_HOST} ^example\.com$ [OR] RewriteCond %{HTTP_HOST} ^www\.example\.com$ RewriteRule ^(.*)$ http://newdomain.com/$1 [R=301,L]
# Available variables: # %{HTTP_HOST}, %{REQUEST_URI}, %{HTTPS} # %{REMOTE_ADDR}, %{QUERY_STRING} # %{REQUEST_FILENAME}, %{TIME} ```
Step 6: Enable Rewrite Logging
```apache # In Apache config (Apache 2.4): LogLevel alert rewrite:trace3
# Or in VirtualHost: <VirtualHost *:80> LogLevel rewrite:trace3 </VirtualHost>
# Log levels: # trace1 - trace8 (trace8 most verbose) # debug, info, notice, warn, error
# Check logs: tail -f /var/log/apache2/error.log | grep rewrite
# Or separate log file: ErrorLogFormat "[%t] [%l] [pid %P] %F: %E: %M"
# Debug specific rewrite: LogLevel rewrite:trace8
# Apache 2.2 syntax: RewriteLog "/var/log/apache2/rewrite.log" RewriteLogLevel 3
# Restart Apache: sudo systemctl restart apache2 ```
Step 7: Fix Common Rule Issues
```apache # Issue: Redirect loop # Fix: Add condition to prevent loop
RewriteCond %{HTTP_HOST} !^www\. [NC] RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
# Issue: Query string lost # Fix: Use QSA flag
RewriteRule ^old-page$ /new-page [R=301,QSA,L]
# Issue: Wrong path # Fix: Check DocumentRoot and path
# Relative path (from DocumentRoot): RewriteRule ^page$ /new-page [L]
# Absolute URL: RewriteRule ^page$ http://example.com/new-page [R=301,L]
# Issue: .htaccess in subdirectory # Fix: Path relative to .htaccess location
# In /var/www/html/blog/.htaccess: RewriteRule ^post/(.+)$ /blog/article/$1 [L]
# Issue: Rules not processing # Fix: Check order, use L flag
# L flag stops processing: RewriteRule ^old$ /new [R=301,L] RewriteRule ^old$ /other [R=301] # Won't run ```
Step 8: Check Rule Location
```apache # .htaccess: # Placed in DocumentRoot or subdirectory # Affects directory and subdirectories
RewriteEngine On RewriteBase / RewriteRule ^old$ /new [R=301,L]
# VirtualHost: <VirtualHost *:80> ServerName example.com DocumentRoot /var/www/html
RewriteEngine On RewriteRule ^old$ /new [R=301,L]
<Directory /var/www/html> RewriteEngine On RewriteRule ^page$ /new-page [L] </Directory> </VirtualHost>
# Global config: # In /etc/apache2/conf-available/rewrite.conf: RewriteEngine On # Rules here apply to all sites
# Check which location your rules are in # VirtualHost rules > Directory rules > .htaccess ```
Step 9: Use RewriteBase
```apache # RewriteBase sets base URL for relative substitutions
# In .htaccess at /var/www/html/blog/: RewriteEngine On RewriteBase /blog/
# Without RewriteBase, paths relative to DocumentRoot RewriteRule ^post/(.+)$ article/$1 [L] # Maps /blog/post/123 -> /blog/article/123
# Check if RewriteBase needed: # If .htaccess in subdirectory, usually needed
# Common pattern: RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]
# For WordPress: # In /var/www/html/.htaccess: RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] ```
Step 10: Apache Rewrite Verification Script
```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-apache-rewrite.sh #!/bin/bash
echo "=== Module Status ===" apache2ctl -M 2>/dev/null | grep rewrite || apachectl -M 2>/dev/null | grep rewrite
echo "" echo "=== AllowOverride Settings ===" grep -r "AllowOverride" /etc/apache2/sites-enabled/ 2>/dev/null || grep -r "AllowOverride" /etc/httpd/conf.d/ 2>/dev/null
echo "" echo "=== .htaccess Files ===" find /var/www/html -name ".htaccess" -exec echo "=== {} ===" \; -exec cat {} \; 2>/dev/null | head -50
echo "" echo "=== VirtualHost Rewrite Rules ===" grep -r "RewriteRule" /etc/apache2/sites-enabled/ 2>/dev/null || grep -r "RewriteRule" /etc/httpd/conf.d/ 2>/dev/null
echo "" echo "=== Config Syntax ===" apache2ctl configtest 2>/dev/null || apachectl -t 2>/dev/null
echo "" echo "=== Test Rewrite ===" echo "Test URL: curl -I http://localhost/old-page" echo "Expected: Redirect or rewrite"
echo "" echo "=== Rewrite Log (if enabled) ===" tail -10 /var/log/apache2/error.log 2>/dev/null | grep -i rewrite || echo "No rewrite logs found"
echo "" echo "=== Debug Commands ===" echo "Enable debug: LogLevel rewrite:trace8" echo "Test syntax: apache2ctl configtest" echo "Test rule: curl -v http://localhost/test-url" EOF
chmod +x /usr/local/bin/check-apache-rewrite.sh
# Usage: /usr/local/bin/check-apache-rewrite.sh
# Test specific rule: alias test-rewrite='curl -I' ```
Apache Rewrite Checklist
| Check | Command | Expected |
|---|---|---|
| Module loaded | apache2ctl -M | rewrite_module |
| RewriteEngine | grep RewriteEngine | On |
| AllowOverride | grep AllowOverride | All or FileInfo |
| Rule syntax | apache2ctl configtest | Syntax OK |
| Rule order | check .htaccess | Correct order |
| RewriteBase | check subdirectory | Correct base |
Verify the Fix
```bash # After fixing rewrite issue
# 1. Check module apache2ctl -M | grep rewrite // rewrite_module (shared)
# 2. Test rewrite curl -I http://example.com/old-page // HTTP/1.1 301 Moved Permanently // Location: http://example.com/new-page
# 3. Check logs tail /var/log/apache2/error.log | grep rewrite // Rewrite applied successfully
# 4. Test multiple URLs curl -I http://example.com/product/123 // Redirected or rewritten as expected
# 5. Check .htaccess cat .htaccess // RewriteEngine On, correct rules
# 6. Verify no errors apache2ctl configtest // Syntax OK ```
Related Issues
- [Fix Apache VirtualHost Not Working](/articles/fix-apache-virtualhost-not-working)
- [Fix Apache htaccess Not Processing](/articles/fix-apache-htaccess-not-processing)
- [Fix Apache Redirect Loop](/articles/fix-apache-redirect-loop)