How to cache a PHP page using Nginx

You find it slightly annoying that a completely static page like this one still causes the web server to run code? You want to shave off a few milliseconds from the page load time? Your page does not have dynamic content for your visitors? You have a static IP address at home? You are okay with slightly hackish solutions that might break spectacularly in the future? Then this guide is for you! If not there might still be parts you can apply to your website.

You have any feedback or ideas to improve this? Contact me on Social Media or per E-Mail. You can find my other projects at lw1.at.

Prerequisites

Caching

Cache Path

First of all we need to create a new cache path. Outside of any server block add the following:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=bookstack:10m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

more about fastcgi_cache_path and fastcgi_cache_key

key_zone has to be a unique name, 10m limits the size of the keys (not the cache) to 10MB which allows to store around 80000 pages. levels=1:2 instucts Nginx to store files in directories like /c/29/b7f54b2df7773722d382f4809d65029c which avoids having too many files in one directory (which might be very slow on some file systems). Data not accessed for 60 minutes (inactive=60m) gets deleted.

fastcgi_cache_key indicates how the key of the cache is made up. Two requests for the same key are cached as one file. If your website differs by the language of the visitors browser (like this one), then you might want to add the AcceptLanguage HTTP header. Keep in mind that this makes the cache much less effective as the chances that two visitors have the exact same Language preferences are slim.

fastcgi_cache_key "$scheme$request_method$host$request_uri$http_accept_language";

fastcgi_cache

Next we need to edit the PHP section of your Nginx config.

fastcgi_cache bookstack tells Nginx to use the cache created above. fastcgi_cache_valid 200 60m limits the caching to 60 minutes and only for requests not failing (200 status code).

By default PHP sends a cache-control: no-cache, private header instructing to not cache any pages. To override this we need to ignore this header (fastcgi_ignore_headers Cache-Control). Also sites sending Cookies are not cached by default, so we ignore this too. This of course breaks login and other interactive things on the site, but this doesn't matter for a static website like this one. Last we add a custom header to the requests to make the status of the cache (HIT or MISS) public.

location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
        fastcgi_cache bookstack;
        fastcgi_cache_valid 200 60m;
        fastcgi_ignore_headers Cache-Control;
        fastcgi_ignore_headers Set-Cookie;
        add_header X-Cache-Status $upstream_cache_status;
}

Afterwards don't forget to save the config and reload Nginx to apply the changes (after checking the validity of the config with sudo nginx -t)

You might notice now that your are also now unable to log into your website. Therefore in the next section we will look into creating an exception for the own IP address.

Adding an exception for static IP address

Assuming you have a static IP address you can easly ignore the cache for your visits. Outside of the server block add the following (with your IP address):

geo $ip_cache_bypass {
        default         0;
        198.51.100.123  1;
}

Afterwards add the following to your PHP section (the one edited before) to make sure you won't see cached pages and your pages won't be cached for others to see.

fastcgi_cache_bypass $ip_cache_bypass;
fastcgi_no_cache $ip_cache_bypass;