Nginx Optimization Tuning with Caching

Nginx is a powerful web application software to host websites with. In time, Nginx has finally surpassed Apache in the most used web server software after its launch in the early 2000s, mainly due to performance plus the ability to be more than a traditional web server. One of the many things Nginx is used for is being deployed as a reverse proxy or load balancer.

However, a key feature is missed most times when deployed as a front for back end servers. That is the ability Nginx has over HAProxy in catching static resources relatively easy with writing to disk or if you have a beefy server with quite an abundance of RAM adding the cache to memory for ultimate performance.

In the following tutorial, you will see some examples of how to do this, instantly giving your Nginx server a boost if you use Nginx in a reverse proxy situation.

Prerequisites

  • Recommended OS: Any Linux operating system with Nginx installed.
  • User account: A user account with sudo or root access.

Create Nginx cache (Traditional Disk cache)

In the first example, you will create a directory and add a zone to your Nginx server block with the traditional caching static files to a disk. In the long term, If you are not using an SSD, this can sometimes be a negative factor. Also, even with SSD, constant writing can shorten the life expectancy depending on the age and quality of the hard drive.

First, create the directory to store the cache data:


sudo mkdir -p /cache/tmpfs/

Next, add the following to your server block, modify the existing proxy server block, and add the extras.

proxy_cache_path /cache/nginx/tmpfs levels=1:2 keys_zone=my_zone:100m max_size=6g inactive=1d use_temp_path=off;

server {
    ...
    location / {
        proxy_cache my_zone;
        proxy_cache_key $scheme$request_method$proxy_host$request_uri;
        proxy_cache_valid 404 302 1m;
        proxy_cache_valid 200 1d;
        proxy_http_version   1.1;
        add_header X-Cache-Status $upstream_cache_status;
    }
    ...
}

Note, make sure the (proxy_cache_path) goes into the HTTP part and not the server block. Also, the file paths must be absolute, or else it will not work. Overall, the guide has the files not touched for 24hours removed and cache validation for 24 hours before being refreshed.

Here is a breakdown of the Nginx terminology.

  • keys_zone: sets up a shared memory zone for storing the cache keys and metadata such as usage timers. Having a copy of the keys in memory enables NGINX to quickly determine if a request is (HIT) or a (MISS) without having to go to disk, greatly speeding up the check. A 1‑MB zone can store data for about 8,000 keys, so the 10‑MB zone configured in the example can store data for about 80,000 keys.
  • inactive: specifies how long an item can remain in the cache without being accessed. In this example, a file that has not been requested for 60 minutes is automatically deleted from the cache by the cache manager process, regardless of whether or not it has expired. The default value is 10 minutes (10m). Inactive content differs from expired content. NGINX does not automatically delete content that has expired as defined by a cache control header (Cache-Control:max-age=120 for example). Expired (stale) content is deleted only when it has not been accessed for the time specified by inactive. When expired content is accessed, NGINX refreshes it from the origin server and resets the inactive timer.
  • max_size: sets the upper limit of the size of the cache (to 10 gigabytes in this example). It is optional; not specifying a value allows the cache to grow to use all available disk space. When the cache size reaches the limit, a process called the cache manager removes the files that were least recently used to bring the cache size back under the limit.
  • proxy_cache_path: the file path to the cache folder you created.
  • add_header X-Cache-Status $upstream_cache_status: adds an X-Cache-Status HTTP header in responses to clients

Additional options below may benefit your server in conjunction with the original proxy cache configuration:

  • proxy_cache_revalidate <on|off>: instructs NGINX to use conditional (GET) requests when refreshing content from the origin servers. If a client requests an item that is cached but expired as defined by the cache control headers, NGINX includes the (If-Modified-Since) field in the header of the (GET) request it sends to the origin server. This saves on bandwidth, because the server sends the full item only if it has been modified since the time recorded in the (Last-Modified) header attached to the file when NGINX originally cached it.
  • proxy_cache_min_uses <number>: sets the number of times an item must be requested by clients before NGINX caches it. This is useful if the cache is constantly filling up, as it ensures that only the most frequently accessed items are added to the cache. By default (proxy_cache_min_uses) is set to 1.
  • proxy_cache_background_update <on|off>: The (updating) parameter to the (proxy_cache_use_stale) directive, combined with enabling the (proxy_cache_background_update) directive, instructs NGINX to deliver stale content when clients request an item that is expired or is in the process of being updated from the origin server. All updates will be done in the background. The stale file is returned for all requests until the updated file is fully downloaded.
  • proxy_cache_lock <on|off>: With (proxy_cache_lock) enabled, if multiple clients request a file that is not current in the cache a (MISS), only the first of those requests is allowed through to the origin server. The remaining requests wait for that request to be satisfied and then pull the file from the cache. Without (proxy_cache_lock) enabled, all requests that result in cache misses go straight to the origin server.

