How to speed up WordPress


This guide includes 9 tips to show you how to take just about any WordPress site to a maximum 1-2 seconds load time. It receives regular updates and was extended in the first quarter of 2020 to include a couple extra bonus tips.

If you’ve selected a hosting partner and resources (theme, plugins, etc) that are built from the ground up for speed and to expect to work well with large quantities of traffic (many are not), then you can reach fractions of a second load times per page, your cached site should easily handle spikes of more than 500 simultaneous visitors, and 20k or more daily page views should be no problem. This guide will also help you get top marks on synthetic benchmarks like GTMetrix and Google Page Speed.

Want to get 80% of this guide done for you? Switch your WordPress hosting to Websavers, activate a caching plugin and image optimization plugin, and check a few boxes in Plesk, and your site will perform like never before!

Read on to get started!

This guide will not:

  • Help you fix problems with your hosting provider or their poor performance. Using a high quality WordPress host makes a huge difference.
  • Help you with issues with plugins or themes that could be heavily slowing things down or contributing to poor synthetic benchmark scores. For example many plugins and themes make external resource calls that you cannot control, which will inherently bring down the speed (and benchmark scores) for your site. Similarly many plugins and themes use poor coding practices that require heavy data processing unnecessarily. Therefore you may need to disable parts of your website (theme, plugins, etc) in order for it to play nicely with your caching plugin. If you’re using themes and plugins that optimize for performance, then you’ll find that enabling caching will work perfectly without much fiddling.
  • Allow you to have your cake and eat it too: you may need to accept a loss in functionality or quality in favour of better performance and top marks on synthetic benchmarks like Google PageSpeed Insights. If you love exceptionally high quality images that are each 1MB+ or not-sufficiently-compressed video files, then you’re never going to get top marks on website bench-marking utilities. Similarly if you’ve got a part of your site that loads dynamic content on every page load, you may need to turn that functionality off or find another way to load it for optimal performance.

If you’re experiencing a problem where a page takes longer than 10 seconds before it even begins loading content, there’s often an underlying problem with WordPress or the hosting configuration that you must resolve before turning to caching for a solution. Click here for help troubleshooting issues with suddenly very slow loading pages.

If you are comfortable with all of the instructions in this article, then you should do them all to improve the performance of your website. If you’re a basic user, it’s also OK to only complete the sections you’re comfortable with. You’ll still see a performance improvement, just not as much as when you are able to complete all options.

Some parts will provide massive load time improvements while others will be negligible. Some parts will make a synthetic speed test like Google’s PageSpeed or GTMetrix speed test think highly of your site but may not create a difference in real-world load times. Such is the nature of synthetic bench-marking tools.

Without further ado, here’s 9 things you can do to dramatically improve your website performance:

1. Use a web host that cares about performance

If your web hosting provider loads down their servers with thousands of websites or doesn’t know how to get the most out of their servers, then it’s inevitable that you’ll encounter frequent slowdowns.

If you’re on shared hosting, be sure your host artificially limits the total number of sites per server to ensure your site will have the resources it needs to perform well. If you have your own VPS, ask your host how it’s configured and what parts of the configuration are designed to ensure top performance. If their answer doesn’t include things like SSD storage, nginx or lightspeed web server, and custom optimized configurations to serve static cache files, then it’s time to find a better WordPress web host.

If you’re very concerned about performance then it’s recommended to obtain a VPS of your own to ensure your site has dedicated resources. We often recommend trying out our underloaded shared hosting first and if you find you need even greater performance, you can always move on up to a VPS.

2. Use the Latest PHP Verison

You can take a look at some of the metrics here. If you can use PHP 8.1 or higher, it will provide a 30-70% improvement in performance over earlier versions of PHP. But take note: you will not notice this improvement on cached front-end pages. This performance improvement will be seen in page load times when your caching plugin first caches the page and when you’re traversing the WordPress admin area.

That sounds great… but how do I use it?

  • First, log into Plesk
  • Under the domain in question, go to “PHP Settings”
  • There will be two drop downs at the top of the page; one for PHP Version, one for the PHP Handler. For the version, try the latest version, then if you encounter issues repeatedly step down a version until the site works. If you do encounter problems with the latest release of PHP, ultimately your best bet will be finding the source of the issue and fixing it, like updating an incompatible plugin or theme, however we recognize that this isn’t always possible.
  • Scroll to the bottom and click “OK” or “Apply”

3. Enable Brotli or Gzip Compression

Are you using Websavers’ shared hosting or a VPS with Hands-On Support? If so, we’ve already enabled Brotli (with gzip fallback) compression for you! Not experiencing the Websavers difference yet? Hopefully we’ll see you soon!

If you don’t host with us, most caching plugins take care of this for you, however if yours does not, add these values to your .htaccess file to ad GZIP compression on your website:

<IfModule mod_deflate.c>
# Compress HTML, CSS, JavaScript, Text, XML and fonts
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml

This tells Apache to compress all compatible content before sending it – saving time and bandwidth!

