Tutorial

.htaccess Guide: Redirects, Security, and Performance for Apache

May 08, 2026

Back to Blog

What is .htaccess and Why Does It Matter?

The .htaccess file (hypertext access) is a powerful configuration file used by the Apache web server. It allows you to control server behavior on a per-directory basis without modifying the main Apache configuration files or restarting the server. This makes it the go-to tool for shared hosting environments where users don't have access to the main server configuration.

From URL redirects and pretty permalinks to security headers and caching rules, .htaccess handles an enormous range of web server functionality. Nearly every PHP application you've used, whether WordPress, Laravel, Drupal, or Joomla, includes a .htaccess file as part of its standard setup.

How .htaccess works: When Apache receives a request, it checks every directory from the server root down to the requested file's directory for .htaccess files. Each file's directives are applied in order. This per-request directory scanning is what makes .htaccess both powerful (instant changes without restart) and potentially slow (filesystem reads on every request).

URL Redirects and Rewriting

URL rewriting with mod_rewrite is the most common use of .htaccess. It lets you transform ugly URLs into clean, SEO-friendly ones, redirect old pages, and route all requests through a front controller.

Enable mod_rewrite

# Always start with RewriteEngine On
RewriteEngine On
RewriteBase /

HTTP to HTTPS Redirect

The most common redirect in modern web hosting. Force all traffic through HTTPS for security and SEO:

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

WWW to Non-WWW (and Vice Versa)

Remove WWW

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

Force WWW

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

Common Redirect Types

CodeTypeUse CaseSEO Impact
301Permanent RedirectURL changed forever, page movedPasses link equity to new URL
302Temporary RedirectMaintenance, A/B testingDoes not pass link equity
307Temporary (strict)Preserves HTTP method (POST stays POST)Does not pass link equity
308Permanent (strict)Like 301 but preserves HTTP methodPasses link equity

Redirect Specific Pages

# Redirect a single page
Redirect 301 /old-page.html /new-page.html

# Redirect an entire directory
RedirectMatch 301 ^/blog/(.*)$ /articles/$1

# Redirect old domain to new domain
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?oldsite\.com$ [NC]
RewriteRule ^(.*)$ https://newsite.com/$1 [R=301,L]

# Remove trailing slashes
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]

WordPress Permalinks

WordPress uses .htaccess to create clean URLs. The standard WordPress rewrite rules route all non-file, non-directory requests through index.php:

# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress

Security Headers

Security headers instruct browsers to enforce various security policies, protecting your site and visitors from common attacks. Adding them via .htaccess is straightforward:

# Security Headers
<IfModule mod_headers.c>

  # Prevent clickjacking
  Header always set X-Frame-Options "SAMEORIGIN"

  # Prevent MIME-type sniffing
  Header always set X-Content-Type-Options "nosniff"

  # Enable XSS protection
  Header always set X-XSS-Protection "1; mode=block"

  # Referrer policy
  Header always set Referrer-Policy "strict-origin-when-cross-origin"

  # HTTP Strict Transport Security (HSTS)
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

  # Content Security Policy
  Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"

  # Permissions policy
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

</IfModule>
CSP caution: Content-Security-Policy is the most powerful but also the most potentially breaking header. Start with Content-Security-Policy-Report-Only to test without blocking, then switch to enforcing mode once you've confirmed nothing breaks.

Hotlink Protection

Hotlinking is when other websites embed your images directly, stealing your bandwidth. Prevent this with a few rewrite rules:

# Hotlink protection
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?yourdomain\.com [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?google\.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp|svg)$ - [F,NC,L]

# F = 403 Forbidden for hotlinked images
# Allow empty referer (direct access) and Google (image search)

Directory Listing and Access Control

Disable Directory Listing

# Prevent browsing directory contents
Options -Indexes

# Block access to sensitive files
<FilesMatch "\.(env|log|ini|sql|bak|config)$">
  Require all denied
</FilesMatch>

# Protect .htaccess itself
<Files .htaccess>
  Require all denied
</Files>

# Block access to hidden files (dotfiles)
<FilesMatch "^\.">
  Require all denied
</FilesMatch>

Password Protection

Protect a directory or admin area with HTTP Basic Authentication:

# Create password file (run on command line)
$ htpasswd -c /path/outside/webroot/.htpasswd username

# .htaccess in the directory to protect
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /path/outside/webroot/.htpasswd
Require valid-user
Security warning: Always store the .htpasswd file OUTSIDE the web root. If stored inside the document root, the password hashes could be downloaded by anyone who guesses the file path.

IP-Based Access Control

# Allow only specific IPs (Apache 2.4+)
<RequireAll>
  Require ip 192.168.1.0/24
  Require ip 10.0.0.0/8
</RequireAll>

# Block specific IPs
<RequireAll>
  Require all granted
  Require not ip 1.2.3.4
  Require not ip 5.6.7.0/24
</RequireAll>

Custom Error Pages

# Custom error pages
ErrorDocument 400 /errors/400.html
ErrorDocument 401 /errors/401.html
ErrorDocument 403 /errors/403.html
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
ErrorDocument 503 /errors/maintenance.html

Browser Caching with mod_expires

