.htaccess rules you can add to your site

Activate your RewriteEngine

In all of these I usually have put the rewrite engine line. But just in case, all rewrite rules need this before them:

RewriteEngine On
RewriteBase /

The RewriteBase refers to where the site lives. If it’s in the docs folder, you need to use RewriteBase /docs.

Use only www

For those of you who love www:

<IfModule mod_rewrite.c>
# Redirect all users to access the site with the 'www.' prefix
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} !\.([0-9a-zA-Z-]+\.[a-z]{2,10})$ [NC]
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

</IfModule>

If your site uses https, modify the last line accordingly.

Never use www

For those of you who hate www:

<IfModule mod_rewrite.c>
# Redirect all users that go to www, to the site without www
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} !\.([0-9a-zA-Z-]+\.[a-z]{2,10})$ [NC]
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

</IfModule>

If your site uses https, modify the last line accordingly.

Redirect to https

Simple way to redirect your site to https:

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Basic things

Set your directory index. These are the files that the server looks for if it’s not given a file, such as https://example.com/. Note that the order matters.

DirectoryIndex index.php index.html

Set the time zone

SetEnv TZ America/Denver

Set the server admin email. Good so others can report issues. Note that this email will likely get lots of spam.

SetEnv SERVER_ADMIN webmaster@example.com

Improve your security

Block access to .htaccess

Apache 2.2

# Block access to .htaccess
<FilesMatch "^\.ht">
  Order allow,deny
  Deny from all
  satisfy all
</FilesMatch>

Apache 2.4

# Block access to .htaccess
<Files ".ht*">
  Require all denied
</Files>

Disable indexes.

Otherwise the server may list the files, allowing someone to see all your files

IndexIgnore *
Options -Indexes -MultiViews +FollowSymLinks

Turn off signature

Your server’s version number can give a hacker a lot of info.

ServerSignature Off

Block certain requests

Block these request methods, as they are usually not used

RewriteCond %{REQUEST_METHOD} ^(TRACE|DELETE|TRACK|DEBUG) [NC]
RewriteRule ^(.*)$ - [F,L]

Block access attempts that look too much like hacks. Note, this may break your site. Also, some are commented out because they gave me trouble, but you can uncomment them out.