Using Nginx only? Use this in your Nginx directives to enable brotli with gzip fallback:

brotli on;
brotli_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

gzip on;
gzip_disable "MSIE [1-6]\\.(?!.*SV1)";
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon image/bmp image/svg+xml;
gzip_vary on;

4. Enable Long Expiry for Browser Cache

First it’s important to understand what exactly browser caching is. Ever have someone tell you to ‘clear your cache’ on your browser? That’s because modern browsers are configured to download copies of resources (HTML/CSS/JS/Images) to speed up page speeds on subsequent visits. By default, though, some of these items are set to ‘expire’ very quickly – which doesn’t help with speed much!

In a later section we’ll talk about using a Caching Plugin for WordPress. If you’re using strictly apache web server, all of the caching plugins will also enable long expiry values for browser caching for you.

This is not recommended if you’re still developing your site. Please only apply this config when the site is live.

That said, if your site is live and you want to squeeze a bit more performance out of it, you can configure your server to tell visitors how long to hold on to certain resources.

These directives are sent by the server to instruct the browser of every visitor how long to ‘hold on’ to various file types.

Using apache web server?

Add the following to your .htaccess file.

<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 month" 
ExpiresByType text/html "access plus 1 week" 
ExpiresByType application/x-compressed "access plus 1 week" ExpiresByType application/x-gzip "access plus 1 week" 
ExpiresByType application/pdf "access plus 1 month" 
ExpiresByType text/x-javascript "access plus 1 month" 
ExpiresByType application/x-javascript "access plus 1 month" ExpiresByType application/x-shockwave-flash "access plus 1 month" ExpiresByType image/x-icon "access plus 1 year" 
ExpiresDefault "access plus 1 week" 

Using nginx with Plesk

  • Log in to Plesk and go to “Apache & nginx settings” under the domain you’re optimizing
  • Under Expires > Enter custom value enter 365 days. and check the box: response with Expires headers for static files only
  • Check the boxes beside Smart static files processing and Serve static files directly by nginx

In the event the “Serve static files directly by nginx” option does not have all of these static file extensions listed (either with spaces or | between them), paste this in the provided box:


Now Click Apply or OK to save your changes.

Using barebones nginx

Add this to your nginx config:

