This section will cover the basics on configuring services for performance with WordPress.
WordPress has a lot of dynamic functionality, but this comes at a cost. Tasks such as processing PHP, querying the database and collecting information from external APIs all take resources and time.
Caching saves time for potentially heavy tasks by reusing previously computed results, rather than calculating them for every page view.
Caches typically expire after a certain amount of time and are regenerated so the most recent content is displayed. When items are served from cache they have a faster response time, often coming from memory, and take load off the server.
In a typical page load, various caches might be checked in the following order:
- Local Browser cache / Local Storage / Web App Manifest
- Content Delivery Network (CDN)
- Full Page Cache (Reverse Proxy - Varnish or NGINX)
- Full Page Cache (File-based Full-page caching with plugins)
- Static Cache (JS, CSS, Images with static caching services, NGINX tryfiles, etc.)
- Opcode Cache
- Object Cache (wp_options, transient API)
- Fragment Cache (Database, static files, transient API)
Each caching instance may be a different domain, server, or compute instance, either routed by the local machine, the remote server cluster, or any intermediary computer along the request chain. It is recommended to check with your hosting company for server-side solutions like Varnish and NGINX which usually works faster than plugins and file based full page caching.
Each cache may have any configuration of:
Data storage: RAM, SSD, or spinning hard drives. Physical connection, or data over network.
Input/Output latency: Connection from local or remote server motherboard to RAM, Data I/O, or network I/O.
For any given page load, speed and user experience will result from the combined latency of all services, and the order they are processed as users interact with a web application.
(Example: CSS; Generated by JavaScript, PHP or pre-processor. Sent over network. Earliest display: Inline script within first HTTP packet of first HTML response. Typical: Loaded by many plugins in many files over many requests. Middle-ground: combined file, cached locally, to CDN, or server RAM or SSD.)
Content Delivery Networks are designed to optimize the network latency between servers and visitors from different geographical locations. Data is distributed amongst endpoints and then visitors get served from the endpoint which is closest to them.
In addition to optimizing networking latency, CDNs can act as another layer of static and/or full-page caching running on all those endpoints.
It’s important to make sure that CDNs are working well with all your other caching systems and that it purges caches on all endpoints when you request that from your main server. Otherwise, people in certain areas may get old results which is generally an issue that’s difficult to troubleshoot.
Note, that if you don't have full page caching on the CDN edges, this might increase the TTFB (time to first byte) because the node must fetch the data from the origin server before serving it to the end user.
In order to display your content, WordPress does a lot of work under the hood, and all those calculations require server resources and time to complete. For starters, the PHP service on the server has to process the request, load WordPress core, your theme PHP files and all PHP scripts coming from your plugins. The majority of those PHP files make requests to your database, too, which adds to the overall resource footprint of your site.
The best way to cache these requests is to use a reverse proxy like NGINX or Varnish which stores the output directly into the server memory or hard disk. That saves a lot of processing power because cached content is served straight out of the reverse proxy without hitting your web server, the PHP service, or your database service at all. If a reverse proxy is not available on your current server setup, you can fallback to storing cached content into your file system. It's slower than reverse proxies and a hit reaches your web server and your PHP service at least once, so it can direct the request to the proper cached file but still - it's much faster than doing all the computing for every request.
Full Page Caching stores the HTML output of a request, but all the CSS, JS, images and font files will have to be loaded separately too. They are handled separately and optimizing them is worth investing the time and effort. Static caching can have great effect on those resources. You can often use the same reverse proxy to static resources in the server memory - CSS, JS, Fonts, Images and serve them directly.
It's important to have the ability to expire caches when necessary to avoid serving visitors old data. When available, selective caching is preferred over purging the entire cache, to avoid the cost of WordPress regenerating every page for the site. Furthermore, it's good practice to exclude certain types of pages from your full page caching completely because they are different for each user. For example, if you have an online store, it's imperative that your cart, checkout and profile pages are completely dynamic. In general, it’s a good idea to exclude all logged in users from the cache because they are supposed to see personalized content. Another important aspect is the default caching period, which can be different for each website depending on how often data is changed.
In 2005, WordPress introduced its internal object cache — a way of automatically storing any data from the database (not just objects) in PHP memory to prevent unnecessary queries. However, out of the box, WordPress will discard all of those objects at the end of the request, requiring them to be rebuilt from scratch for the next page load. In addition to that, you can use persistent object caching mechanisms like Redis or Memcached which, however, require additional plugins (drop-ins) which allow WordPress to use these services.
source: scalewp.io
What does this mean? Think of a standard WordPress homepage displaying the most recent posts. Each of these posts has quite a bit of information associated with it WordPress must look up such as the author, categories, tags, and excerpt.
Support for a persistent object cache gives WordPress, plugins, and themes, a place to store that data for reuse. While these items are cached, PHP execution time is improved while lessening the load on the database. It's particularly helpful in situations where much of the page is difficult to cache from the front-end, like for authenticated traffic or e-commerce applications.
For these reasons, persistent object caching support is commonly offered with managed WordPress hosting.
Transients are inherently sped up by caching plugins, where normal Options are not. A memcached plugin, for example, would make WordPress store transient values in fast memory instead of in the database. For this reason, transients should be used to store any data that is expected to expire, or which can expire at any time. Transients should also never be assumed to be in the database, since they may not be stored there at all.
source: WordPress Codex
The web server must read, compile, and run each PHP script. An opcode cache stores a compiled copy of each PHP script in memory or on disk. When the web server starts processing PHP scripts for WordPress, the web server checks the opcode cache for a cached copy of the PHP script. If there is a cached copy, the web server can skip straight to running the PHP script using the cached copy instead of having to read and compile the script again. Skipping this reading and compiling PHP scripts can greatly improve the web server's resource usage and enable WordPress to serve many more requests than it might have been able to otherwise. It's particularly helpful for dynamic content and authenticated traffic, where full page caching isn't as effective.
As with any cache, opcode caches can keep changes from taking effect until the cache expires or is purged. With opcode cache specifically, this means older versions of the compiled PHP code will be loaded. When updating plugins, themes, or WordPress core, the appropriate files should be purged from the cache to avoid continuing to load the older versions.
This caching method allows saving sections of otherwise non-cacheable dynamic website content. It can help especially for sites where the majority of the page is static, but has certain dynamic elements, like a shopping cart, or for membership sites.
In the WordPress context, developers often store parts of the page using the WordPress transients/object cache API. In these cases, providing a persistent object cache will allow that caching to happen outside of the database.
Storing these fragments separately in a front end cache is not natively supported by WordPress, and means both manually configuring the sections of the page to be cached, and configuring your front-end cache, whether it be Nginx, Varnish, or otherwise, to support fragment caching. This is usually an advanced technique, and reserved for sites or hosting platforms with very high dynamic traffic needs.
Purging caches is as important as storing them. You have to make sure that all layers of caching are cleared when necessary.
Fragment caching is the temporary storage of expensive or long-running server-side operations to avoid taxing web servers and delayed delivery to visitors. It's become a common practice for operations such as generating Menu markup, Widget markup and slow MySQL or HTTP responses. Core currently uses transients to cache HTTP calls to WordPress.org APIs for updates and events.
Fragment caching is particularly beneficial when appropriately paired with full-page caching. Perhaps there's uniform <footer>
markup displayed on every page that can be temporarily stored. When the server needs to rebuild static cache files and a fragment is found, it saves the server from running Menu/Widget queries to generate the footer markup on every page.
Significant caution should be exercised blanket caching Core resources. If a site Menu relies on dynamic .current-menu-item
classes, storing the menu markup in a fragment will "burn" that class in, no longer highlighting the correct page as a user navigates. Any caching of WordPress Core resources should be opt-in and integrate an appropriate flushing mechanism for when users modify the resource.
The Transients API should always be used for fragment caching instead of directly using wp_cache_*
functions. In environments without a persistent Object Cache, set_transient()
will store cache values in the database in the wp_options
table. However, when Object Cache is enabled, set_transient()
will wrap wp_cache_set()
.
See PHP Optimization.
- 2023-09-14: Moved PHP Optimization to Advanced Administration.
- 2021-05-27: Fixing infoboxes
- 2021-02-17: Changelog added
- 2020-11-09: Lifted PHP Recommendation to 7.4
- 2020-06-02: Published from Github