Skip to main content

Apache .htaccess file

Master your Apache .htaccess file with essential tips and tricks for security, redirection, file access control, Content Security Policy (CSP), forcing HTTPS, and performance optimization.

· 4 min read
View raw
Christophe
Markdown, WSL and Docker lover ~ Insatiable curious.
Cet article existe aussi en français — lire la version française.

This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.

Apache .htaccess file

⚡
TL;DR

This is a large reference of ready-to-use .htaccess snippets: Content Security Policy headers, blocking files by name/extension/hidden-dot-prefix, forcing inline display or forced download, forcing HTTPS+www with HSTS preload, maintenance mode, gzip compression and expires headers, IP-based access control, clickjacking/MIME-sniffing protection, disabling script execution or directory listing, password-protecting files/folders, various redirect patterns, and blocking search-engine indexing of a folder.

Some tips and tricks for your .htaccess file (Apache).

These directives work on any Apache server, including the throw-away one you can start locally in seconds with Running an HTML site in seconds using Docker — a convenient way to test a redirect rule before pushing it to production.

CSP - Content Security Policy​

Use the following lines as inspiration:

<IfModule mod_headers.c>
# Add CSP (Content Security Policy)
Header set Protected-by "What-you-want-or-just-drop-this-line"
# Replace XXXXXXXXXXXXXX by your site name like www.yoursite.com
Header always set Feature-Policy "camera 'none'; fullscreen 'self'; microphone 'none'; payment 'none'; sync-xhr 'self' XXXXXXXXXXXXXX"
# Blocks a request if the requested type is
# "style" and the MIME type is not "text/css", or
# "script" and the MIME type is not a JavaScript MIME type.
Header set X-Content-Type-Options "nosniff"
# Prevent from Clickjacking by allowing frame to be displayed only
# on the same origin as the page itself.
Header always set X-Frame-Options SAMEORIGIN
# Force HTTPS (don't use this if you're still on http)
# env=HTTPS didn't work... but while "expr=%{HTTPS} == 'on'" is well working
# see https://stackoverflow.com/questions/24144552/how-to-set-hsts-header-from-htaccess-only-on-https#comment81632711_24145033
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" "expr=%{HTTPS} == 'on'"
# Enables XSS filtering. Rather than sanitizing the page, the browser
# will prevent rendering of the page if an attack is detected.
Header always set X-XSS-Protection "1; mode=block"
# The Referrer header will be omitted entirely. No referrer information is
# sent along with requests.
Header always set Referrer-Policy "no-referrer"
# CSP : define / whitelist domains where files can be loaded
# (f.i. fonts.googleapis.com, ...)
# This should be done for scripts, images, styles, frame, ...
# Replace XXXXXXXXXXXXXX by your site name like https://www.yoursite.com
# ----------------------------------------------------------------------
# UNCOMMENT THE FOLLOWING LINE ONLY IF YOU KNOW WHAT YOU'RE DOING.
# THIS LINE CAN BREAK YOUR SITE SO, ENABLE IT AND TEST YOUR SITE A LOT,
# ALL PAGES IF POSSIBLE.
# ----------------------------------------------------------------------
#Header set Content-Security-Policy: "default-src 'self'; base-uri 'self'; form-action 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://ajax.googleapis.com https://www.google.com https://www.google-analytics.com https://code.jquery.com https://www.gstatic.com https://maxcdn.bootstrapcdn.com https://cdnjs.cloudflare.com https://stackpath.bootstrapcdn.com https://unpkg.com; font-src 'self' data: https://fonts.googleapis.com https://fonts.gstatic.com https://maxcdn.bootstrapcdn.com; style-src 'self' 'unsafe-inline' https://maxcdn.bootstrapcdn.com https://fonts.googleapis.com https://cdnjs.cloudflare.com https://stackpath.bootstrapcdn.com; img-src 'self' data: https://www.paypal.com https://raw.githubusercontent.com; frame-src XXXXXXXXXXXXXX https://www.google.com https://www.youtube.com; frame-ancestors 'none'"
</IfModule>