location ~* \.(css|js)$ {
        gzip_vary on;
        expires 30d;
location ~* \.(ac3|atom|avi|bmp|bz2|css|csv|cue|dat|doc|docx|dts|eot|exe|flv|gif|gz|htm|html|ico|img|iso|jpeg|jpg|js|map|mid|midi|mkv|mp3|mp4|mpeg|mpg|m4a|ogg|ogv|otf|pdf|png|ppt|pptx|qt|rar|rm|rss|rtf|svg|svgz|swf|tar|tgz|ttf|txt|wav|webp|woff|woff2|xls|xlsx|zip)$ {      
        gzip_vary on;
        expires max;  
        log_not_found off;

5. Use a Server-Side Static Cache Generator

In our testing the best commercial option for this is WP Rocket and the best free option for this is WP Super Cache. Both will create static cache files for each of your site’s pages and posts and both will work exceptionally well with our custom nginx configuration that auto-detects the static files and serves those without the need to launch a heavier Apache and/or PHP process. The advantages we’ve found to paying for WP Rocket over free caching plugins are almost exclusively in the website code minification and optimization part of things. See part 6 of this guide below for more info on that.

We use an in-house built custom nginx configuration on all of our servers to auto-detect the use of the most common server-side page caching plugins and automatically serve their static html files directly with nginx without having to funnel through to apache or the PHP processor, thus reducing CPU and memory usage. Our systems can auto-detect and serve static cache output from WP Rocket, WP Super Cache, WP Fastest Cache, W3 Total Cache, and WP Optimize. Sadly, Hummingbird cannot work with our systems as they use randomly generated static cache file names. If you wish to use Hummingbird, ask WPMUDEV support to fix their cache filenames.

These static cache generating plugins can allow your site to handle massive spikes in traffic without breaking a sweat even on our shared hosting services!

We’ve used these configurations to bring a site on shared hosting to around 100k daily unique views without any problems, and a site on its own VPS to over 500k daily unique page views at an average load of 1.0.

If your host can’t keep up, check out our Canadian WordPress hosting options. Even better, with the optimizations described in this step, step 6 (optimizing), and step 7 (image compression), your site can go from a failing, or otherwise low, Google Page Speed score to a 90+, depending upon the theme and plugins used.

Here’s how to get static cache output from our two favourite caching plugins:

  • WP Rocket: Here’s how to install and activate WP Rocket using their guide. Once activated, it will automatically begin creating a static cache.
  • WP Super Cache: log in to your WordPress installation and go to Plugins > Add New. Install WP Super Cache and activate it. Head to Settings > WP Super Cache and select “Turn Caching On” and press the Update Status button. Click on the Advanced tab. Check off everything with “Recommended” next to it. Press the Update Status button. Scroll down to the “Expiry Time & Garbage Collection” section and set the interval to either once or twice daily, then press the “Change Expiration” button.

Some people prefer W3 Total Cache and others like WP Fastest Cache, among many other caching plugins. In general these plugins are great at handling performance improvements, and any of the ones listed above will work great at generating static cache files for faster website performance. They don’t all tend to to be as consistently good at optimizing as described in step 6 below.

Now, open up a different browser than the one you’re logged in to WordPress with, and load any page on your site. Once it’s done loading, refresh the page. You should find the refresh is now significantly faster than the initial page load. This is not *only* because your browser has used that first page load to cache resources, but also (most significantly) because the server used that initial page load to create a static cache file that is then served on subsequent visits.

Enabling static page caching should not have any effect on your site’s functionality, but will dramatically improve the TTFB (time to first byte) of your website as the server will be able to supply each page much faster than before. The single exception to this is if you have dynamic data that changes on the site that does not use AJAX; that part of your site will not automatically update on every page load. If this occurs, talk to your website developer and ask them to change the feature so that it uses AJAX.

In part 6 below, you can read about how to configure either WP Rocket or SuperCache + Autoptimize to handle more aggressive on-site optimizations that will dramatically improve your Google Page Speed score.

6. Use a Code Minifier and Optimizer Plugin

Generally speaking you want to stick to a single optimization plugin at a time, however WP Super Cache and Autoptimize are actually designed to work together. WP Rocket has the functionality of WP Super Cache and Autoptimize combined in that it both generates static cache files (as described in part 5 above) and minifies and optimizes the site code.

Autoptimize does work with WP Rocket, in that WP Rocket will simply disable its optimization functionality when Autoptimze is detected, however in most cases we’ve found it unnecessary as WP Rocket has the same functionality already built in. WP Rocket’s optimization functionality also tends to be better at not breaking websites than Autoptimize when features like minification and deferral of scripts are enabled.

When it comes to optimizing site code (like minification), which is often needed to obtain higher scores with Google Page Speed or GTMetrix, we’ve never been able to get W3 Total Cache or WP Fastest Cache (among others) to work as well as Autoptimize or WP Rocket’s optimization functions.

Please note: this is the stage where enabling some optimizations may break parts of your site, particularly if the theme, plugin, or custom code hasn’t been written to work well with optimization. If this occurs for you, please see the troubleshooting steps below to learn how to fix it.

WP Rocket (Our Fav, Paid)

If you haven’t completed part 5 above, install and activate WP Rocket using their guide. Then go to Settings > WP Rocket to get started.

Cache Tab:

  • Enable caching for mobile devices. If you use a completely separate mobile site (not recommended) or if you use a lot of elements which only appear on mobile (like swapping parts of a page between a mobile version and desktop version), then check the box to Separate cache files for mobile devices.
  • Enable caching for logged-in WordPress users

Save Changes

File Optimization Tab:

  • Minify CSS: enabled
  • Combine CSS files: disabled*
  • Optimize CSS Delivery: Remove Unused CSS gives a dramatic performance boost. But be prepared for manual tweaks to get it just right, particularly on sites with lots of front-end related plugins.
  • Minify JavaScript Files: enabled
  • Combine JavaScript Files: disabled*
  • Load JavaScript Deferred: enabled
  • Delay JavaScript Execution: enabled**

Now clear the WP Rocket cache and load your site in another browser

*separate files is better when your web server supports http/2 or http/3 (like ours) because the web server engine will send the files concurrently.

**Check your browser web inspector’s console tab for errors. If you see any errors that look like: ‘jQuery is not defined’ or ‘jQuery is not found’, you will either need to figure out why a plugin, theme, or custom code is not loading its resources properly, and exclude those necessary from the Defer and / or Delay Javascript functionality, or take the easy route and apply the default exclusions for Delay Javascript Execution shown on this page of the WP Rocket settings.

If you see any other JavaScript errors you will either need to disable Load JavaScript Deferred or determine why the JavaScript file is not able to be deferred by conferring with the developer of the plugin or theme. Note: some JavaScript errors can be a result of the RUCSS functionality. If you find it’s not deferred JavaScript to blame, look at RUCSS instead.

Media Tab:

  • Enable everything on this page.

Add-Ons Tab:

  • If you have Google Analytics enabled on your site (but not through Google Tag Manager) enable the Google Tracking addon. [Note: the MonsterInsights plugin is not compatible with this addon. If you wish to use this Rocket addon, you will need to Remove MonsterInsights and include the analytics code using another plugin like Google’s SiteKit, or by manually copying and pasting the UA code into your theme’s header code field.]
  • If you use Facebook Pixel tracking code on your site, enable the Facebook Pixel addon.
  • If your server uses Varnish cache (ours do not because nginx caching is available instead), you should enable the Varnish add-on here.
  • If you’re using Shortpixel or Imagify to generate WEBP images for your site, you can enable the option here to serve them in preference over JPG/PNG images.

WP Super Cache + Autoptimize (Free)

If you haven’t completed part 5 above, do so now, then log in to your WordPress installation and go to Plugins > Add New and install and activate Autoptimize

Configuring Autoptimize

  • Go to Settings > Autoptimize and make sure that Javascript, CSS and HTML minification are enabled.
  • Under the Extra tab, beside Google Fonts, try out the option to “Combine and load fonts asynchronously with webfont.js” as well as “Removing Query Strings”.
  • Press “Save Changes”.

Troubleshooting Broken Elements

After enabling optimizations in either WP Rocket or Autoptimze, it’s possible you will see broken elements on the site, depending on plugins used, your theme, and a variety of other things. Here’s some common reasons and fixes:

Images or Videos are missing:

If your theme or page builder uses Javascript to load images on your site, the Lazyload for images and videos option may break that and you will need to disable lazyload on the pages that have this content. See the “Per-Page” section below to learn how to do this.

Global pieces of my site aren’t working, like a menu

If it’s custom code, it’s best to try to fix the code responsible. If it’s functionality provided by a theme or plugin, try to replace that functionality with another theme or plugin (as the theme or plugin is probably not built for speed). You could also report the compatibility issue to the developer of the theme or plugin so they can fix it.

If you’re unable to fix the code or replace the software that isn’t optimized for speed, disable each of the types of minification (e.g.: under the File Optimization Tab in WP Rocket) one-by-one until you see the problem go away, then enable everything again except the setting you last disabled, as it was causing the problem.

If you’re an advanced user, then you can re-enable the setting that didn’t work for you, then use a web inspector to inspect the page and find out which file is causing the problem. Once you’ve found it, go into the WP Rocket or Autoptimize config and exclude just that singular file from minification. (This will likely decrease your page speed score)

Per-Page Exclusions

If you find only select pages having problems, you can disable the option causing problems on that page alone. Edit the page in WordPress, and look for the “Cache Options” box on the right.

If you know which part of the caching plugin is causing problems, uncheck it. If you don’t, uncheck them one at a time, and view the page to see if the issue is resolved. Repeat this for each option until you find the problem setting.

If you’re using Remove Unused CSS (WP Rocket)

There are two options to fix this, depending on the situation:

  1. Just getting started with Remove Unused CSS? Well, we told you this would likely happen. No matter, though; simply look for the CSS identifiers of any elements which have ‘gone wonky’ and add them to the exclusion list in WP Rocket. Then reload the page in another browser, and confirm that the issue is repaired. If not, find the next CSS element down and try that one – you’ll get it figured out and still should see significant gains in your site’s performance.
  2. If you’ve had Remove Unused CSS running for a while now, and it’s suddenly broken on you, try refreshing it by going to WP Rocket -> Clear Used CSS. This can happen in the event of some plugin / theme updates, and WP Rocket needs to be prompted to regenerate its files.

7. Compress image files

This is only going to give you a small noticeable speed boost unless you use a slow Internet connection or have uploaded extremely large images to your site, but it can improve your Google page speed score quite a bit since their mobile Core Web Vitals scores are based on 3G Internet speeds.

Please note: if you’re serious about optimizing your site to eke out every last drop of performance, you should expect a drop in image quality. If you prefer higher quality images, then you are choosing this option at the expense of a) page load times on slower connections and b) synthetic benchmark scores like Google PageSpeed and Core Web Vitals. You must choose which you prefer: high quality images or better benchmark scores. You can’t have both.

