Some tips and tricks for your .htaccess file (Apache)
- Table of Contents
- CSP - Content Security Policy
- Files
- Force
- Misc
- Optimization
- Protection
- Redirect
- Search engine
- License
Be inspired by the following lines:
<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>
Refuse requests to these files:
<FilesMatch "(file_1\.gif|file_2\.png)">
Order Allow,Deny
Deny from all
</FilesMatch>
Blocks access to all files except those whose extension is mentioned in the list below:
Option 1
RewriteCond %{REQUEST_FILENAME} !(.*)\.(bmp|css|eot|html?|icon?|jpe?g|js|gif|pdf|png|svg|te?xt|ttf|webp|woff2?|xml|zip)$
RewriteRule . - [F]
Option 2
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]
Block access to hidden files & directories
Don't allow to access to a file or folder when the name start with a dot (i.e. a hidden file / folder):
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
Don't allow the browser to download such files but tell him how to display them (text in the example):
<FilesMatch "\.(tex|log|aux)$">
Header set Content-Type text/plain
</FilesMatch>
For instance, force download for pdf files:
<FilesMatch "\.(pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
When implemented in your .htaccess, try to get access to
yoursite.com
orhttp:https://yoursite.com
should redirect tohttps://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 mentionned
# 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>
Don't show errors (just like a error_reporting=E_NONE
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>
Show errors (just like a error_reporting=E_ALL
does).
Only use this on a development server otherwise you'll expose sensitive information to your visitor.
<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>
Redirect every requests done to your site to a specific page (called maintenance.php
here below). Just think to replace the code ADD_YOUR_IP_HERE
by your current IP adress.
<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>
<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 somes hosters, 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>
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>
## Apache 2.2
Deny from all
## Apache 2.4
# Require all denied
Just replace xxx.xxx.xxx.xxx
by your IP adress.
## 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
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>
<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>
Put these lines in f.i. /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>
Don't allow the webserver to provide the list of files / folders like a dir
does.
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
.htpasswd
generator : http:https://aspirine.org/htpasswd.html
AuthName "File access restriction"
AuthType Basic
AuthUserFile /home/your_account/.htpasswd
<Files "confidential.md">
Require valid-user
</Files>
Place these lines in a file called .htaccess
in the folder to protect (f.i. folder_name
):
AuthType Basic
AuthName "This folder is protected"
AuthUserFile /home/your_account/folder_name/.htpasswd
Require valid-user
# 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>
Redirect 301 / https://www.newsite.com/
RedirectPermanent /old.php http:https://www.yoursite.com/new.php
Redirect 301 /old.php http:https://www.yoursite.com/new.php
For instance, redirect /category/apple.php
to apple.php
RedirectMatch 301 ^/category/(.*)$ /$1
or solve spelling issue by f.i. redirect every requests to the fruit
folder to the plural form.
RedirectMatch 301 ^/fruit/(.*)$ /fruits/$1
Another example: redirecting URLs from /archive/2020/...
to /2020/...
.
RewriteRule ^archive/2020/(.*)$ /2020/$1 [R=301,NC,L]
Put these lines in f.i. yoursite/administrator
to inform search engines that you don't allow him 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, "
</IfModule>