.htaccess tweaks for security and performance

Hi guys, since we now have WordPress integration in Blocs 4 Plus, I thought it would be a good idea to share my personal tweaks I add to each .htaccess to improve security and performance. This assumes your WordPress has been installed with your primary address using https://.

Security - main .htaccess
In the main .htaccess within the root of your WordPress site, I suggest adding these snippets:

#Enable HSTS
Header set Strict-Transport-Security "max-age=31536000" env=HTTPS

#hide htaccess file
<Files .htaccess>
order allow,deny
deny from all
</Files>

#hide wp-config file
<files wp-config.php>
order allow,deny
deny from all
</files>

#Block wp-includes folder and files
<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>

#Block track & trace to prevent XSS attacks
RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]

#Stop username enumeration
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} author=\d
RewriteRule ^ /? [L,R=301]

#Blocking xml-rpc requests
#If you do not use the XML-RPC protocol (used by for example the WordPress apps on iOS and Android) you can improve security by blocking these requests.
RewriteRule ^xmlrpc.php$ "http://0.0.0.0/" [R=301,L]

Security - htaccess in wp-content/uploads
The rules below are destined for an .htaccess file you’d add in wp-content/uploads

#Prevent php execution
<Files *.php>
deny from all
</Files>

#Limit accessible files to certain filetypes
order deny,allow
deny from all
<files ~ ".(xml|css|jpe?g|png|gif|js)$">
allow from all
</files>

Performance - Main .htaccess
The snippets below are used to improve performance of your WordPress site. Some do imply that GZIP is active on your server.

#BEGIN Cache-Control Headers
<ifModule mod_headers.c>
<filesMatch "\.(ico|jpeg|jpg|png|gif|swf|pdf|svg|js|flv)$">
Header set Cache-Control "max-age=84600, public"
</filesMatch>
</ifModule>
#END Cache-Control Headers


#BEGIN Expire headers
<IfModule mod_expires.c>
  ExpiresActive On
 
  #Images
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/x-icon "access plus 1 year"
 
  #Video
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/mpeg "access plus 1 year"
 
  #CSS, JavaScript
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
 
  # Others
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType application/x-shockwave-flash "access plus 1 month"
</IfModule>
#END Expire headers

#START DEFLATE COMPRESSION
<IfModule mod_deflate.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" \
"application/x-font-opentype" \
"application/x-font-otf" \
"application/x-font-truetype" \
"application/x-font-ttf" \
"application/x-javascript" \
"application/x-web-app-manifest+json" \
"application/xhtml+xml" \
"application/xml" \
"font/eot" \
"font/otf" \
"font/ttf" \
"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-cross-domain-policy" \
"text/xml"
</IfModule>
#DEFLATE COMPRESSION

#Disable Entity Tags
#Entity Tags are a mechanism that allows to check if elements in the browser cache correspond to the version on the server. By disabling these Entity Tags a faster loading speed can sometimes be achieved. (The actual impact varies depending on the site). 
Header unset ETag
FileETag None
11 Likes

Thank you that is really useful. Would this drive all traffic to https:// ?

I’d have to test this because I don’t know how those speed tweaks would play with my LiteSpeed server.

I think it’s also worth pointing out to users that HSTS is a one way street. I enable it on my own sites, but I’ve not been doing it until now on client sites.

1 Like

Actually my list assumes you’ve already set up WordPress with https://yourdomain.extension as the primary address. This would cause WordPress itself to perform the redirect from http to https in case a vistor would surf towards http://yourdomain.extension instead of https://yourdomain.extension.

If not, you can easily solve this on a fresh install by going to “Settings” => “General” and alter the urls there. Alternatively you can also do this in the database, specifically in the options table, by editing the fields “siteurl” and “home”.

If content has already been entered, I suggest exporting your database via PhpMyAdmin (or similar tools) to a .sql file and changing the primary address using https://wpdomainchanger.com