Original manual method: Run all PNG files through a compressor then upload them overtop of their prior versions. You may not have these tools available, but I opened all PNG files in Photoshop and exported them for web. If you do this make sure that the files do not include Metadata as this can eat up large portions of the files — especially for icons.

Easier, Paid Method: We use Imagify to handle this for us, all server-side. It’s a fantastic plugin provided by the same folks as WP Rocket that will automatically manage the compression of your images either as a one-shot deal or ongoing. Another option is the plugin called ShortPixel, it works just as well as Imagify in our testing and also offers very fair pricing. Check out all of our recommended resources here.

Caching Gravatars

As this refers to images (those used by post authors), this felt like the right place to talk about this. WP Rocket and WP Super Cache do not cache gravatars by default, meaning if you have lots of comments on your posts from various authors, the loading of external gravatar images can extend your page total render time.

The WP Rocket blog has a great article on how to manage this, the gist being to install the plugin called “FV Gravatar Cache”.

8. Configure Nginx to Load Static Cache Output [Advanced]

Only those using a host with an nginx reverse proxy can take advantage of this option. This applies to anyone using Plesk 12 or newer unless the host is doing weird things with their server configuration.

Because nginx is a lighter weight web server engine than apache, by configuring nginx to directly serve these static files, we dramatically improve performance. For every single request, rather than using an nginx thread to launch an apache process to handle the request, we’re going to configure nginx to only use the nginx thread and not bother to load apache at all for all pages for which we have a static cache file. Cool eh?