Nginx cache in RAM

If your server has the resources, caching into RAM will always be better than caching files to disk, this even applies to the state-of-the-art SSD drives. This is aimed towards self-managed servers and has abundant resources such as ram sitting around doing nothing. You can potentially set up some great memory caching that’ll have immediate impacts on your website loading.

First, create a new directory for caching in RAM:


sudo mkdir -p /cache/nginx/tmpfs

Secondly, mount the created directory in RAM with (tmpfs) using the following command:

sudo mount -t tmpfs -o size=2g tmpfs /cache/nginx/tmpfs

This mounts (/data/nginx/tmpfs) in RAM, allocating 2 GB. This can be adjusted increasing or decreasing. Smaller servers would start with 512MB instead of 2g. However, there is no right or wrong answer as each server is different.

If you need to unmount, execute the following code:

sudo umount /cache/nginx/ramcache

To finish the setup with RAM caching with Nginx, you need to add the following to (/etc/fstab), so when the server is automatically rebooted, the RAM cache directory gets re-created.

Open the (/etc/fstab) file using nano:

sudo nano /etc/fstab

Enter the following and adjust to your cache location and size:


tmpfs /cache/nginx/tmpfs tmpfs defaults,size=1g 0 0

Nginx Proxy Buffering

If you are utilizing an Nginx reverse proxy a good way to increase performance is to use proxy buffering. This is related to the way how Nginx handles the response received from the proxied server. This means that the proxied server is not queried for every client request, but the data is served from the cache if the cached data is still considered valid (lifetime has not passed).

Working example below:

#Enables or disables buffering of responses from the proxied server.
proxy_buffering on;
 
#proxy buffer cache sizes
proxy_buffers 4 256k;
proxy_buffer_size 128k; 
proxy_busy_buffers_size 256k;

WordPress Caching

The above examples work very well with WordPress websites. However, some additional rules will be needed to ensure we are not caching some resources for security reasons amongst the top.

Firstly, stick this above your (location) area that is being cached in your server block file:

set $skip_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $skip_cache 1;
}
if ($query_string != "") {
    set $skip_cache 1;
}

# Don't cache uris containing the following segments
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|^/feed/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") {
    set $skip_cache 1;
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
    set $skip_cache 1;
}

As you can see, we are skipping the cache on (POST) requests with a query string that should always go to PHP and not caching at all URLs containing some everyday things like feeds, sitemaps, etc.

Next, add the following two lines in your cache location:


        proxy_cache_bypass $skip_cache;
        proxy_no_cache $skip_cache;

For newer users wanting to see a complete example, see below in a working environment:

    location / {
        proxy_pass http://webserver;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache my_zone;
        proxy_cache_key $scheme$request_method$proxy_host$request_uri;
        proxy_cache_valid 404 302 1m;
        proxy_cache_valid 200 31d;
        add_header X-Cache-Status $upstream_cache_status;
        proxy_buffering on;
        proxy_buffers 256 16k;
        proxy_buffer_size 32k;
        proxy_http_version   1.1;
        proxy_cache_bypass $skip_cache;
        proxy_no_cache $skip_cache;
    }
}

Nginx Browser Caching

Browser caching is a great way to reduce the load on servers, especially if you run blogs or other similar content. You can set extended time to shopping and bullet forums where dynamic content changes more often than not.

The most common examples of popular catching are below. Enter this in your server block.

  # assets, media
  location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
  expires    365d;
  access_log off;
  }
  
  # svg, fonts
  location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
  add_header Access-Control-Allow-Origin "*";
  expires    365d;
  access_log off;
  }

To save on disk IO and logging, you can declare these resources off in your access log to increase performance on the server if you are struggling with higher load times.

Comments and Conclusion

In the tutorial, you learned how to use the Nginx proxy cache using files and RAM. For further information on Nginx caching, visit Nginx Caching documentation to learn much as it’s pretty extensive and additional options. Extras can be done as the guide has just scratched the surface of what really can be done.


Not what you were looking for? Try searching for additional tutorials.

Leave a Comment