I have this piece of code that has always worked on Blocs sites without WP. It drives everything to https:// whether it comes from http, www or any other combination.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.(.)
RewriteRule ^.
$ https://%1/$1 [R=301,L]

RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

HSTS preload would require all traffic going https but I also like to eliminate https://www

1 Like

Just wondering if your performance section should include a keep alive command like detailed here

Thanks for sharing @brechtryckaert

htaccess is one of those things I just copy and paste with. I really don’t know much about it. Do you do anything special for static sites at all?

@Flashman currently it does not, but I’ll look into that.

@PeteSharp actually every single snippet for performance can also be applied to static sites, as the goal of these snippets is to improve the handling of the HTML output rendered by WordPress. So in essence you’d be able to apply them to any static site or any cms, since it deals with the rendered output.

2 Likes

Cool, thanks @brechtryckaert

1 Like

I have just applied these htaccess changes for the security to a test site and all seemed to be good from the minimal testing I did.

I hesitated on the performance tweaks, because the server is running LiteSpeed and I think there might be some conflicts, though I would need to investigate more. The expire headers would be OK, but I wondered about the default compression section.

This was also the first time I have tried a WP site with PHP 8 so it was a relief to see everything seemed to work. In the server PHP settings it curiously no longer lists either zip or brotli as options, so I want to check with my web host about that.

1 Like

Hope you’re well @Flashman and Guru is still good for you. I found that switching to PHP 8.0 broke my contact form so I had to revert back to PHP 7.4

The Reply To email part was broken and would show up as from ‘noreply@example.com Reply To example@example.com

So when you clicked reply on the email, it opens a new email message up with the to part filled in as ‘noreply@example.com Reply To example@example.com’ rather than ‘example@example.com’

Just in case anyone else comes across this as it took me a while to figure it out!

@Brocky120 We should probably post this under a different thread. Was this on a Wordpress site that you had the problem with PHP 8? I hadn’t tried the contact form.

One issue found with PHP 8 came with Volt, because Guru have not yet enabled zip inside the panel settings, so there are no backups being saved and they advised that it would be better going back to 7.4 until PHP 8 is has been around for longer.

Yesterday after switching to PHP 8 on my shared hosting account I found that mail was working fine on a site built with Blocs 3, so that would presumably be the case as well on Blocs 4. I did have a problem though with an old site built with Rapidweaver and Foundation where PHP 8 broke the contact form. I did wonder about what might happen with an old Blocs 2 website done for a client.

All fine with Guru generally and reliable as ever.

No it wasn’t a Wordpess site actually, just a normal one. I think it came out around November so still fairly new, I will stick with 7.4 for the time being until it becomes more adapted.

Yeah the contact form is what broke for me, switching back to 7.4 was definitely the fix so not sure what happened there.

Happy Guru is still good for you. Anyway we can get this one back on topic… :sweat_smile:

@brechtryckaert

I am making use of your htaccess above with a few minor changes. After adding the performance section #START DEFLATE COMPRESSION should I then add those items like fonts inside cPanel to the list of specified MIME types? Recently I read that if a MIME type is listed in the htaccess it should be disabled in cPanel. What is the right way to do this?

I once read it is not good to choose compress all, because that will include image files like Jpeg that are already compressed and just slow down the page loading.

I’m not used to work with Cpanel, but normally if GZIP (mod_deflate) is already active on the server, the rules of the .htaccess entry should suffice.

From your screenshot I assume that this really does something similar (e.g. setting what needs to be compressed, as currently defined in the .htaccess). So I think adding those here will set duplicate instructions. But I could be mistaken due to lack of experience with Cpanel.

Thank you, this is a LiteSpeed server and I just ran a test for GZIP compression, which indicated it was enabled with brotli, so I disabled it in cPanel and ran it again. It still came back as being active, so it looks like your htaccess is doing the job, but covering more MIME types. Good job :grinning:

1 Like