This configuration file sets HTTP security headers for a website to protect against common attacks like clickjacking, XSS, and man-in-the-middle attacks. It uses Apache's mod_headers module to add these protective headers to every response sent to visitors' browsers. Most lines are already active, but there's one powerful Content Security Policy line commented out that requires careful testing before enabling.

Files​

Block access to some files based on their names​

Block requests to these files:

<FilesMatch "(file_1\.gif|file_2\.png)">
Order Allow,Deny
Deny from all
</FilesMatch>

This configuration block uses Apache's FilesMatch directive to target two specific image files by their names and then blocks all access to them by denying requests from every client. It's a way to hide or protect those particular files from being served to anyone who requests them.

Block access to some files based on their extensions​

Block access to all files except those whose extension is mentioned in the list below:

First option:

RewriteCond %{REQUEST_FILENAME} !(.*)\.(bmp|css|eot|html?|icon?|jpe?g|js|gif|pdf|png|svg|te?xt|ttf|webp|woff2?|xml|zip)$
RewriteRule . - [F]

This snippet blocks all requests that don't match common static file extensions like images, stylesheets, and fonts. It's a security measure that denies access to any URL that doesn't end with one of the explicitly allowed file types.

Second option:

RewriteCond %{REQUEST_FILENAME} !\.(ico?n|img|gif|jpe?g|png|css|map)$ [NC]
RewriteCond %{REQUEST_FILENAME} !\.js(\?.*)?$ [NC]
RewriteCond %{REQUEST_FILENAME} !\.(eot|svg|ttf|woff2?)(\?.*)?$ [NC]
# Comment this line if you wish to make possible to access the /libraries folder by url.
RewriteRule . - [F]

These rules tell the web server to block direct access to certain file types and folders. The first three lines list out which files are allowed to pass through (images, stylesheets, JavaScript, fonts), and then line 5 forbids everything else, effectively preventing people from browsing to directories or accessing files that aren't explicitly whitelisted.

Block access to hidden files & directories​

Block access to a file or folder when the name starts with a dot (i.e., a hidden file or folder):

<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>

This Apache configuration block prevents direct access to hidden files and directories (those starting with a dot, like .htaccess or .git) by checking if they exist on the server and returning a 403 Forbidden error if someone tries to access them. It's a security measure to keep sensitive configuration and version control files hidden from the public.

Force​

Force display​

Don't allow the browser to download such files but tell it how to display them (text in the example):

<FilesMatch "\.(tex|log|aux)$">
Header set Content-Type text/plain
</FilesMatch>

This Apache configuration snippet intercepts requests for files ending in .tex, .log, or .aux and forces the web server to tell browsers these are plain text files, regardless of what the files actually contain. This prevents browsers from trying to interpret or download these development/LaTeX files in unexpected ways.

Prevent downloading​

For instance, force the download of PDF files:

<FilesMatch "\.(pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>

This configuration forces PDF files to download as attachments instead of opening in the browser. When a user requests a PDF, the server tells their browser to treat it as a generic binary file and present a download dialog rather than attempting to display it.

Force https and www, compatible hstspreload​

When implemented in your .htaccess, accessing yoursite.com or http://yoursite.com should redirect to https://www.yoursite.com.

Also, test your site with https://hstspreload.org/ to verify that your preloading is correct (green).

<IfModule mod_rewrite.c>
# Rewrite the URL to force https and www.
RewriteEngine On
# Compliant with hstspreload.org : first redirect to https if needed
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# then redirect to www. when the prefix wasn't mentioned
# hstspreload.org seems to not really like to make the two at once
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>

This Apache configuration file automatically redirects all incoming requests to use HTTPS and add 'www.' to the domain name if it's missing. It does this in two separate steps—first forcing HTTPS, then adding www—because some security certificate services prefer it that way.

Misc​

Disable error reporting​

Don't show errors (just like error_reporting(0) does)

<IfModule mod_php5.c>
php_flag display_errors off
php_flag log_errors on
php_flag track_errors on
php_value error_log error.log
</IfModule>

