When a site is migrated, a domain changes, or an SSL certificate has just been installed, it is a common problem for the browser to still open the old address (http:// or without www.). When the same content is reachable at two different addresses, search engines may treat them as separate pages, and visitors may run into an insecure connection warning. On a server running Apache, the standard way to solve redirects like these centrally, in a single file, is the .htaccess file.

What is a .htaccess file and when does Apache read it?

.htaccess is a per-directory Apache configuration file that applies to the directory it sits in and all of its subdirectories. It lets you define redirect, access and rewrite rules without touching the main configuration file (httpd.conf). In shared hosting environments where you have no access to the main configuration, this is in practice the only way to define a redirect rule; and you do not need to restart the server for a change, because Apache re-reads the file on every request.

That convenience has two prerequisites. First, the mod_rewrite module that runs the redirect rules must be enabled on the server (on Debian/Ubuntu it is turned on with sudo a2enmod rewrite). Second, AllowOverride All must be defined in the relevant <Directory> or <VirtualHost> block. If the server is configured with AllowOverride None, Apache ignores the .htaccess file entirely; the rules are silently disabled without any error — which is why most "the rules just don't work" complaints actually come down to this setting.

The convenience also has a cost: Apache checks whether there is a .htaccess at every directory level on the way to the requested file, and re-parses every file it finds on every request — the result is not cached. On a VPS or dedicated server where you do have access to the main server configuration, writing the same rules directly into the <VirtualHost> block is faster, because Apache does not have to scan the directory tree for .htaccess. On shared hosting, there is no other option anyway.

mod_rewrite basics: RewriteEngine, RewriteCond, RewriteRule

Before writing redirect rules in a .htaccess file you need to explicitly switch the module on: RewriteEngine On. Every RewriteRule line after that matches the request URI against a pattern and, if necessary, redirects it to another address. RewriteCond defines a precondition for the single RewriteRule that follows it; if the condition is not met, that rule is skipped. Rules are processed in order from top to bottom in the file, so the ordering directly affects the outcome.

  • %{HTTPS} — gives whether the connection came over SSL as on/off; it is the basis of forcing HTTPS.
  • %{HTTP_HOST} — the domain the client typed into the browser (with or without www).
  • %{REQUEST_URI} — the requested path, excluding the query string (e.g. /products/pen).
  • %{REQUEST_FILENAME} — the full filesystem path corresponding to the request; usually used to check whether a file/directory exists.
  • The [L] flag — do not process any further rules after this one.
  • The [R=301] flag — permanent redirect (search engines replace the old address with the new one).
  • The [NC] flag — makes pattern matching case-insensitive.

Example rules: forcing HTTPS, managing www and gzip compression

The block below shows three common scenarios together: moving all traffic to HTTPS, standardising addresses with the www. prefix, and compressing text-based responses with gzip.

The first block rebuilds the request under the https:// prefix with the same host and path when the %{HTTPS} variable is off. The second block prepends www. if the host name does not begin with it (!^www\.). If you want the opposite — that is, www. always stripped — you write the condition as RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] and use the %1 capture group in the target: RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]. Here %1 comes not from the RewriteRule pattern but from the parentheses in the preceding RewriteCond line; for RewriteRule's own capture groups you use $1.

The last block uses the mod_deflate module and compresses responses of the given MIME types (CSS, JavaScript, plain text, JSON, XML and so on) before they are sent from the server. This reduces the amount of data going to the browser; for the rule to have any effect, the mod_deflate module must be installed and enabled on the server. Adding image and video files, which already arrive compressed, to this list brings no extra benefit and may even create unnecessary CPU load.

Common mistakes

SymptomPossible causeFix
Infinite redirect loopOn a server behind a reverse proxy or load balancer, <code>%{HTTPS}</code> always looks <code>off</code>; the HTTPS rule redirects the request over and overA condition that checks the <code>X-Forwarded-Proto</code> header passed by the proxy has to be added; this problem does not occur in setups where Apache handles SSL directly
The rules never run, the address opens in its old form<code>AllowOverride None</code> is set or <code>mod_rewrite</code> is not enabledAsk the server administrator for <code>AllowOverride All</code> in the relevant <code>&lt;Directory&gt;</code> block; on your own server the module is enabled with <code>a2enmod rewrite</code>
500 Internal Server ErrorA syntax error in the file: an unclosed <code>&lt;IfModule&gt;</code> tag, a badly written flag, or a wrongly escaped dot/slashThe Apache error log (<code>error_log</code>) gives the line number; review the file from that line onwards
Gzip is enabled but the size does not drop<code>mod_deflate</code> is not installed on the server, or the relevant MIME type has not been added to the listConfirm with your server administrator that the module is enabled; check whether the response headers include <code>Content-Encoding: gzip</code>

What these errors have in common is that a single missing condition or the wrong ordering can render an entire rule useless. When using HTTPS and www rules together in the same file, preserving the order (protocol first, host normalisation second) and making sure every RewriteRule is paired with the right RewriteCond greatly reduces the risk of loops and wrong redirects.

Write the rules by hand or use a generator?

Once you have learned the syntax, writing a single rule by hand is not hard. The difficulty is bringing HTTPS forcing, www normalisation, trailing slash cleanup and gzip compression together in the same file in the right order and inside the right <IfModule> blocks — details such as the dot that has to be escaped (\.) are especially easy to miss on the first attempt. For anyone who would rather tick the options and get a ready, copy-paste-friendly file, KEYDAL's .htaccess generator produces that combination automatically.