Apache 129 parses .htaccess per request, so configtest never sees it, and one bad line turns every URL below it into a 500. This file came from a server with mod_expires enabled:
# /var/www/example/.htaccess, copied from the old server
Header set X-Content-Type-Options "nosniff"
ExpiresActive OnThe log names the file and the directive but no line. To get one, wrap the file in <Directory> and let configtest parse it, which catches syntax errors but not AllowOverride limits:
curl -s -o /dev/null -w '%{http_code}\n' localhost:8111/css/site.css
sudo tail -n 1 /var/log/apache2/example-error.log
printf '<Directory /var/www/example>\nInclude /var/www/example/.htaccess\n</Directory>\n' \
> /tmp/htcheck.conf
sudo apache2ctl -t -c 'Include /tmp/htcheck.conf' 2>&1 | head -n 1Output
500 [Wed Sep 23 15:58:00.605326 2026] [core:alert] [pid 121144:tid 121144] [client ::1:34432] /var/www/example/.htaccess: Invalid command 'ExpiresActive', perhaps misspelled or defined by a module not included in the server configuration AH00526: Syntax error on line 3 of /var/www/example/.htaccess:
sudo a2enmod expires and a reload fix it; an <IfModule> wrapper would only hide the fault (Ignored Rules). The message also covers typos and a UTF-8 byte-order mark (Invalid command '\xef\xbb\xbfHeader'; sed -i '1s/^\xEF\xBB\xBF//' .htaccess strips it). All six below were reproduced here:
| Log message (status) | Usual cause | See |
|---|---|---|
| Invalid command 'X' (500) | Typo, BOM, module off | 2.11.1 |
| X not allowed here (500) | Class not granted | 2.4.5 |
| bad flag delimiters (500) | Space inside [...] | 2.5.6 |
| AH00124 (500) | Internal rewrite loop | 2.11.3 |
| AH00529 (403) | File unreadable | 1.3 |
| AH00670 (403) | FollowSymLinks off | 2.5 |