2 min read

Configuring Nginx Caching for WordPress

To satisfy the "Serve static assets with an efficient cache policy" warning in PageSpeed Insights and achieve sub-100ms Time to First Byte (TTFB), you need to configure Nginx to handle caching at the server level.

This guide covers two types of caching:

  1. Browser Caching: Tells the user's browser to save images/CSS locally so they don't re-download them on repeat visits.
  2. FastCGI Caching: Tells the server to save the generated HTML of your WordPress pages so PHP doesn't have to run on every visit.

1. Browser Caching (The "Static Assets" Fix)

This section specifically fixes the "efficient cache policy" warning. We need to set Expires headers and Cache-Control directives for static files.

Open your Nginx configuration file (usually in /etc/nginx/sites-available/your-site):

server {
    # ... existing config ...

    # Cache Images, Fonts, and Media for 1 Year
    location ~* \.(jpg|jpeg|gif|png|webp|avif|svg|woff|woff2|ttf|eot|ico|mp4)$ {
        expires 365d;
        access_log off;
        add_header Cache-Control "public, no-transform";
    }

    # Cache CSS & JS for 1 Month
    location ~* \.(css|js)$ {
        expires 30d;
        access_log off;
        add_header Cache-Control "public, no-transform";
    }
}

2. FastCGI Caching (Server-Side)

This is the secret weapon for WordPress performance on Ubuntu. Instead of using a heavy PHP plugin, we use Nginx's native engine to serve static HTML.

Step A: Define the Cache Path

Open /etc/nginx/nginx.conf (the main config) and add this inside the http { ... } block:

fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

Step B: Configure the Site

Back in your site config (/etc/nginx/sites-available/your-site), add the logic to skip caching for logged-in users.

# Logic to skip cache for logged-in users, POST requests, or query strings
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { set $skip_cache 1; }

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; # Check your specific PHP version

    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 60m;
    add_header X-FastCGI-Cache $upstream_cache_status;
}