.htaccess 500 Errors: Every Cause and the Fix
Site 500s right after editing .htaccess? The ordered checklist: AllowOverride, module guards, Options, php_value, syntax — and why configtest can't help.
.htaccess has no syntax check. apachectl configtest validates the server config files — it never looks at .htaccess, because the file is parsed per request, at request time, in the context of the directory that owns it. So the failure mode is binary: the site works, or every page under that directory returns 500. The good news is the causes are a short list, and the fix is nearly always “find the line.”
Step 0 — confirm it’s the file
Rename .htaccess to .htaccess.off (or delete it). If the site comes back, the file is guilty. If it doesn’t, the 500 lives elsewhere — PHP, the app, file permissions — and nothing here applies.
The causes, roughly in frequency order
1. AllowOverride doesn’t permit the directive.
Every .htaccess directive belongs to an override class, and the server admin decides which classes are enabled:
| Directive(s) | Override class needed |
|---|---|
RewriteRule, Redirect, ErrorDocument, ExpiresByType, Header, AddOutputFilterByType |
FileInfo |
AuthType, AuthUserFile, Require |
AuthConfig |
Order, Allow, Deny (mod_access_compat, 2.2-era only) |
Limit |
Options -Indexes |
Options |
DirectoryIndex |
Indexes |
A directive whose class is disabled produces “not allowed here” in the error log and a 500 — not a polite skip. The Options class is the classic: Options -Indexes is the single most common 500 on locked-down shared hosting. Wrap what you can in <IfModule> (it only helps with missing modules, not missing overrides) and test incrementally.
2. A module the directives need isn’t loaded.
php_value on a PHP-FPM host, ExpiresByType without mod_expires, Header without mod_headers — each is an “Invalid command” 500. This is why every block the generator emits sits inside <IfModule …>: the feature turns off instead of the site dying. Trade-off: a skipped block fails silently — the feature quietly doesn’t work. Verify the behavior after upload; don’t assume presence of rules means presence of the feature.
3. Syntax errors introduced on paste.
Unbalanced quotes (AuthName "Members), a RewriteCond that lost its RewriteRule (harmless alone but a sign of a broken paste), smart quotes from a word processor (“ instead of "), a stray > or <, smart-apostrophe comments. Also check encoding: save as UTF-8 without BOM — a BOM on line 1 can corrupt the first directive — and Unix LF line endings (CRLF is tolerated by most builds but has caused real bugs on some).
4. <IfModule> typos.
The check is on the module identifier, not the file name — mod_rewrite.c is right, mod_rewrite alone is also accepted (Apache matches both forms), but mod_rewrite2.c, mod_expires.so, or Mod_rewrite.c variants that don’t resolve make the block silently skip (no 500 — the worse outcome: your rules just don’t run and you think they do). Copy module names verbatim: mod_rewrite.c, mod_alias.c, mod_expires.c, mod_headers.c, mod_deflate.c, mod_authz_host.c, mod_authn_file.c, mod_php.c.
5. Regex mistakes in RedirectMatch/RewriteRule.
An unclosed group ^/old-(.*$, an invalid quantifier *foo, an unescaped [ — regex compile failures 500 the request. Quote paths that contain spaces (Redirect 301 "/my page" "/new page") and remember . matches any character: ^/old.html$ also matches /oldXhtml — escape it ^/old\.html$.
6. File mechanics.
Permissions 644 or 640 (readable by the Apache user); the file actually named .htaccess, not htaccess or .htaccess.txt (Windows hides the extension — check); placed in the served document root, not beside it; and AccessFileName unchanged if the host renamed it (rare).
When there’s no 500 but nothing works
The opposite failure is quieter: AllowOverride None — or a limited set that excludes your class — makes Apache ignore the whole file. No error, no rules, and many shared hosts do exactly this in some directories. Symptoms: your 301 doesn’t fire, HTTPS isn’t forced, but the site is fine. The test: drop a deliberate ErrorDocument 404 /definitely-test-404.html line in, hit a missing URL — default Apache 404 page means your file is being ignored entirely. Then the fix is a support ticket, not more syntax.
Cached 301s fake the same symptom differently: a corrected rule “not working” may be your browser replaying the old permanent redirect. Test with curl -I, always.
The bisect method (3 minutes)
- Rename the file → site works → confirmed.
- Restore half the file. Works? The fault is in the other half. Doesn’t? It’s in this half.
- Halve again. Three or four rounds isolates any line in a ~40-line file.
- Fix the line, restore the whole file, verify with
curl -Iand a missing-page test.
Reading the actual error
If you have log access, it names the culprit directly:
- cPanel → Metrics → Errors (or
error_lognext to public_html) - Plesk → Logs
- Own server →
/var/log/apache2/error_logorjournalctl -u httpd
Invalid command 'X' = missing module (add <IfModule>). X not allowed here = AllowOverride class. RewriteRule: bad flag delimiters / regex compile errors = syntax.
Now that the file won’t take the site down, make it do something useful — back to the generator or on to mod_rewrite basics.