This configuration block sets up error handling for PHP by hiding errors from users while still recording them to a log file. It only applies if the PHP 5 module is loaded on the server, and it tells PHP to track which errors occur so they can be logged for debugging purposes.

Enable error reporting​

Show errors (just like error_reporting = E_ALL does).

Only use this on a development server otherwise you'll expose sensitive information to your visitors.

<IfModule mod_php5.c>
php_flag display_errors on
php_flag log_errors on
php_flag track_errors on
php_value error_log error.log
</IfModule>

This configuration block sets up error handling for PHP by enabling error display and logging. It tells the web server to show errors to users, log them to a file, and track which errors occurred—but only if the PHP 5 module is actually installed and active on the server.

Enable a maintenance mode​

Redirect every request made to your site to a specific page (called maintenance.php below). Just remember to replace the code ADD_YOUR_IP_HERE with your current IP address.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REMOTE_ADDR} !127.0.0.1 [NC]
RewriteCond %{REMOTE_ADDR} !localhost [NC]
RewriteCond %{REMOTE_ADDR} !ADD_YOUR_IP_HERE [NC]
RewriteCond %{REQUEST_FILENAME} !maintenance.php(.*)$ [NC]
RewriteRule .* /maintenance.php [L,NC,QSA]
</IfModule>

This htaccess snippet redirects all incoming requests to a maintenance.php page, but only for visitors whose IP addresses aren't on a whitelist. It's a common way to put a website in maintenance mode while allowing specific people (like developers) to access the real site.

Optimization​

Compress files based on their type or extensions​

<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/font-otf application/font-ttf application/font-woff application/javascript application/json application/manifest+json application/rss+xml application/vnd.ms-fontobject application/xhtml+xml application/xml application/x-javascript image/svg+xml text/css text/csv text/html text/javascript text/plain text/xml
</IfModule>
</IfModule>
# On some hosting companies, mod_deflate isn't installed but well mod_gzip.
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/font-otf
mod_gzip_item_include mime ^application/font-ttf
mod_gzip_item_include mime ^application/font-woff
mod_gzip_item_include mime ^application/vnd.ms-fontobject
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_include mime ^image/svg+xml*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>

This configuration file enables compression of web content to reduce file sizes and improve load times. It tries two different Apache compression methods: first mod_deflate (the modern standard), and if that's not available, it falls back to mod_gzip (an older alternative). Each method is configured to compress specific file types like HTML, CSS, JavaScript, fonts, and images, while avoiding compressing content that's already compressed.

Add expiration (expires headers)​

Enable ETAGs

<IfModule mod_headers.c>
# Keep the connection alive (not really related to expirations but really increase download speed
Header set Connection keep-alive
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
# Default expiration: 1 hour after request
ExpiresDefault "access plus 1 month"
# CSS and JS expiration
ExpiresByType text/css "access 1 month"
ExpiresByType text/javascript "access 1 month"
ExpiresByType application/javascript "access 1 month"
ExpiresByType application/x-javascript "access 1 month"
# webfonts
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-woff "access 1 year"
ExpiresByType application/x-font-woff2 "access 1 year"
ExpiresByType font/eot "access plus 1 month"
ExpiresByType font/truetype "access 1 year"
ExpiresByType font/opentype "access 1 year"
ExpiresByType font/woff "access 1 year"
ExpiresByType image/svg+xml "access 1 year"
ExpiresByType application/vnd.ms-fontobject "access 1 year"
ExpiresByType application/font-otf "access 1 year"
ExpiresByType application/font-ttf "access 1 year"
ExpiresByType application/font-woff "access 1 year"
ExpiresByType application/x-font-ttf "access 1 year"
# Media
AddType image/vnd.microsoft.icon .cur
ExpiresByType application/ico "access 1 year"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/bmp "access plus 1 month"
ExpiresByType image/gif "access 1 month"
ExpiresByType image/ico "access 1 year"
ExpiresByType image/icon "access 1 year"
ExpiresByType image/jpg "access 1 month"
ExpiresByType image/jpeg "access 1 month"
ExpiresByType image/png "access 1 month"
ExpiresByType image/svg+xml "access 1 month"
ExpiresByType image/vnd.microsoft.icon "access 1 year"
ExpiresByType image/webp "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresByType text/ico "access 1 year"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Flash
ExpiresByType application/x-shockwave-flash "access plus 2 months"
ExpiresByType image/swf "access plus 2592000 seconds"
# Files
ExpiresByType application/pdf "access 1 week"
ExpiresByType application/x-gzip "access 1 month"
ExpiresByType text/x-component "access 1 month"
# Data
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rdf+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/ld+json "access plus 0 seconds"
ExpiresByType application/schema+json "access plus 0 seconds"
ExpiresByType application/vnd.geo+json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
</IfModule>
# Perhaps the MIME type of SWF is incorrect, in this case, the FileMatch will do the job
<IfModule mod_headers.c>
<FilesMatch "\.(swf)$">
Header set Expires "access plus 2592000 seconds"
</FilesMatch>
</IfModule>

