Your .htaccess has RewriteEngine On and a few rules, yet pretty URLs return 404, redirects never fire, or the file seems to be ignored entirely. Apache will not tell you which of several different problems you have, so work through them in order. Most cases are solved in the first three steps.
If instead the whole site returns 500 after adding rules, go to Apache .htaccess 500 error.
Step 1: is mod_rewrite loaded?
apachectl -M | grep rewrite
You want rewrite_module (shared). If it is missing, on Debian and Ubuntu run sudo a2enmod rewrite and restart Apache. On RHEL-family systems check for a LoadModule rewrite_module modules/mod_rewrite.so line in /etc/httpd/conf.modules.d/. If the module is absent, you normally get a 500 with "Invalid command 'RewriteEngine'", see Invalid command in .htaccess. A silent no-op usually means the module is loaded and something else is wrong, or the rules sit inside an <IfModule mod_rewrite.c> block that never matched.
Step 2: is .htaccess being read at all?
Apache only honors .htaccess where AllowOverride permits it. On Ubuntu the default for /var/www/ is AllowOverride None, which makes every .htaccess file invisible.
<Directory /var/www/html>
AllowOverride All
</Directory>
Put that in the virtual host (or a conf file), run apachectl configtest, and reload. To prove whether .htaccess is read, add a deliberate nonsense line such as ThisShouldBreak. If you still get 200, the file is ignored: wrong directory, wrong filename (it must be exactly .htaccess, with a leading dot and no .txt), AllowOverride None, or the request is served by a different vhost or by another server such as nginx in front. Remove the test line afterward.
Step 3: is the request even reaching this server?
Check the response headers with the HTTP Header Checker. A Server: nginx header, a CDN cache hit, or a cached 301 can mean Apache never sees your new rule. Browsers cache 301 redirects hard; test with curl -I or a private window.
Step 4: FollowSymLinks
If the log shows Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden, add Options +FollowSymLinks to the .htaccess (and make sure AllowOverride includes Options).
Step 5: understand how the pattern matches
This is where most "not working" reports end up.
- In
.htaccess, the pattern does not start with a slash. The per-directory path has the directory prefix stripped, so use^about$, not^/about$. In a virtual host context the path does start with/. - Query strings are not part of the pattern. To match them, use
RewriteCond %{QUERY_STRING} .... - Conditions apply only to the next rule. A
RewriteCondfollowed by twoRewriteRulelines protects only the first. - Rule order matters.
[L]stops processing in the current pass, but in.htaccessthe engine restarts with the rewritten URL, so rules can run again. Add a guard condition to prevent loops. - Regex mistakes: unescaped dots, missing
$anchors, or a greedy.*capturing too much. Test patterns in the Regex Tester, keeping in mind Apache uses PCRE syntax.
A classic front-controller rule, which sends non-file requests to index.php, looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
If this fails with 404, the usual cause is a wrong RewriteBase or the application living in a subdirectory. Add RewriteBase /subdir/ to match.
Step 6: turn on rewrite logging
Apache 2.4 replaced the old RewriteLog directive with a log level. Add this to the server or vhost config (it is not allowed in .htaccess):
LogLevel alert rewrite:trace3
Reload, request the URL, and watch the error log:
sudo tail -f /var/log/apache2/error.log | grep rewrite
Each line shows the pattern tested and whether it matched. Use trace6 for more detail. Remove or lower it afterward, because it is very verbose.
Step 7: check redirect behavior from outside
For redirect rules, confirm the final result with the Redirect Checker and the Redirect Debugger. They show each hop's status code and Location header, which tells you whether a rule fired and whether it used 301, 302 or something else (the R flag defaults to 302). For redirect loops, see ERR_TOO_MANY_REDIRECTS.
Next steps
- Understand status codes in HTTP redirects 301 vs 302.
- Trace chains with the Redirect Checker.
- Test your regex in the Regex Tester.
- Keep an Uptime Monitor on a rewritten URL.
FAQ
Why do my rules work locally but not on the host?
The host may have AllowOverride None, a different module set, or nginx in front that ignores .htaccess. Compare apachectl -M and the vhost config on both machines.
Do I need to restart Apache after editing .htaccess?
No. Apache re-reads .htaccess on every request. You only restart for changes to the main config, such as AllowOverride.
Why does my redirect keep returning the old destination?
Browsers cache 301 responses. Test with curl or a private window, and use a temporary 302 while you experiment.
Is the RewriteLog directive still available?
No. It was removed in Apache 2.4. Use LogLevel with the rewrite:traceN module setting instead.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.