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:

bash
RewriteRule ^old-page$ /new-page [R=301,L]
# Rule in .htaccess but no effect

Module not loaded:

```bash $ apache2ctl -M | grep rewrite

# No output - module not loaded ```

Internal error:

bash
500 Internal Server Error
.htaccess: Invalid command 'RewriteEngine'

Why This Happens

  1. 1.Module not loaded - mod_rewrite not enabled in Apache
  2. 2.RewriteEngine off - RewriteEngine directive not set to On
  3. 3.AllowOverride None - .htaccess not allowed by Apache config
  4. 4.Rule syntax error - Incorrect RewriteRule syntax
  5. 5.Rule order wrong - Rules processed in wrong order
  6. 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

CheckCommandExpected
Module loadedapache2ctl -Mrewrite_module
RewriteEnginegrep RewriteEngineOn
AllowOverridegrep AllowOverrideAll or FileInfo
Rule syntaxapache2ctl configtestSyntax OK
Rule ordercheck .htaccessCorrect order
RewriteBasecheck subdirectoryCorrect 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 ```

  • [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)