Simon Bragg of website and design agency Sibra gave a talk on Monday about the .htaccess file. This file is found in the root directory of websites running on Apache web servers (so check what server software you are using if the htaccess is missing). It controls access to pages on the site, handles redirects and can be used for security and optimisations.
You can see Simon’s slides here:
Here’s a written version of the presentation:
What to shove up your .htaccess
Simon Bragg
Cambridge WordPress Meetup August 2018
The .htaccess file
.htaccess files enable:
- Configuration changes to directory and sub-directory;
- Without accessing httpd.conf,
- Usually allowed;
- Short commands:
- key value pair.
If you screw it up syntax, you get:
Error 500 internal server error
What you can do
- Browser caching
- gzip compression for file transfer
- Keep alive
- Regex for redirects
- Security enhancements
Browser caching
## EXPIRES CACHING ## <IfModule mod_expires.c> ExpiresActive On ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType text/css "access plus 1 year" ExpiresByType application/pdf "access plus 1 month" ExpiresByType application/x-shockwave-flash "access plus 1 month" ExpiresByType image/x-icon "access plus 1 year" ExpiresByType text/javascript "access 1 month" ExpiresByType text/x-javascript "access 1 month" ExpiresByType application/javascript "access 1 month" ExpiresByType application/x-javascript "access 1 month" ExpiresByType application/json "access 1 month" ExpiresByType application/vnd.ms-fontobject "access plus 1 year" ExpiresByType application/x-font-ttf "access plus 1 year" ExpiresByType application/x-font-opentype "access plus 1 year" ExpiresByType application/x-font-woff "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" ExpiresDefault "access plus 2 days" </IfModule> ## EXPIRES CACHING ##
This can make a dramatic difference:
Compress transfer: gzip
Place this code AFTER WordPress stuff:
<IfModule mod_filter.c> AddOutputFilterByType DEFLATE "application/atom+xml" \ "application/javascript" \ "application/json" \ "application/ld+json" \ "application/manifest+json" \ "application/rdf+xml" \ "application/rss+xml" \ "application/schema+json" \ "application/vnd.geo+json" \ "application/vnd.ms-fontobject" \ "application/x-font-ttf" \ "application/x-javascript" \ "application/x-web-app-manifest+json" \ "application/xhtml+xml" \ "application/xml" \ "font/eot" \ "font/opentype" \ "image/bmp" \ "image/svg+xml" \ "image/vnd.microsoft.icon" \ "image/x-icon" \ "text/cache-manifest" \ "text/css" \ "text/html" \ "text/javascript" \ "text/plain" \ "text/vcard" \ "text/vnd.rim.location.xloc" \ "text/vtt" \ "text/x-component" \ "text/x-cros
Keep alive, if allowed by host
At end of .htaccess file
## KEEP ALIVE ## <ifModule mod_headers.c> Header set Connection keep-alive </ifModule> ## END ENABLE KEEP ALIVE ##
But cheapo host doesn’t allow this.
RedirectMatch for Regex redirects
Have mod_rewrite.c enabled for #Begin WordPress stuff.
So can use Regex to redirect multiple pages in one line. Some Examples:
Perhaps for tweaking URL structure:
.* means anything, (.*) means whatever, and repeat in $1
RedirectMatch 301 .*/employment/employee-shares/(.*) http://www.website.co.uk/employee-shares/$1
^ means start of string, (/D) means 1 non digit character.
RedirectMatch 301 ^/share(\D)options$ http://www.website.co.uk/employee-shares/
Use of OR for multiple redirects to one page:
RedirectMatch 301 ((/introducing-thepod/) |(/products/pod/) |(/about-us/the-vision/) |(/cambridgepod/)) https://website.co.uk/pod/
http to https
When have http site and converting to https, add in bold
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{HTTPS} !=on RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # BEGIN WordPress RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule>
Bits of code
1. Protect important files, deny access to them:
<FilesMatch "^.*(error_log|wp config\.php|php.ini|\.[hH][tT][aApP].*)$"> Order deny,allow Deny from all </FilesMatch>
Check php.ini, is php.ini
2. Prevent directory browsing /wp-content/uploads/
Options All –Indexes
3. Block unauthorized execution of PHP files.
Most hackers upload backdoors to /uploads folder
<Directory "/var/www/wp-content/uploads/"> <Files "*.php"> Order Deny,Allow Deny from All </Files> </Directory>
4. Protect against Script injections
Hackers change WordPress GLOBALS & REQUEST variables, so:
Options +FollowSymLinks RewriteEngine On RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR] RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR] RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2}) RewriteRule ^(.*)$ index.php [F,L]
5. Secure wp-includes directory
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^wp-admin/includes/ - [F,L] RewriteRule !^wp-includes/ - [S=3] RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] RewriteRule ^wp-includes/theme-compat/ - [F,L] </IfModule>
6. Prevent username enumeration
Visitor who enters your-site.com/?author=1 finds username. One less thing to guess. Just needs the password. So:
RewriteCond %{QUERY_STRING} author=d RewriteRule ^ /? [L,R=301]
7. Prevent hot linking
Most hackers upload backdoors to /uploads folder
RewriteEngine On RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://(www\.)?your-site.com/.*$ [NC] RewriteRule \.(gif|jpg)$ http://www.your-site.com/hotlink.gif [R,L]Directory "/var/www/wpcontent/uploads/">
And replace http://www.your-site.com/hotlink.gif with image url you want to protect
xmlrpc.php blocking?
Xmlrpc : remote procedure call using XML to encode, and http for transport
Enables you to:
Post using weblog clients e.g. Windows Live Writer, IFTTT
Was a security concern, although not any more.
If want to block:
# Block WordPress xmlrpc.php requests <Files xmlrpc.php> order deny,allow deny from all allow from 123.123.123.123 </Files>
Thanks to Simon for his excellent talk!