# Fix Apache .htaccess Rewrite Not Matching Mobile User Agents
Your Apache .htaccess file contains rewrite rules to redirect mobile users to a mobile-optimized subdomain, but mobile visitors are not being redirected. Desktop users work fine. The rules look correct but simply do not match.
The Common Problem: RewriteEngine Not Enabled
The most common reason .htaccess rewrite rules silently fail is that RewriteEngine is not enabled in the directory context:
# .htaccess in document root
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} "Android|iPhone|iPad|iPod|Opera Mini|IEMobile" [NC]
RewriteRule ^$ https://m.example.com/ [R=302,L]Without RewriteEngine On, the RewriteCond and RewriteRule directives are silently ignored. No error is logged.
The AllowOverride Problem
Even with correct rules, Apache must be configured to process .htaccess files. Check the VirtualHost or Directory configuration:
<Directory /var/www/html>
AllowOverride None # Problem: .htaccess is completely ignored
</Directory>Change to:
<Directory /var/www/html>
AllowOverride All
</Directory>Or more specifically:
<Directory /var/www/html>
AllowOverride FileInfo
</Directory>FileInfo allows RewriteEngine, RewriteCond, and RewriteRule directives. All allows everything. After changing AllowOverride, restart Apache:
sudo systemctl restart apache2Debugging Rewrite Rules
Enable rewrite logging to see exactly what Apache is matching:
# Apache 2.4
LogLevel alert rewrite:trace3Then check the error log:
sudo tail -f /var/log/apache2/error.log | grep rewriteWith trace3, you will see each RewriteCond evaluation and whether it matched:
[rewrite:trace3] applying pattern '^$' to uri '/'
[rewrite:trace3] RewriteCond: input='Mozilla/5.0 (Linux; Android 14; Pixel 8)' pattern='Android|iPhone|...' => matched
[rewrite:trace3] rewrite '/' -> 'https://m.example.com/'If you see => not matched, the pattern does not match the actual User-Agent string.
Common User-Agent Matching Issues
Modern User-Agents Change Frequently
Google Chrome on Android may not always include "Android" in the User-Agent. Modern browsers use User-Agent reduction. Check actual User-Agent strings:
RewriteCond %{HTTP_USER_AGENT} "Android|iPhone|iPad|iPod|Opera Mini|IEMobile|Mobile.*Safari|Mobile.*Chrome" [NC]
RewriteCond %{HTTP:X-Device-Type} "mobile" [NC,OR]
RewriteCond %{HTTP:Sec-CH-UA-Mobile} "?1"The Sec-CH-UA-Mobile header is part of the new Client Hints API and is more reliable than User-Agent sniffing.
Checking Query String Exceptions
Mobile users who manually want the desktop site often add ?m=0 or ?desktop=1. Add an exclusion:
RewriteCond %{QUERY_STRING} !(^|&)desktop=1 [NC]
RewriteCond %{HTTP_USER_AGENT} "Android|iPhone|iPad|iPod|Opera Mini|IEMobile|Mobile.*Safari" [NC]
RewriteRule ^$ https://m.example.com/ [R=302,L]Cookie-Based Override
Allow users to override the redirect:
RewriteCond %{HTTP_COOKIE} !^.*prefer_desktop=1.*$ [NC]
RewriteCond %{HTTP_USER_AGENT} "Android|iPhone|iPad|iPod|Opera Mini|IEMobile|Mobile.*Safari" [NC]
RewriteCond %{QUERY_STRING} !(^|&)desktop=1 [NC]
RewriteRule ^$ https://m.example.com/ [R=302,L]Users who set a prefer_desktop=1 cookie will always get the desktop site.
Testing the Rules
```bash # Test with Android User-Agent curl -sI -A "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 Chrome/120.0.0.0 Mobile Safari/537.36" https://example.com/ | grep -E "HTTP/|Location"
# Test with iPhone User-Agent curl -sI -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15" https://example.com/ | grep -E "HTTP/|Location"
# Test with desktop User-Agent (should NOT redirect) curl -sI -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36" https://example.com/ | grep -E "HTTP/|Location" ```
The first two should return a 302 redirect to https://m.example.com/. The third should return 200 with no redirect.