Here are some rules you can add to your .htaccess file. They cover redirection, security, caching, and error pages.

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 [email protected]

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

Note, this may break your site. If that happens, simply comment out each one by putting a # in front of it, save it, and try your site again.

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 %{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 about Custom Error Pages and Error Codes with Apache and NGINX.