Are you seeing the “Specify a cache validator” warning in Pingdom, GTmetrix, or Google PageSpeed Insights on your WordPress site?

This is due to missing HTTP caching headers which should be included on every origin server response, as they both validate and set the length of the cache. If the headers aren’t found, it will generate a new request for the resource every time, which increases the load on your server. 

Utilizing caching headers ensures that subsequent requests don’t have to be loaded from the server, thus saving bandwidth and improving performance for the user.

specify a cache validator warning
Specify a cache validator warning

The warning from Pingdom states:

The following resources are missing a cache validator. Resources that do not specify a cache validator cannot be refreshed efficiently. Specify a Last-Modified or ETag header to enable cache validation for the following resources.

Follow the steps below on how to fix the “Specify a cache validator” warning.

Fix the “Specify a cache validator” Warning

The first thing that is important to note about this warning is that you can only fix this for requests that are on your server. If you have 3rd party requests you are seeing this on, there is nothing you can do as you don’t have control over their web servers. Although feel free to share this article with them. And remember, with Pingdom you might need to run the test a few times. It could be that the warning might show up the first time and will be gone the second time. When you first run the tool it is priming the cache of the assets from the server.

There are four different types of headers which can be utilized in different ways to fix this warning. This is where it can get a little confusing, but we will try to explain it as easy as possible.

Headers that Validate Cache

The first two headers are last-modified and ETag. These headers help the browser determine if the file has changed since the last time it was requested. Or rather, they validate the cache.

1. Last-Modified

The last-modified header is generally sent automatically from the server. This is one header you generally won’t need to manually add. It is sent to see if the file in the browser’s cache has been modified since the last time it was requested. You can look at the header request in Pingdom or use Chrome DevTools to see the value of the last-modified header.

last-modified header
Last-modified header

2. ETag

The ETag header is also very similar to the last-modified header. It is also used to validate the cache of a file. If you are running Apache 2.4 or higher, the ETag header is already automatically added using the FileETag directive. And as far as NGINX goes, since 2016 the ETag header is enabled by default.

etag header
ETag header

You can enable the ETag header manually in NGINX using the following code.

etag on

Headers that Determine Cache Length

The next two headers are Cache-Control and Expires. These headers help determine how long the file should be held in cache before it fetches a new copy from the server. Remember, to fix the warnings you see in Pingdom or GTmetrix, you need to ensure that you have a header that both validates the cache, as well as one that determines the cache length.

3. Cache-Control

Cache-Control is a header made up of different directives which allow you to define the length of the cache. A few of the most common directives include:

  • max-age: Defines the amount of time a file should be cached for.
  • public: Allows any cache publicly to store the response.
  • private: Only cacheable by browser accessing the file.
cache-control header
Cache-Control header

In our example above, we can see the asset is using the max-age directive. 604800 seconds would equal a cache of seven days. To configure this in Apache, simply add the following code to your .htaccess file.

<filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=604800, public"

To configure this in NGINX, simply add the following code to your config file. All NGINX configuration files are located in the /etc/nginx/ directory. The primary configuration file is /etc/nginx/nginx.conf.

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
 add_header Cache-Control "public";

To learn more about the different directives, check out this in-depth article on Cache-Control .

4. Expires

And last you have the expires header. According to this Google Developers article, HTTP Caching: Cache-Control header was defined as part of the HTTP/1.1 specification and supersedes previous headers (in this case the Expires header) used to define response caching policies. All modern browsers support Cache-Control, hence that is all you need. However, it won’t hurt anything if you have both, but remember that only one will be used. The Expires header uses an actual date, whereas the Cache-Control header lets you specify an amount of time before expiry.

expires header
Expires header

To add the Expires header in Apache, simply add the following code to your .htaccess file.

 <IfModule mod_expires.c>
 ExpiresActive On
 ExpiresByType image/jpg "access 1 year"
 ExpiresByType image/jpeg "access 1 year"
 ExpiresByType image/gif "access 1 year"
 ExpiresByType image/png "access 1 year"
 ExpiresByType text/css "access 1 month"
 ExpiresByType application/pdf "access 1 month"
 ExpiresByType application/javascript "access 1 month"
 ExpiresByType application/x-javascript "access 1 month"
 ExpiresByType application/x-shockwave-flash "access 1 month"
 ExpiresByType image/x-icon "access 1 year"
 ExpiresDefault "access 7 days"

Ensure that you add your Expires headers block below things such as mod_rewrite, GZIP, etc. At the bottom of the file is the safest.

expire headers htaccess
Adding Expires headers in .htaccess

To add Expires headers in NGINX, simply add the following code to your config file.

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 7d;

In a lot of cases on NGINX, both the Cache-Control header and Expires header are simply used together, even though this is not technically required:

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 7d;
    add_header Cache-Control "public";

All of the headers above are added by default on all Kinsta servers, so if you are a Kinsta customer you won’t see ever see this warning and don’t have to worry about. Most 3rd party CDN providers, such as KeyCDN and Cloudflare, also automatically add these headers when delivering your assets. If you are seeing the warnings, it could be that your host is running out of date software or has misconfigured the server. We typically see this on shared hosts. Or perhaps you are setting up your own server, in which case some of the headers above might not yet be added.

And if all goes well, and you don’t have any 3rd party requests that aren’t correctly using the header, you should see an improvement on your score with website speed test tools such as Pingdom (as seen below).

Fixed specify a cache validator warning
Fixed specify a cache validator warning