Using .htaccess files with Apache httpd 2.4
An .htaccess file is a small piece of Apache configuration that you put in a folder of your website. Its directives apply to that folder and everything below it, and changes work at once, without a reload. Apache reads them only where AllowOverride allows. If you can edit the main server configuration, put your rules there: it is faster and easier to keep track of.
What an .htaccess file is
A .htaccess file in /var/www/example.com/public/docs/ does exactly the same as this section in the server configuration:
<Directory "/var/www/example.com/public/docs">
# ...the same directives...
</Directory>
The syntax is the same as in the main files. The difference is when the file is read. The main configuration is read once, at startup. A .htaccess file is read again on every request. That is why changes take effect immediately, and also why it costs time. You can change the file name with AccessFileName, but a non-standard name confuses anyone else who works on the site: they will not know where to look.
.htaccess exists for people who cannot touch the server configuration: customers on shared hosting, or applications such as WordPress that ship their own rules. If you run the server yourself, you rarely need one.
Switching .htaccess on: AllowOverride
The default is AllowOverride None. Apache then does not even look for .htaccess files. Debian, Ubuntu, RHEL and Fedora all keep that default for /var/www, so a fresh install ignores your file completely. To allow it, add AllowOverride to the <Directory> block for the site, in the vhost file:
<Directory /var/www/example.com/public>
Options FollowSymLinks
AllowOverride FileInfo AuthConfig Indexes
Require all granted
</Directory>
Then sudo apachectl configtest and reload. AllowOverride works only in a plain <Directory> section, not in <Location>, <Files> or <DirectoryMatch>. Never put it on <Directory />: that makes Apache look for .htaccess files all the way up to the root of the disk.
| Value | Allows, for example |
|---|---|
None | nothing; files are not read at all |
AuthConfig | AuthType, AuthName, AuthUserFile, Require |
FileInfo | ErrorDocument, Redirect, RewriteRule, Header, AddType, SetHandler, SetEnvIf |
Indexes | DirectoryIndex, FallbackResource, IndexOptions, ExpiresByType |
Limit | the old Order, Allow, Deny (2.2 syntax) |
Options or Options=Indexes,MultiViews | Options, optionally only the listed ones |
All | every directive allowed in .htaccess context |
For finer control, AllowOverrideList names single directives. This allows redirects and nothing else:
AllowOverride None
AllowOverrideList Redirect RedirectMatch
A directive that is not allowed produces a 500 Internal Server Error, and the error log says “… not allowed here”. Sections such as <Files>, <FilesMatch> and <If> are allowed whenever overrides are on at all; the directives inside them still need their own class.
The cost in speed
With overrides on, Apache checks for a .htaccess file in every folder of the path, on every request, whether or not one exists. Suppose overrides are on from /var/www down, and a browser asks for /blog/2026/post.html in /var/www/example.com/public. Apache checks five folders: /var/www, example.com, public, blog and 2026. At 200 requests a second that makes 200 × 5 = 1,000 extra file lookups a second, even if no .htaccess file exists anywhere. Files it does find are read and parsed each time, and mod_rewrite rules in .htaccess are recompiled on every request instead of once. The operating system caches most of this, so a small site will not notice. A busy one will. Enable overrides only on the folders that need them.
When to use the main config
- Use the main config when you have root access. Put the same lines in the site’s
<Directory>block, setAllowOverride None, test and reload. Everything is in one place, under version control, and checked byapachectl configtest. - Use .htaccess when users must manage their own folders without root access, or when an application needs to write its own rules and you accept that.
- Either way, a stray
.htaccessleft over from an old host can silently change behavior. Search for them:find /var/www -name .htaccess.
Common examples
Each example below works in a .htaccess file on httpd 2.4, provided the override class shown is allowed and the module is loaded. On Debian and Ubuntu, mod_headers, mod_expires and mod_rewrite are off by default (sudo a2enmod headers expires rewrite); RHEL and Fedora load them.
Custom error pages (FileInfo)
ErrorDocument 404 /errors/not-found.html
ErrorDocument 403 /errors/forbidden.html
Redirect a moved page (FileInfo)
Redirect permanent "/old-page.html" "https://www.example.com/new-page.html"
The target can be a full URL or a path starting with /. Anything after the matched path is added to the target: Redirect permanent "/docs" "/manual" also sends /docs/intro.html to /manual/intro.html. More patterns are in Redirects.
Turn off folder listings (Options)
Options -Indexes
With a + or - the option is added to or removed from the inherited set. Without a sign (Options Indexes) the line replaces the whole set and silently switches off everything else, including FollowSymLinks. Mixing signed and unsigned options in one line is a syntax error.
Send every unknown URL to one script (Indexes)
FallbackResource /index.php
Content management systems and frameworks use this pattern. One line of mod_dir replaces the usual block of rewrite rules: existing files are served as they are, and everything else goes to index.php.
Password-protect a folder (AuthConfig)
AuthType Basic
AuthName "Staff only"
AuthUserFile "/etc/apache2/passwords/example.com"
Require valid-user
Create the password file outside the web root with bcrypt hashing (htpasswd is in apache2-utils on Debian and httpd-tools on RHEL):
sudo mkdir -p /etc/apache2/passwords
sudo htpasswd -c -B /etc/apache2/passwords/example.com alice
On RHEL, use a folder such as /etc/httpd/passwords instead. Leave out -c when you add more users, or the file is overwritten. Basic authentication sends the password unencrypted, so use it only over HTTPS. Details are in Authentication.
Allow only some addresses (AuthConfig)
Require ip 192.0.2.0/24 2001:db8::/32
In old files you will see Order deny,allow with Deny from all and Allow from …. That is the 2.2 syntax. It needs AllowOverride Limit and mod_access_compat, and mixing it with Require gives confusing results. See Access control.
Block backup and config files (AuthConfig)
<FilesMatch "\.(bak|old|sql|ini|log|sh)$">
Require all denied
</FilesMatch>
Both default configurations already refuse to serve files whose names start with .ht, so .htaccess and .htpasswd cannot be downloaded. Keep that rule.
Security header and browser caching (FileInfo, Indexes)
Header always set X-Content-Type-Options "nosniff"
ExpiresActive On
ExpiresByType image/webp "access plus 1 month"
ExpiresByType text/css "access plus 1 week"
The first line needs mod_headers, the others mod_expires. Caching and compression explains cache lifetimes, and Hardening checklist the other security headers.
Force HTTPS with mod_rewrite (FileInfo)
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
In .htaccess, mod_rewrite works only if FollowSymLinks or SymLinksIfOwnerMatch is enabled for the folder. Patterns in .htaccess match the path without the folder prefix and leading slash: in the document root, RewriteRule "^images/(.+)\.jpg$" …, not ^/images/…. If you control the server, a Redirect in the port-80 vhost does this job better. See mod_rewrite.
Which setting wins
- A
.htaccessfile overrides the<Directory>block for the same folder. - A
.htaccessfile in a subfolder overrides one higher up. They are applied from the top folder down. <Files>,<Location>and<If>sections in the main configuration are merged later, so they beat.htaccess. Administrators can use this to enforce rules. For example, a<Location "/">section withOptions -ExecCGIkeeps CGI off even where.htaccessfiles are allowed.
Troubleshooting
- Nothing happens
- The file is probably not being read. Add a line with a made-up word such as
TestMeand reload the page. No 500 error meansAllowOverride Noneis in effect for that folder, or the file is in a different folder than you think, or it has the wrong name (htaccess.txt,.htaccess.txt). - 500 Internal Server Error
- Read the error log (
/var/log/apache2/error.logor/var/log/httpd/error_log). “… .htaccess: Header not allowed here” means the override class is missing. “Invalid command 'Header', perhaps misspelled or defined by a module not included in the server configuration” means the module is not loaded. Other messages point to a syntax error in the line they name. - Rules work in .htaccess but not after moving them
- Rewrite patterns must change: in the server configuration, they see the full path with the leading slash.
Hacked sites. Attackers who get write access often add .htaccess rules that redirect visitors or make uploaded files run as PHP. If you find unexpected rules, see Hacked site recovery. Do not allow overrides on upload folders.
Sources
- Apache HTTP Server Tutorial: .htaccess files, Apache httpd 2.4 documentation
- Apache Core Features (AllowOverride, AllowOverrideList, AccessFileName, Options), Apache httpd 2.4 documentation
- Override Class Index for .htaccess, Apache httpd 2.4 documentation
- Configuration Sections (merge order), Apache httpd 2.4 documentation
- mod_alias (Redirect), Apache httpd 2.4 documentation
- mod_dir (FallbackResource), Apache httpd 2.4 documentation
- mod_rewrite, Apache httpd 2.4 documentation
- mod_expires, Apache httpd 2.4 documentation
- mod_authz_core (Require), Apache httpd 2.4 documentation
- mod_authz_host (Require ip), Apache httpd 2.4 documentation
- Authentication and Authorization, Apache httpd 2.4 documentation
- Upgrading to 2.4 from 2.2 (mixing old and new access directives), Apache httpd 2.4 documentation
- htpasswd, Apache httpd 2.4 documentation
- Debian default apache2.conf, Debian Sources
- apache2 postinst (default modules), Debian Sources
- Fedora default httpd.conf, Fedora Package Sources
- httpd-tools package, Fedora Packages