Browser caching tells visitors' browsers to store static files locally. On subsequent visits, the browser loads these files from cache instead of downloading them again, dramatically improving page load times.

<IfModule mod_expires.c>
  ExpiresActive On

  # Images - cache for 1 year
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/x-icon "access plus 1 year"

  # CSS and JavaScript - cache for 1 month
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"

  # Fonts - cache for 1 year
  ExpiresByType font/woff2 "access plus 1 year"
  ExpiresByType font/woff "access plus 1 year"

  # HTML - cache for 10 minutes
  ExpiresByType text/html "access plus 600 seconds"
</IfModule>

Gzip Compression with mod_deflate

Compression reduces the size of files sent to the browser, often by 60-80% for text-based resources. This directly speeds up page loads, especially on slower connections.

<IfModule mod_deflate.c>
  # Compress text-based content
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/json
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE font/woff
  AddOutputFilterByType DEFLATE font/woff2

  # Don't compress already-compressed files
  SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp)$ no-gzip
</IfModule>

PHP Settings via .htaccess

You can override certain php.ini settings in .htaccess, which is particularly useful on shared hosting where you don't have access to the PHP configuration:

# PHP settings via .htaccess
php_value upload_max_filesize 64M
php_value post_max_size 64M
php_value max_execution_time 300
php_value max_input_time 300
php_value memory_limit 256M
php_value max_input_vars 3000

# Error handling
php_flag display_errors Off
php_flag log_errors On
php_value error_log /home/user/logs/php_error.log
PHP-FPM note: The php_value and php_flag directives only work when PHP runs as an Apache module (mod_php). If your server uses PHP-FPM (which most modern setups do), these directives won't work in .htaccess. Instead, use a .user.ini file or configure settings in the PHP-FPM pool configuration.

Performance Impact of .htaccess

While .htaccess is incredibly convenient, it comes with a performance cost. Apache must search for and parse .htaccess files on every request, for every directory level from the document root to the requested file.

.htaccess (Per-Directory)

  • Slower - parsed on every request
  • No restart needed - instant changes
  • User accessible - no root needed
  • Best for shared hosting

VirtualHost Config (Main Config)

  • Faster - parsed once at startup
  • Requires restart to apply changes
  • Root access needed
  • Best for dedicated/VPS servers
Performance tip: If you have root access to your server, move your .htaccess rules into the VirtualHost configuration and set AllowOverride None. This eliminates the filesystem overhead of .htaccess parsing on every request. On high-traffic sites, this alone can improve response times by 10-20%.

Complete .htaccess Template

Here's a comprehensive .htaccess template combining all the best practices covered above:

# === SECURITY ===
Options -Indexes
ServerSignature Off

<FilesMatch "\.(env|log|ini|sql|bak|config|yml|yaml)$">
  Require all denied
</FilesMatch>

# === SECURITY HEADERS ===
<IfModule mod_headers.c>
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Strict-Transport-Security "max-age=31536000"
</IfModule>

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

# === CACHING ===
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
</IfModule>

# === COMPRESSION ===
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css
  AddOutputFilterByType DEFLATE application/javascript
</IfModule>

.htaccess with Panelica

Panelica supports both Apache and Nginx as web servers. For domains configured to use Apache, .htaccess files work seamlessly with the panel's domain configuration. You can manage your Apache domains through the panel GUI while still customizing behavior with .htaccess in your document root.

Panelica's dual web server support: When you create a domain in Panelica, you can choose between Nginx (primary, high-performance) and Apache (full .htaccess support). For sites that depend heavily on .htaccess rules, such as WordPress with complex rewrite rules, Apache mode provides full compatibility while still benefiting from Panelica's resource management, SSL, and user isolation features.

Debugging .htaccess Issues

When .htaccess rules don't work as expected, use these debugging techniques:

1
Check if AllowOverride is enabled. If AllowOverride None is set in your Apache config, .htaccess files are completely ignored. You need at least AllowOverride All for mod_rewrite.
2
Check if mod_rewrite is loaded. Run apache2ctl -M | grep rewrite. If it's not listed, enable it with a2enmod rewrite.
3
Enable rewrite logging. Add LogLevel alert rewrite:trace6 to your VirtualHost config temporarily. Check the error log for detailed rewrite processing steps.
4
Test with a simple rule first. Start with a basic redirect to confirm .htaccess is being read, then add complexity incrementally.

Conclusion

The .htaccess file remains one of the most versatile tools in a web developer's toolkit. From simple redirects to complex URL rewriting, from security headers to browser caching, it handles a remarkably wide range of server configuration tasks, all without requiring server restarts or root access.

While Nginx is increasingly popular as a primary web server (and doesn't support .htaccess), Apache with .htaccess support remains essential for compatibility with thousands of PHP applications that ship with .htaccess files. Understanding how to write and optimize these rules is a fundamental skill for anyone managing web servers or PHP applications.

Start with the template provided above, customize it for your specific needs, and remember: if you have root access, consider moving your most critical rules into the VirtualHost configuration for better performance. And if you need both Apache and Nginx, hosting panels like Panelica let you choose the right web server per domain.

Share:
Built for 2026, not 2002.