RewriteCond %{HTTP_USER_AGENT} (%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{THE_REQUEST} (\?|\*|%2a)+(%20+|\\s+|%20+\\s+|\\s+%20+|\\s+%20+\\s+)HTTP(:/|/) [NC,OR]
RewriteCond %{THE_REQUEST} \?\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} \/\*\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} etc/passwd [NC,OR]
RewriteCond %{THE_REQUEST} cgi-bin [NC,OR]
RewriteCond %{THE_REQUEST} (%0A|%0D|\\r|\\n) [NC,OR]
RewriteCond %{REQUEST_URI} owssvr\.dll [NC,OR]
#RewriteCond %{HTTP_REFERER} (%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
#RewriteCond %{HTTP_REFERER} \.opendirviewer\. [NC,OR]
#RewriteCond %{HTTP_REFERER} users\.skynet\.be.* [NC,OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [NC,OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [NC,OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC,OR]
RewriteCond %{QUERY_STRING} \=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [NC,OR]
RewriteCond %{QUERY_STRING} (\.\./|%2e%2e%2f|%2e%2e/|\.\.%2f|%2e\.%2f|%2e\./|\.%2e%2f|\.%2e/) [NC,OR]
RewriteCond %{QUERY_STRING} ftp\: [NC,OR]
RewriteCond %{QUERY_STRING} http\: [NC,OR]
RewriteCond %{QUERY_STRING} https\: [NC,OR]
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)/self/(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)cPath=http://(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*embed.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^e]*e)+mbed.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*object.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^o]*o)+bject.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*iframe.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^i]*i)+frame.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
RewriteCond %{QUERY_STRING} base64_(en|de)code[^(]*\([^)]*\) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} ^.*(\(|\)|<|>|%3c|%3e).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(\x00|\x04|\x08|\x0d|\x1b|\x20|\x3c|\x3e|\x7f).* [NC,OR]
RewriteCond %{QUERY_STRING} (NULL|OUTFILE|LOAD_FILE) [OR]
RewriteCond %{QUERY_STRING} (\.{1,}/)+(motd|etc|bin) [NC,OR]
RewriteCond %{QUERY_STRING} (localhost|loopback|127\.0\.0\.1) [NC,OR]
RewriteCond %{QUERY_STRING} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{QUERY_STRING} concat[^\(]*\( [NC,OR]
RewriteCond %{QUERY_STRING} union([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} union([^a]*a)+ll([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} \-[sdcr].*(allow_url_include|allow_url_fopen|safe_mode|disable_functions|auto_prepend_file) [NC,OR]
RewriteCond %{QUERY_STRING} (;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode) [NC,OR]
RewriteCond %{QUERY_STRING} (sp_executesql) [NC,OR]
RewriteCond %{QUERY_STRING} ^.*\.(bash|git|hg|log|svn|swp|cvs) [NC,OR]
RewriteCond %{QUERY_STRING} boot\.ini [NC,OR]
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|%3D) [NC,OR]
#RewriteCond %{QUERY_STRING} ^.*(\[|\]|\(|\)|<|>|ê|"|;|\?|\=$).*  [NC,OR]
#RewriteCond %{QUERY_STRING} ^.*(\(|\)|<|>|ê|;|\?|\*).*  [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(<|>|ê|;).*  [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(&#x22;|&#x27;|&#x3C;|&#x3E;|&#x5C;|&#x7B;|&#x7C;).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(%24&x).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(127\.0).* [NC,OR]
#RewriteCond %{QUERY_STRING} ^.*(%0|%A|%B|%C|%D|%E|%F).* [NC,OR]
#RewriteCond %{QUERY_STRING} ^.*(globals|encode|localhost|loopback).* [NC,OR]
#RewriteCond %{QUERY_STRING} ^.*(request|select|concat|insert|union|declare).* [NC]
RewriteCond %{THE_REQUEST} \.\.\/ [NC,OR]
RewriteCond %{QUERY_STRING} \.\.\/ [NC]
RewriteCond %{QUERY_STRING} !.*ckgedit/fckeditor/editor.* [NC]
RewriteRule ^(.*)$ - [F,L]

Deny Browser access to these files. Works on all versions of Apache.

# php and log files
<FilesMatch "^(php\.ini|php5\.ini|php_errors|php_errors\.log|errors|error_log|logs|errordocs)">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</FilesMatch>

# wp-config.php, bb-config.php, license.txt, php5.ini, readme.html, install.php
<FilesMatch "^(wp-config\.php|license\.txt|license|readme\.html|bb-config\.php|install\.php)">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</FilesMatch>

# readme files
<Files ~ "^([\._]ht|README$|VERSION$|COPYING$)">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</Files>

Restricting access

Block ip addresses

When that certain someone keeps hammering your system.

Apache 2.2:

# ipv4
deny from xxx.yyy.zzz.pii
# ipv6
deny from 2604:2000:400:xxx::yyyy:zzz

Apache 2.4:

Require all granted
Require not ip xxx.yyy.zzz.pii

Caching rules

Caching helps to speed up your site, by telling the client not to fetch the file for a certain amount of time.

# BEGIN Compress text files
<ifModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/xml text/css text/plain
AddOutputFilterByType DEFLATE image/svg+xml application/xhtml+xml application/xml
AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript application/json
AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
AddOutputFilterByType DEFLATE font/truetype font/opentype
</ifModule>
# END Compress text files

# BEGIN Expire headers
<ifModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 30 seconds"
ExpiresByType image/x-icon  	"access plus 2592000 seconds"
ExpiresByType image/jpeg 		"access plus 2592000 seconds"
ExpiresByType image/jpg			"access plus 2592000 seconds"
ExpiresByType image/png 		"access plus 2592000 seconds"
ExpiresByType image/gif 		"access plus 2592000 seconds"
ExpiresByType image/svg 		"access plus 2592000 seconds"
ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"
ExpiresByType text/css "access plus 604800 seconds"
ExpiresByType application/x-woff "access plus 604800 seconds"
ExpiresByType application/woff "access plus 604800 seconds"
ExpiresByType text/javascript "access plus 216000 seconds"
ExpiresByType application/javascript "access plus 216000 seconds"
ExpiresByType application/x-javascript "access plus 216000 seconds"
ExpiresByType text/html "access plus 600 seconds"
ExpiresByType application/xhtml+xml "access plus 600 seconds"
</ifModule>
# END Expire headers

# BEGIN Cache-Control Headers
<ifModule mod_headers.c>
<filesMatch "\.(ico|pdf|flv|jpe?g|png|gif|swf|svg)$">
Header set Cache-Control "private"
# due to being a private blog
# Header set Cache-Control "max-age=2592000, public"
</filesMatch>
<filesMatch "\.(css|woff)$">
Header set Cache-Control "public"
</filesMatch>
<filesMatch "\.(js)$">
Header set Cache-Control "private"
</filesMatch>
<filesMatch "\.(x?html?|php)$">
Header set Cache-Control "private, must-revalidate"
</filesMatch>
</ifModule>
# END Cache-Control Headers

Error pages

You can set your own error pages, personalizing them and making them more useful.

ErrorDocument 400 /error/bad-request
ErrorDocument 401 /error/forbidden
ErrorDocument 403 /error/forbidden
ErrorDocument 404 /error/notfound
ErrorDocument 406 /error/bad-request
ErrorDocument 409 /error/internal-server-error
ErrorDocument 410 /error/browser-error
ErrorDocument 500 /error/internal-server-error
ErrorDocument 502 /error/internal-server-error
ErrorDocument 503 /error/internal-server-error
ErrorDocument 504 /error/internal-server-error

You can read more here.