This Apache configuration file sets up browser caching rules to improve website performance. It tells browsers how long to keep different types of files (like CSS, images, fonts, and videos) before requesting fresh copies from the server. Static files like fonts and images are cached for a year, while dynamic content like HTML and JSON aren't cached at all since they change frequently.

Protection​

Hardening .htaccess protects you going forward; if you suspect the site has already been compromised, start with aeSecure - QuickScan - Free viruses scanner.

Deny All Access​

## Apache 2.2
Deny from all
## Apache 2.4
# Require all denied

This file contains two different syntax examples for blocking all access to a directory using Apache's access control rules. The first uses Apache 2.2's older 'Deny' directive, while the second shows the newer 'Require' directive syntax for Apache 2.4 and later. Only one should be uncommented depending on your server version.

Deny All Access except you​

Just replace xxx.xxx.xxx.xxx with your IP address.

## Apache 2.2
Order deny,allow
Deny from all
Allow from xxx.xxx.xxx.xxx
## Apache 2.4
# Require all denied
# Require ip xxx.xxx.xxx.xxx

This file configures Apache web server access control in two different ways, showing both the old Apache 2.2 syntax and the newer Apache 2.4 syntax. It sets up a whitelist where all traffic is blocked by default, except for requests coming from a specific IP address.

Stops a browser from trying to MIME-sniff​

<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>

This snippet uses Apache's mod_headers module to add a security header that prevents browsers from trying to guess the type of content being served. By setting X-Content-Type-Options to 'nosniff', we're telling the browser 'trust what I tell you this file is, don't try to be clever about it', which prevents certain types of attacks where a malicious file might be misinterpreted.

Avoid Clickjacking and enable XSS-protection for browsers​

<FilesMatch "\.(pl|php|cgi|spl)$">
<IfModule mod_headers.c>
# security
Header set X-Frame-Options "DENY"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
</FilesMatch>

This configuration block adds security headers to responses for executable files like Perl scripts, PHP pages, and CGI programs. It tells browsers not to embed these pages in frames and to block cross-site scripting attempts, protecting against common web vulnerabilities.

Disable script execution​

Put these lines in e.g. /tmp/.htaccess to prevent execution of scripts in the /tmp folder.

# secure directory by disabling script execution
AddHandler cgi-script .php .pl .py .jsp .asp .sh .cgi
Options -ExecCGI
##Deny access to all CGI, Perl, PHP and Python
<FilesMatch "\.(asp?x|cgi|php|pl|py)$">
Deny from all
</FilesMatch>

This .htaccess file locks down a directory by preventing any scripts from running. It does this in two ways: first by telling the web server to stop treating certain file types as executable scripts, and second by explicitly denying all web access to those dangerous file types.

Disallow listing for directories​

Don't allow the web server to provide the list of files / folders as a dir command does.

<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>

This snippet disables directory listing in Apache. When someone tries to visit a folder URL without specifying a file, instead of seeing a list of files in that directory, they'll get a forbidden error. It's a common security practice to hide what files are on your server.

