Skip to main content

.htaccess: redirect and optimization rules

Hosting & cPanel · 26.02.2025 · 4 min read
Illustration for “.htaccess: redirect and optimization rules”

.htaccess is an Apache configuration file that applies rules to the folder it sits in and to every subfolder. On shared hosting it is the only way to control the server without access to the main config — but any syntax mistake takes the whole site down with a 500 error, so the file must be edited carefully and with a backup on hand.

Forcing HTTPS

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

The condition %{HTTPS} off only triggers for requests without TLS, so the rule does not loop on pages that are already secure.

Rewrite rule flags: what they mean

FlagMeaning
LLast — stop processing further rules after this one
R=301Permanent redirect (302 with no number is temporary)
NCNo Case — the condition ignores letter case
QSAQuery String Append — keep GET parameters during the redirect

Without QSA, a link like example.com/page?utm_source=ads loses the utm_source parameter after the redirect — this breaks ad campaign analytics, so the flag is mandatory for rules that carry parameters.

Redirecting www to non-www

RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

The reverse case (adding www) needs a mirrored rule: the condition catches hosts without www., and the replacement adds it at the front.

Common error: 500 Internal Server Error

After editing .htaccess the site suddenly shows a 500 error. The domain's error_log holds the reason:

[Wed Sep 24 10:12:03 2026] [core:alert] [pid 8842] /home/user/public_html/.htaccess:
Invalid command 'RewriteEngin', perhaps misspelled or defined by a module
not included in the server configuration

This is a typo in the directive name (RewriteEngin instead of RewriteEngine) — Apache cannot recognize it and stops processing the whole file. The second common cause of the same error is that the mod_rewrite module is not loaded on the server at all; then the message reads "not included in the server configuration" with no typo in the directive text.

Browser caching

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png  "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType text/css   "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
</IfModule>
File typeRecommended lifetimeWhy
Images1 yearRarely change, versioned through the file name
CSS / JS1 monthUpdated more often, risk of serving a stale version
HTMLNo cache or minutesPage content must refresh immediately
Fonts1 yearAlmost never change

GZIP compression

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css
  AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>

GZIP via mod_deflate shrinks text responses by 60–80%, but it does not compress already-compressed formats — adding image/jpeg or video/mp4 to the list is pointless, the server just burns CPU with no size gain.

Protecting a directory

# Blocking access to a folder (Apache 2.2)
<Files *>
  Order Deny,Allow
  Deny from all
</Files>

# Protecting wp-config.php
<Files wp-config.php>
  Order Allow,Deny
  Deny from all
</Files>
# The same block for Apache 2.4+
<Files wp-config.php>
  Require all denied
</Files>

The Order/Deny directives belong to mod_access_compat from Apache 2.2 — on newer servers running Apache 2.4 the Require all denied syntax works, and it is better to use it if the hosting supports it.

When rules in .htaccess will not work

  • The main server config has AllowOverride None for the directory — then Apache does not read .htaccess at all, and no edits will have any effect
  • The mod_rewrite or mod_expires module is disabled by the administrator — ask hosting support to confirm
  • The site runs on Nginx, not Apache — Nginx never reads .htaccess at all, the rules need to move into the server{} config
  • The provider runs LiteSpeed without the Apache-directive compatibility module — some rare rules (for example, mod_security through .htaccess) are not supported there

Verifying the result

curl -I http://example.com/
# Expected: HTTP/1.1 301 Moved Permanently
# Location: https://example.com/

The Location header must point to the final HTTPS address without www (or with www, depending on the chosen canonical form), and the status code must be exactly 301, not 302, otherwise search engines will not pass the page's ranking weight to the new address. More on SSL types and HTTPS setup is in the article on redirecting HTTP to HTTPS, and on blocking other sites from linking to your images in Hotlink Protection.

Was this article helpful?
← Back to Knowledge Base Ask Support