If your hosting is with Websavers, you don’t have to do any of this, as we do this automatically with all shared hosting and managed VPS hosting accounts.

If your hosting is not with us, then you must have admin access to Plesk to be able to apply these changes. You will typically only have admin access if you have your own VPS or dedicated server.

Configure nginx to load WP Super Cache’s static cache files

Remember above when we told WP Super Cache to use mod_rewrite to serve our cache files? This is pretty much automatic if you’re using apache because WP Super Cache will automatically modify your .htaccess file to include the necessary mod_rewrite rules.

Inserting the following in your nginx configuration will tell nginx to bypass apache entirely when serving WP Super Cache’s static output. In Plesk, you do this under the domain’s “Apache & nginx Settings” then under the Additional nginx directives field.

### WP Super Cache Below ###
set $cache_uri $request_uri;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
if ($query_string != "") {
    set $cache_uri 'null cache';
# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
     set $cache_uri 'null cache';
# Use cached or actual file if they exists, otherwise pass request to WordPress
location ~ / {
   try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri /index.php;

It’s important to note that you may have some issues with the supplied nginx rules if you’re using WordPress in a subdirectory, especially if you have a ‘nested’ install (with WordPress in the base directory and another WordPress installation in a subdirectory). It can be done with a lot of tricky changes to the supplied rules, but it’s definitely easier on all counts if you change your structure to use a subdomain rather than subfolder.

Configure nginx to load WP Rocket’s static cache files

We made use of this handy WP Rocket nginx config by generating a config (follow the installation instructions), then pasting the result into the advanced nginx configuration directives location in Plesk.

We have completely rewritten this nginx configuration file to work with multiple caching plugins and better optimize its performance. Even better: it’s automatic on all sites hosted with us! If you’d like to take advantage of these optimizations, host with Websavers.

9. Disable unused PHP modules [Advanced]

Plesk Admin Access Required (VPS Only)

If you have full admin access to your VPS, you can actually disable PHP modules you don’t need. Less things to load into memory, less memory use, less intensive processing, and faster performance. This is very advanced stuff, though, so make sure you either know what you’re doing or aren’t doing it on a live server so you’re not breaking anything 😉

  • Go to Tools and Settings in Plesk
  • Under General Settings click PHP Settings
  • Select the PHP version and handler you want to change settings for
  • Toggle PHP modules off that you’re not using
  • Click OK or Apply to save

Now go back to your website and make sure nothing’s broken! You may not know what modules are used in every aspect of your website, so there could be a fair bit of trial and error to get this right.

Bonus Tip #1: ReCAPTCHA v3

If you are using Google ReCAPTCHA for bot protection, either on a contact/quote form or to protect your blog post comments, we don’t blame ya, it’s a decent system to prevent bots from submitting your forms.

ReCAPTCHA v2 is the type of captcha everyone knows and kinda hates where you ‘check a box’ to confirm you’re not a bot. The hate comes in when the system pops up a verification where you have to click on images that match a description like “select all images with a truck in them”. Sound familiar?

ReCAPTCHA v3 is ‘invisible’ in that there’s no checkbox required, making for an improved user experience. But, it has some site speed downsides in that it must load all of its scripts on every page of your website. It tracks users as they navigate your site because their behaviours will help to determine if they’re bots or not.

Because these scripts are loaded on every page, the only way you can prevent those resources from being flagged by GTMetrix and other synthetic benchmarking tools is by switching back to v2 (if your form or recaptcha plugin allows it) because then the resources will only be loaded on the pages where the forms exist.

Advanced users: you can write a WordPress hook that dequeues the ReCAPTCHA v3 resources on all pages except those with your forms, but you should know that doing this will very likely decrease the effectiveness of ReCAPTCHA’s protection.

Bonus Tip #2: Detect and Reduce Dynamic Processing

There’s a few common cases where, regardless of all the caching and optimizations, we just implemented, your server will still launch dynamic PHP processes to serve the requests, thus requiring CPU and memory to handle that request.

Check out our guide to analyzing dynamic processing with WordPress!


  • Apr 3, 3034: Updates to PHP Versions and WP Rocket configurations
  • Jan 12, 2022: Updates to PHP Versions, simplify wording, add brotli compression details
  • Aug 28, 2020: Add notes about content encoding headers, MonsterInsights incompatibility with WP Rocket’s GA Addon, Gravity Forms incompatibility with deferred jQuery
  • Apr 30, 2020: Add Bonus Tip 2: Detecting and Reducing Dynamic Processing
  • Apr 26, 2020: Add tip for caching Gravatar images.
  • Jan 10, 2020: Add improved instructions for configuring browser caching rules in Plesk to improve scores on synthetic benchmarks like Google Page Speed and GTMetrix.
  • Dec 31, 2019: Remove our recommendation to use nginx direct to PHP-FPM processing and instead use rules that will ensure nginx will directly serve your caching plugin’s static cache file output, bypassing apache. This results in identical performance for most website visitors, but much better security, if your host has Plesk’s web application firewall properly configured.
  • Oct 15, 2019: Split step 5 into 2 sections (now 5 and 6) — static cache generating in step 5 (which improves performance but doesn’t break websites) and optimization of resources in step 6 (which improves scores on things like Google Page Speed, but which can also break your website).
  • Feb 4, 2019: Replace Better WordPress Minification with Autoptimize as recommended minification system.
  • Jan 9, 2019: Add PHP 7.3 Info
  • Dec 3, 2018: Reformatted post for better understanding of difference between browser cache vs. nginx->php-fpm direct mode. Use Plesk recommended rewrite rules for nginx.
  • Jul 4, 2017: Added ShortPixel as an image compression option, reformatted post.
  • Feb 20, 2016: Added commercial option, WP Rocket, as a great paid replacement for WP Super Cache + WP Minify.
  • Jun 16, 2017: Added some hosting/server tips.
  • Dec 25, 2013: Original Post Date.

Posted in

Jordan Schelew

Jordan has been working with computers, security, and network systems since the 90s and is a managing partner at Websavers Inc. As a founder of the company, he's been in the web tech space for over 15 years.

About Websavers

Websavers provides web services like Canadian WordPress Hosting and VPS Hosting to customers all over the globe, from hometown Halifax, CA to Auckland, NZ.

If this article helped you, our web services surely will as well! We might just be the perfect fit for you.


  1. Jesse on July 8, 2022 at 9:30 pm

    Hello Jordan,

    Thanks for this post / info! I am trying to set this up on a plesk server with WP Super Cache, but the nginx directives don’t seem to be correct now (2022). I found if I change the last block from index.hmtl to index-https.html (ie. includes the request scheme) that it will serve the man homepage (the only cached page currently):

    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri /index.php;

    However any page which is not cached, or all pages without that change, fall through to index.php and it actually sends the contents of index.php (ie. php code), it does not pass the request to the php handler (using php-fpm). Similarly in the wp-admin backend, any requests just send tools.php, options.php, etc.

    Thanks for any tips….


    • Jordan Schelew on September 7, 2022 at 9:47 am

      Hey Jesse,

      Try this out:

      location @apache_fallback {
      proxy_pass $ws_proxy_url;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      access_log off;
      try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri @apache_fallback;

  2. Marci on March 24, 2022 at 4:11 am

    Thanks so much for this post. I was able to bring my client’s GTMetrix score from a C to an A in less time than it takes me to make a cup of coffee. They are happy! (Me, too!)

  3. Ryan on January 1, 2020 at 4:36 pm

    Hi Jordan,

    Firstly thanks for the great post.

    In Plesk Onyx/Obsidian where exactly should the code in point 8 be added?
    I cannot find the “Web Server Settings” or an “Advanced Field”

    If I add it to “Apache & nginx Settings” in the bottom Additional nginx directives section the website immediately shows a 403 error.

    Am I doing something wrong?

    • Jordan Schelew on January 1, 2020 at 4:52 pm

      Hey Ryan,
      Thanks for the feedback! I’ve corrected that part of the guide: the “Additional nginx directives” under Apache & nginx settings is the right place. After you add it, what specifically do the logs say is the reason for the 403?

      • Joël on January 25, 2020 at 3:42 pm

        Hi Jordan! I’m experiencing the same problem as Ryan. These are my logs for when I load the code:
        Even if the NGINX-part of Wp Super Cache doesn’t work I’m very impressed with the better performance of my sites 😉

        • Jordan Schelew on January 25, 2020 at 10:40 pm

          Hey Joël, thanks for the comments and server logs! Try replacing the location ~ / block with this version and let me know the results.
          location ~ / {
          try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri /index.php;

  4. Sarang Walet on December 14, 2019 at 9:26 pm

    WP Rocket Plugin is really recommended to increase site speed, even though the appearance is simple, there aren’t many animations or other styles but it’s really really great for On Page SEO especially for visitors who use smartphones. Google’s algorithm seems to still be assessing site loading pages, right?

    • Jordan Schelew on December 15, 2019 at 10:26 am

      Hey Sarang, yes we’ve found WP Rocket to be the best balance of effectiveness, simplicity, and power. Google’s algorithm does indeed still appear to take site speed into account, though I would take their PageSpeed results with a grain of salt. I think regardless of what benchmarking tool you use, the TTFB for your target geographic market is going to be your most important metric on which everything else is laid atop. Ensuring that at bare minimum you’ve got a fast web server working in tandem with static file output to serve your compressed index HTML file, you should be able to reach avg 100ms TTFB; and that is an excellent baseline on which the rest of the site’s resources will be loaded. If you don’t have that baseline, ignore everything else and make that happen first.

  5. Fahad on October 24, 2019 at 1:48 am

    The best and most well-documented article on how to speed up wordPress for newbie.

  6. Kevin on February 20, 2016 at 2:51 pm

    Hi Jordan,

    thanks for this great tutorial.

    If i use the code to avoid if statements for performance the /wp-admin link gets too many redirections.

    With the if statement everything works fine.

    Can you help me with that?


    • Jordan Schelew on February 20, 2016 at 2:58 pm


      Honestly we’ve just been using the if statement on all our sites! I tried switching back and forth and running speed tests and the difference never showed itself. I suppose it would actually be in terms of CPU usage on the server side, but even that hasn’t presented issues on shared hosting.

  7. Jordan Schelew on July 21, 2015 at 7:59 pm

    Hey there Sahil,

    That does make sense! HTTP authentication either manually configured or using Plesk probably only gets set up via .htaccess file or specific apache config file, which is not read by nginx. While I haven’t tested this, a quick check of the nginx documentation indicates that the following should work. Add it to the same place you put the nginx supercache config:

    auth_basic “Restricted”;
    auth_basic_user_file {PATH_TO_.htpasswd};

    Note that you have to include the path to your .htpasswd file that was set up to work with Apache. If you created it through Plesk, then the path to the .htpasswd file should be located within the apache config file for the domain here:


    Open up that file and look for a reference to .htpasswd — copy the directory path and use it in the nginx code above. Any remaining issues would have to be permission related.

    All that said… if you DID create your http authentication via Plesk, you should let them know that they’re not configuring nginx at the same time, as this should be up to them to do!

  8. Sahil on June 24, 2015 at 11:52 am

    Having problem with http authentication with wp supercache in plesk panel.
    When i enable supercache with nginx and write the above code with nginx my http authentication for login page not working. 401 authentication page comes but after entered details it started downloding that page.
    Please send me the proper code for using supercache with http authorization for login page.


  9. Renzo on May 18, 2015 at 11:17 pm

    Hi, how can I setup fastcgi_cache with conditional purging ( In plesk. its going fine till i get this error when I copy this part;

    set $skip_cache 0;

    # POST requests and urls with a query string should always go to PHP
    if ($request_method = POST) {
    set $skip_cache 1;
    if ($query_string != “”) {
    set $skip_cache 1;

    # Don’t cache uris containing the following segments
    if ($request_uri ~* “/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml”) {
    set $skip_cache 1;

    # Don’t use the cache for logged in users or recent commenters
    if ($http_cookie ~* “comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in”) {
    set $skip_cache 1;

    location / {
    try_files $uri $uri/ /index.php?$args;

    location ~ .php$ {
    try_files $uri =404;
    include fastcgi_params;

    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 60m;

    location ~ /purge(/.*) {
    fastcgi_cache_purge WORDPRESS “$scheme$request_method$host$1”;

    location ~* ^.+.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off; log_not_found off; expires max;

    location = /robots.txt { access_log off; log_not_found off; }
    location ~ /. { deny all; access_log off; log_not_found off; }

    I get this error : Invalid nginx configuration: nginx: [emerg] unknown directive “fastcgi_cache_purge” in /var/www/vhosts/system/ nginx: configuration file /etc/nginx/nginx.conf test fail

    How can I make it work?

    • Jordan Schelew on May 19, 2015 at 12:35 am


      It looks like you need to install a custom build of nginx that supports it. They have the details in the URL you provided under the header “Check if your nginx has fastcgi_cache_purge module”. If you can’t find a custom build for your OS (looks like they only support Ubuntu / Debian) you can probably do a custom recompile, but that’s something we tend to avoid with production servers.

  10. Ben on April 11, 2015 at 2:31 am


    I found this tutorial a big help but after all implementing the guide, i found out that WP Minify isnt working. but then again the guide gives me a +5 on google speed

    • Jordan Schelew on April 16, 2015 at 1:58 am

      Thanks Ben! Spurred by your comment, I updated it today to quickly indicate that I’ve been testing out the “Better WordPress Minify” plugin for the last couple of months. So far so good, though I find in general it’s tricky to get minification working with everything (like Google seems to really want). This is partly because of their new CSS inlining requirements and mostly thanks to them wanting to force all Javascript to the footer, even if it uses document.onload.

  11. Nikita on April 9, 2015 at 12:37 pm

    Really Good Tutorial Regarding Caching And Page Speed…

  12. Kingsley on December 27, 2014 at 6:46 am

    Great tutorial, this seems to work for me without 404 errors


    • Jordan Schelew on December 29, 2014 at 8:57 pm

      Thanks Kingsley!

  13. Artur on August 14, 2014 at 7:30 pm

    Thanks God I found your post 😀

    I was serching for a way to enable this with ssh but
    unfortunatly my host do not let me in as admin.

    It was so easy, thank you 🙂

    My pagespeed over google insights went from 33 to 75

  14. Ryan Williams on December 18, 2013 at 11:40 am

    Scratch that, turns out a vhost issue was the problem. I was (temporarily) accessing my domain via IP which seemed to trip up the Nginx proxy and just routed everything directly to Apache.

  15. Ryan Williams on December 18, 2013 at 9:22 am

    I wasn’t able to get the gzip part to work in my Plesk 11.5 Nginx directives input. It literally did nothing at all, yet when I used gzip in my .htaccess file they started being gzipped — even though they’re being served by Nginx, which I know because it says so in the HTTP header.

    Not sure what the deal is there. Maybe Plesk now ignores any gzip-related directives and just passes through whatever Apache returns, gzipped or not?

  16. R. Anthony Solis on November 20, 2013 at 7:08 pm

    For us, this almost worked. We had to use

    if (!-e $request_filename){
    rewrite ^(.*)$ /index.php break;

    instead of the rewrite – our admin URL was inaccessible.

    And we opted to check the box for static files served by NGinx and no we did not insert the code:

    location ~* ^/(.*.(js|css|png|jpg|jpeg|gif|ico))$ {
    expires 2w;
    log_not_found off;

    What we didn’t realize was adding the PHP-FPM and modules to support, would upgrade PHP to 5.4. Caused havoc on our end for some WP sites. 🙂

    Thanks for the post and tutorial.


    • Jordan Schelew on December 3, 2013 at 10:36 pm

      Thanks for the tip Anthony! I’ve added a notice to the guide indicating that upgrading to PHP 5.4 could be problematic for older software / php code.

  17. Javier on October 10, 2013 at 11:42 am


    Thank you for this little guide WPO WordPress installed on Plesk servers.

    I tried the settings and work very well, loading times have been reduced.

    My clients will be happy with the loading speed of your websites.

    Thanks again

    • Jordan Schelew on October 10, 2013 at 11:51 am


      Happy to have helped! If you’re ever looking for hosting (shared, VPS, radio, etc.), come see us again.



  18. Ben May on September 21, 2013 at 6:07 am


    Thanks for this post. I was looking for the nginx rule for WordPress and Plesk. Perfect!


    Plesk 11.5 has got me really excited in terms of performance and WordPress hosting. Starting to port my Plesk 10.x client sites over and test!

    • Jordan Schelew on September 26, 2013 at 10:41 am


      Me too! I’m a little surprised they made this move as Parallels has been pretty cautious about changes like this in the past. I’m happy to see them slowly providing admins with more tools to play with without requiring minor hacks to config files and Plesk itself.

      It’s especially nice to know that we can easily compete with specialized services like WP Engine.

  19. bob veris on August 13, 2013 at 5:31 pm

    Hi Jordan,

    I wish I had a nickel for every time someone asked me about nginx. (Okay, I had to run over to wikipedia just to find out) But, it begs the question as an open-source reverse proxy server, is it better than Apache on Linux? I host at godaddy for the while until I find a better place (Websavers is looking awfully good right now).

    The problem with Apache is it repeatedly throws the error Internal Error message at Port 80. Godaddy, who is well aware of the problem, says it must be WordPress plugins. When asked for a solution/workaround, they sheepishly advise to disable every plugin and then start testing them one-at-a-time. Right!??

    Best, Veris

    • Jordan Schelew on August 13, 2013 at 6:27 pm

      Hey Bob,

      I’ve wondered the same thing for quite some time now. When Plesk integrated it as a reverse proxy in Plesk 10, It didn’t really seem to do much for me to impress other than to add complexity to troubleshooting. But with Plesk 11.5 I can definitely see the improvements in performance. With Plesk 11.5 they’ve made it so that:

      1. We have the reverse proxy like in Plesk 10 as the default. This means all incoming requests are handled via threads rather than full-weight processes like Apache normally uses (when supporting PHP — it’s important to note that you can run apache in a threaded mode without mod_php support). This makes for reduced memory usage, especially with a high traffic server. Because nginx also uses less files (it refuses to support things like .htaccess) it means less IO load on the server for every single request.

      2. As of Plesk 11, you can now specify that all static files be handled directly by nginx rather than everything being funnelled to Apache. This makes for really efficient management. You get all the traditional benefits of the tried and tested Apache for running scripts of any kind when needed, but all static content like images, html, css, javascript, etc. are all handled by the nginx thread rather than funneling it through to apache to process. This makes for great performance improvements.

      3. And finally with Plesk 11.5 we can also have nginx pass PHP processing directly from nginx to php-fpm (similar to php-cgi used by Apache). The biggest benefit of this being that a single nginx thread can pass it over to php-fpm, meaning rather than loading an nginx thread, an apache process and finally a php-cgi process, we can simply have the nginx thread and the php-fpm process. It’s considerably easier on disc IO and even better on memory.

      The Internal Server Error can indeed be any number of things. In some cases it can be incorrectly configured server software, in others a plugin misbehaving (although this is less likely — that’s more likely to result in a clear error in your apache error log), or as with us recently, a misconfigured php.ini file that doesn’t work nicely with the latest version of PHP.

      We’ve also seen similar errors as a result of low bandwidth or connection limiting being enabled on a server to try and artificially keep its load low by basically tossing your customers away for the greater good. This is something we don’t typically do unless someone is running a heavily out of date script that’s causing problems (wherein a simple upgrade would be the solution).

Leave a Comment