htpasswd​

File password​

AuthName "File access restriction"
AuthType Basic
AuthUserFile /home/your_account/.htpasswd
<Files "confidential.md">
Require valid-user
</Files>

This htaccess configuration sets up HTTP Basic Authentication (a username/password login prompt) for a specific file called confidential.md. It tells the web server to look up user credentials from a password file and only allow authenticated users to access that file.

Folder password​

Place these lines in a file called .htaccess in the folder to protect (e.g. folder_name):

AuthType Basic
AuthName "This folder is protected"
AuthUserFile /home/your_account/folder_name/.htpasswd
Require valid-user

This snippet configures HTTP Basic Authentication for a folder, requiring visitors to log in with a username and password before accessing any files inside. It tells the web server to prompt for credentials, names the protected area, points to a password file, and ensures only authenticated users can proceed.

Whitelist - Disallow access to all files except the ones mentioned​

# prevent accessing to all files excepted those mentioned (case sensitive!)
<FilesMatch "(?<!\.png|\.jpe?g|\.gif|\.svg|\.icon?)$">
# Apache 2.2
# deny from all
# Apache 2.4
# Require all denied
</FilesMatch>

This Apache configuration file blocks access to all files except image files with specific extensions (.png, .jpg, .jpeg, .gif, .svg, .ico). The actual denial rules are commented out, showing examples for both older Apache 2.2 syntax and newer Apache 2.4 syntax.

Redirect​

Redirect an entire site​

Redirect 301 / https://www.newsite.com/

This .htaccess rule tells the web server to permanently redirect all traffic from the current site to a new domain. Anyone visiting the old site will be automatically sent to the new location, and search engines will update their records accordingly.

Permanent redirection​

RedirectPermanent /old.php http://www.yoursite.com/new.php

This rule tells the web server to permanently redirect any request for /old.php to a new URL at http://www.yoursite.com/new.php. Browsers and search engines will remember this redirect and update their bookmarks or records accordingly.

Temporary redirection​

Redirect 301 /old.php http://www.yoursite.com/new.php

This line tells the web server to permanently redirect any requests for /old.php to a new location at http://www.yoursite.com/new.php. The 301 status code signals to browsers and search engines that this is a permanent move, so they'll update their records accordingly.

Redirect a subfolder​

For instance, redirect /category/apple.php to apple.php

RedirectMatch 301 ^/category/(.*)$ /$1

This rule tells the web server to permanently redirect any URL that starts with '/category/' to the same path without that '/category/' prefix. For example, a request to '/category/products' would redirect to '/products'. The '301' status code tells browsers and search engines this is a permanent move, so they should update their bookmarks and search indexes accordingly.

or solve spelling issues by e.g. redirect every request to the fruit folder to the plural form.

RedirectMatch 301 ^/fruit/(.*)$ /fruits/$1

This rule tells the web server to permanently redirect any request starting with '/fruit/' to '/fruits/' instead, keeping whatever comes after 'fruit/' in the URL. It's useful when you've renamed a folder or want to send traffic from an old URL path to a new one.

Another example: redirecting URLs from /archive/2020/... to /2020/....

RewriteRule ^archive/2020/(.*)$ /2020/$1 [R=301,NC,L]

This rule redirects any URL starting with 'archive/2020/' to just '2020/', keeping whatever comes after the date intact. It's a permanent redirect (301) that helps maintain SEO value when you reorganize your site's folder structure.

Search engine​

Disallow indexing​

Put these lines in e.g. yoursite/administrator to inform search engines that you don't allow them to index files in that folder (and sub-folders).

# Be sure that pages under this folder won't be indexed
<IfModule mod_headers.c>
Header set X-Robots-Tag "noindex, nofollow"
</IfModule>

This configuration tells search engine crawlers not to index or follow links on pages in this directory. It does this by adding a special HTTP header that search engines check before deciding whether to catalog your content.


Was this article helpful?
Did you try the steps in this article?