WordPress Speed: What to Do and in What Order
If your website is slow to load, people leave before reading anything. This guide brings together, in order, what truly makes a website fast. It’s written for the person who makes decisions about the website, not the developer. Each section explains what it is, why it matters, and what needs to be done. The technical instructions are at the end, in a separate block for the person who will carry them out.
Why Your Website’s Speed Matters
Speed isn’t a technical detail. It’s the first impression every visitor gets. And it decides whether they stay or leave.
A slow website is tiring, sells less, and damages your image. It’s especially noticeable on mobile, where people have less patience. And Google ranks it lower in search results. A fast website keeps people there, encourages purchases, and gets Google to rank you higher. That’s why speed is a business decision, not an IT task.
What Can Go Wrong
Improving speed has its pitfalls. These are the ones we see most often:
- Making changes without a backup. If something breaks, there’s no going back. Backup first, change later.
- Installing plugins blindly. Each plugin adds work to every visit. Two that do the same thing just get in each other’s way.
- Activating everything at once. Some improvements break the homepage photo carousel, a form, or a map. If you activate ten things, you won’t know which one broke it.
- Stale content. When the website stores each page already made, it needs to be refreshed when you publish a change. Otherwise, the visitor sees the old version.
- Things that disappear. If the website stops loading what isn’t visible yet, some embedded video or map might not appear. There’s a solution, but you need to know about it.
- Changing without measuring. Without a measurement before and another after, you don’t know if it made any difference.
The Nine Measures, at a Glance
Almost every list of tips repeats the same things. We’ve brought them together into nine measures, which also serve as this guide’s index. Order matters: the first ones deliver more results with less effort. All of this can be done manually, or comes standard with PF Hosting.
- Good hosting and page caching (each page stored ready-made). This solves most of the problem.
- Your website copied onto servers distributed around the world. So it loads quickly from anywhere.
- Lighter images. These are almost always the heaviest element.
- Lighter code. Remove what’s unnecessary and defer what isn’t needed at the start.
- Make the text visible even if the font takes time to arrive.
- Don’t load what the visitor can’t see yet.
- A lightweight theme, few plugins, and a clean database. Less work on every visit.
- Have the server send files compressed.
- Measure before and after. Without measurement there’s no improvement, only opinions.
1. Hosting and Caching: The Foundation of Everything
The first thing is the server where your website lives. A good theme won’t save a saturated or poorly configured server. That’s why it’s the first place to look.
What to Ask of WordPress Hosting
Not all hosting providers treat WordPress the same. Before signing up, or staying where you are, check this list.
- Each page stored ready-made, by the server itself and not just by a plugin. It’s the difference between building the page on every visit or serving it ready-made.
- Updates handled by them, with a prior backup. An outdated WordPress is slow and, above all, insecure.
- A filter that stops attacks before they reach your website.
- Your website copied onto servers distributed around the world, so images don’t always come from your server.
- Ability to grow when traffic increases, without having to migrate again.
- Support that responds when something fails, with people who know WordPress.
- Server software, up to date. WordPress relies on other software to run. Each new version usually runs faster.
If your hosting doesn’t meet several points on this list, the most cost-effective improvement isn’t another plugin. It’s changing hosting. We built PF Hosting with your own server for your website and servers distributed around the world. It also includes a filter that stops attacks, daily backups, and updates handled by us.
Caching: On Your Server and Close to the Visitor
There are two ways to prevent your website from building each page on every visit. They don’t compete: they complement each other.
The page stored on your server is created by a plugin or the server itself. It stores the assembled page and serves it as-is. This way the server works less and handles more visits with the same resources.
Your website can also be stored on many servers distributed around the world. Each visitor receives it from the nearest one. Someone visiting from afar doesn’t have to reach your server.
The usual approach is to use both methods. With one condition: when you publish a change, both must be refreshed. Otherwise, the visitor sees old content.
2. Your Website, Copied onto Servers Around the World
These servers store your images, your code, and your fonts at many points around the world. They serve them from the one closest to each visitor. The website loads faster, your server works less, and it’s also more protected from attacks. We set them up piece by piece, to tailor them to each website. How it’s done is in the final block.
Properly set up, they do two more things. In front, a filter is placed that stops attacks before they reach your server. And your visitor’s browser stores a copy of the images and code for a long time. That way it doesn’t request what it already has. Both are part of PF Hosting.
3. Images: The Weight That Shows Most
Images are almost always the largest part of a page’s weight. The basics are in optimizing images in WordPress. Compressing them, uploading them already at the size they’ll be displayed, and choosing a modern format. And serving them from servers distributed around the world.
Here we add two techniques. The first: a modern image format that weighs significantly less with the same quality. A plugin converts the images and stores the lighter version alongside the original. The second: notifying the browser from the start which is the main image above the fold, so it displays it sooner. What Google looks at most when grading you is how long that main image takes to appear. That’s why the notification pays off.
4. Lighter Code
Each theme and each plugin adds its own code files. Without control, a page requests dozens of files before showing anything. Three things reduce that work:
- Remove what’s unnecessary: whitespace and the notes the programmer left written in the code. The file weighs less and works the same.
- Combine several files into one, to make fewer requests.
- Defer what isn’t needed to display what’s seen first.
There are plugins that do all three. Whichever you choose, go slowly. Activate one option and check the website on several pages and on mobile. Only then activate the next one. Deferring code can break the homepage photo carousel, a form, or a map. If it happens, you’ll want to know which option caused it. The detail, option by option and by hand, without plugins, is in making your WordPress code lighter.
5. Fonts: Make the Text Visible from the First Moment
A custom font takes time to download. While it arrives, the browser may leave the text invisible for up to a second. The solution is simple: display the text with a system font and swap it when the proper one is ready. Google recommends and measures this.
Two more measures that help equally. Use few different fonts, and for each only what you truly need: regular and bold. And store the fonts on your own server, instead of requesting them from Google. That way your visitor’s data doesn’t leave your server. We explain it in optimizing fonts in WordPress.
6. Don’t Load What No One Sees Yet
Images, videos, and maps below the first screen aren’t needed at the start. You can tell the browser to wait: download them only when the visitor scrolls down to them. This is called lazy loading. WordPress already does this by default for images. A plugin extends it to videos and backgrounds. The page weighs less and displays sooner.
A warning, with a real case. After migrating a client’s website, some embedded videos and maps stopped appearing on several pages. They looked fine in the editor. The culprit was lazy loading: that content couldn’t handle being loaded later. The solution wasn’t to disable it, but to exclude that element. Almost all plugins have an exclusion list. If something disappears after enabling it, start there.
7. Lightweight Theme, Few Plugins, and Clean Database
Three measures that cost no money and are often forgotten:
- Use a lightweight theme. A theme with hundreds of options and a visual editor loads a lot of code your website doesn’t use. The less it does, the faster it will run.
- Deactivate and delete the plugins you don’t need. Each one adds work to every visit. If two do the same thing, keep one. We have a post about which speed plugins work well together and which don’t.
- Clean up the database. Old versions of each page, spam comments and expired data cause the database to grow unnecessarily. A plugin can do this with two clicks. It’s also worth limiting how many old versions WordPress keeps. We explain this in optimizing the WordPress database.
8. Have the server send compressed files
Compressing what travels to the browser reduces its size. Ask your provider if they have this compression enabled. It’s free, and any serious hosting provider offers it. If they say no, that’s a bad sign about everything else.
9. Measure: before, after, and without fooling yourself
Without a baseline measurement, you won’t know whether an improvement made any difference. Google has a free speed tool. Enter your website address, click «Analyze,» and it gives you a report for mobile and another for desktop. Each includes a score from 0 to 100 and a list of recommendations, ordered by impact.
The four scores you need to understand
- How long until the first content appears on screen. This is the moment the visitor knows the page is arriving.
- How long until the largest element of the first screen is visible, almost always an image or a headline. This is the score that carries the most weight.
- How much the layout shifts while loading. It’s that button that moves just as you were about to click it.
- How long the page takes to respond to your actions. This is where heavy code takes its toll.
The top score is a simulation. The tool also shows what actually happens to people who visit you, when the site gets enough traffic. That data is more valuable. How to read each recommendation is explained in the speed score Google gives your website.
The correct order: measure, apply, measure again
- Make a backup. Before touching anything. If something goes wrong, you can revert in minutes.
- Measure the current state, on both mobile and desktop, and save the report. Also measure two or three interior pages, not just the homepage.
- Apply one measure at a time, in the order of this guide. First hosting and page caching, then images and distributed servers, then code.
- Measure again after each change and compare with the saved report. If a measure doesn’t improve anything or breaks something, undo it.
- Repeat in a few weeks. Plugins update and content grows. What’s fine today may not be tomorrow.
If your site isn’t slow overall, but only at specific times or only when you log in to edit, the problem is usually something else. We cover this in Slow WordPress: possible causes and how to increase speed.
And if you’d rather not do it yourself
Write to us with your website address. We’ll tell you how it would perform on PF Hosting and what would change. And if you have someone who handles the website, pass them the section below.
What we use
PF Hosting runs on AWS, Amazon’s data centers, within the European Union. Each website has its own server, with no public address. Amazon CloudFront sits in front as the CDN, with AWS WAF as an attack filter. Backups are daily and updates are handled by our team.
The plugins mentioned in this guide are another matter: we use them when working on a client’s WordPress. WP Offload Media sends images to a private S3 bucket and serves them through the CDN. EWWW Image Optimizer converts them to WebP. WP-Optimize cleans the database. For minifying, combining and deferring CSS and JavaScript, WP Rocket (paid) or Autoptimize (free). Page caching is done by the server, or by WP Super Cache, W3 Total Cache or WP Rocket. Cloudflare is the alternative CDN with edge HTML caching. Gzip or Brotli compression on the server. We measure with Google PageSpeed Insights.
For the person doing the work: steps with commands
What follows is for the person who will be working on the website. If that’s not you, pass this along. Make a backup before each step and test the website on several pages after each change.
Page cache and edge cache
Server caching is done by plugins like WP Super Cache, W3 Total Cache or WP Rocket. Or by the server itself, if it’s set up for it. It stores the already-generated HTML, and sometimes database queries and PHP objects too. It reduces server load, so it can handle more traffic with the same resources.
Edge caching is what a CDN does. It stores copies of content at dozens of locations and serves them from the one closest to the visitor. Some CDNs, like Cloudflare with its automatic WordPress optimization, even store HTML at the edge. The usual combination is page cache on the server plus CDN for static files. With both active, when you publish a change you need to clear both.
Three settings that don’t cost money. Limit the number of revisions in wp-config.php. Clean up revisions, spam and expired transients with WP-Optimize. And enable Gzip or Brotli compression on the server.
Setting up the CDN with CloudFront, step by step
We use Amazon CloudFront because it can be tailored piece by piece to what each website needs.
1. A private S3 bucket for static content. In the AWS console, go to S3 and create a bucket. Give it the same name the CDN subdomain will have (for example, cdn.ejemplo.com): you’ll identify it more easily. Choose the same region where your server is located. If your WordPress lives on Amazon Lightsail, the bucket and the instance must be in the same region. Leave ACLs disabled, check «Block all public access» and keep the rest of the default options. The bucket doesn’t open to the public: CloudFront will read it, with a specific permission created later.
2. The subdomain’s TLS, in ACM. For the CDN to serve via HTTPS, you need a public TLS for the subdomain. Request it in AWS Certificate Manager (ACM): «Request a public certificate», name cdn.ejemplo.com, DNS validation and RSA 2048 algorithm. ACM gives you a CNAME record that you need to create in your domain’s DNS. When it detects it, it marks the TLS as verified; this usually takes a few minutes. If your domain is on Route 53, a button creates the record for you. A detail that saves time. For CloudFront, the TLS must be requested in the Northern Virginia region (us-east-1), regardless of the bucket’s region.
3. The CloudFront distribution. In CloudFront, create a new distribution with this configuration:
- Origin: the newly created S3 bucket. Under «Origin access» choose «Origin access control settings» and create the control. CloudFront will notify you that the bucket permissions need to be updated: this is done at the end.
- Default behavior: redirect HTTP to HTTPS; allowed methods GET, HEAD and OPTIONS, with OPTIONS also in cached methods.
- Policies: cache policy «CachingOptimized», origin request policy «CORS-S3Origin» and response headers policy «CORS-and-SecurityHeadersPolicy». The latter adds security headers to everything the CDN serves.
- Price class: «Use all edge locations» if you have visitors across multiple continents. If most are in Europe, the option limited to Europe and North America costs less and performs the same.
- Alternate domain name (CNAME):
cdn.ejemplo.com, and in the dropdown, the TLS you created in ACM. - HTTP versions: check HTTP/2 and HTTP/3.
4. DNS and bucket permission. Copy the «Distribution domain name» from the distribution (something like d1abc234.cloudfront.net). Create a CNAME record in your DNS for cdn.ejemplo.com pointing to that value. Then, in the distribution’s «Origins» tab, edit the origin. Under «Origin access» there’s a «Copy policy» button that copies the policy authorizing that distribution to read the bucket. Paste it into the S3 bucket policy («Bucket policy» section). Check that the first character is the opening brace and that there’s no extra space before it. To verify everything works, upload any image to the bucket and open it with the CDN URL: https://cdn.ejemplo.com/imagen.png. If it displays, the CDN is ready.
For WordPress to use the CDN, a plugin like WP Offload Media sends each image you upload to the bucket and rewrites its URLs. With EWWW Image Optimizer converting images to WebP, the two plugins form a combination that has worked well for us on AWS.
5. CloudFront functions: WebP and CORS. CloudFront lets you run small functions at the edge, without a server, to modify requests and responses. Two real cases we’ve solved this way.
The first: serve the WebP version of each image without touching the HTML. A function on «viewer request» rewrites the request for a .jpg or .png to the same file with .webp added. That’s how EWWW stores them.
function handler(event) {
var request = event.request;
if (request.uri.match(/\.(jpe?g|png)$/)) {
request.uri += '.webp';
}
return request;
}
The second: when activating a cache plugin, fonts served from the CDN stopped loading. The error was «has been blocked by CORS policy». The CDN and the website don’t have the same origin, so the browser requires the Access-Control-Allow-Origin header. A function on «viewer response» adds it only to font files:
function handler(event) {
var response = event.response;
if (event.request.uri.match(/\.(ttf|woff2?)$/)) {
response.headers['access-control-allow-origin'] = { value: '*' };
}
return response;
}
Two notes. CloudFront allows only one function per event and distribution: if you need two rules on the same event, they go inside the same function. And between CloudFront Functions and Lambda@Edge, for this type of rule we prefer the former: they’re simpler and accept more simultaneous requests.
6. WAF and cache headers. In front of the distribution, AWS WAF—a web application firewall—can be enabled. It filters malicious requests before they reach your server, without penalizing performance. And long cache headers are served for all static files. This way, the visitor’s browser doesn’t request an image or stylesheet it already has. This is what we set up in our private CDN projects and is also included in PF Hosting.
WebP with EWWW Image Optimizer
WebP weighs significantly less than JPEG or PNG with the same visual quality. EWWW Image Optimizer converts images and saves the WebP version alongside the original. For the server to serve WebP to browsers that support it, rewrite rules are needed in .htaccess. The plugin attempts to insert them, but doesn’t always succeed, and once inserted they’re hard to find again. Here they are:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png|gif)$
RewriteCond %{REQUEST_FILENAME}\.webp -f
RewriteCond %{QUERY_STRING} !type=original
RewriteRule (.+)\.(jpe?g|png|gif)$ %{REQUEST_URI}.webp [T=image/webp,L]
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(jpe?g|png|gif)$">
Header append Vary Accept
</FilesMatch>
</IfModule>
AddType image/webp .webp
If you serve images through a CDN, you don’t need these rules. EWWW, even in its free version, can handle the substitution via JavaScript, or the CDN itself can do it using the function from the previous section.
Telling the browser which is the main image
The speed metric that carries the most weight (LCP, Largest Contentful Paint) measures how long it takes for the largest element on the initial screen to appear. On most pages, that element is an image. If the browser discovers it late, the metric suffers. The solution is to notify it from the start with rel="preload". Don’t overuse this tag, but for the featured image of each post, it’s worth it. This code, in the theme’s functions.php, preloads it automatically:
add_action('wp_head', function () {
if (has_post_thumbnail()) {
$url = wp_get_attachment_url(get_post_thumbnail_id());
echo '<link rel="preload" as="image" href="' . esc_url($url) . '">';
}
});
For a specific image on a single page, simply add the tag manually in that page’s <head>.
<link rel="preload" as="image" href="https://cdn.ejemplo.com/portada.jpg">
WP Rocket or Autoptimize
The two plugins we recommend for minifying, combining, and deferring are WP Rocket, a paid option, and Autoptimize, which is free. They do the same thing with different levels of depth. In WP Rocket, under «File Optimization,» four things are enabled: CSS and JavaScript minification and concatenation, deferred JavaScript loading, and critical CSS generation. Then, «Clear Cache.» In Autoptimize, under «Settings,» check «Optimize CSS,» «Optimize JavaScript,» and optionally «Optimize HTML» and critical CSS insertion. Save and clear the cache. Other plugins that sometimes appear in these lists aren’t optimizers. A visual CSS editor or a sales page theme doesn’t minify anything. With HTTP/2 and HTTP/3, combining matters less than before, but it still helps when there are many small files.
Fonts: font-display swap
The font-display: swap property tells the browser to display text using a system font and swap it once the proper font is ready. Google recommends this as a best practice and measures it in PageSpeed. For a self-hosted font, it’s declared like this:
@font-face {
font-family: 'MiFuente';
src: url('/fonts/mifuente.woff2') format('woff2');
font-display: swap;
}
By default, many themes and page builders load fonts with the «auto» value, which can leave text blank for up to a second. Most allow you to change this from their own typography settings. If the theme regenerates its font files, you’ll need to regenerate them again after the change. Host fonts on your own server or CDN, rather than fetching them from Google Fonts. This keeps visitor data from being sent to a third party.
Lazy loading: excluding an iframe
WordPress applies loading="lazy" to images by default. Plugins like EWWW or WP Rocket extend this to iframes and backgrounds. A real case from section 6: after migrating a client’s site to AWS, some iframes inserted as HTML stopped displaying. The browser inspector explained why. The iframe had a class lazyloaded that wasn’t in the original HTML; EWWW Image Optimizer had added it. The content of that iframe didn’t support lazy loading. The definitive test was placing the iframe in a part of the page that wasn’t visible on load: there it worked.
The solution: add a custom class to the iframe and enter it in the exclusions section of EWWW’s settings. Then, save and clear the cache. Almost all lazy loading plugins have a similar exclusion list.
