Cache#

Nginx provides a caching facility that saves responses from proxied URLs and reuses them later. You can enable and configure it using directives that start with proxy_cache. There is a special tutorial on the Nginx website that covers Nginx configuration.

There is a guide for caching at official nginx documentation.

In the next cell, the setup defined earlier is executed, and we access the root page.

docker compose -f ../reverse_proxy_files/docker-compose.yml up -d &> /dev/null

Don’t forget to clean up the environment after everything is done.

docker compose -f ../reverse_proxy_files/docker-compose.yml down &> /dev/null

Check cache status#

There is a special variable called upstream_cache_status that holds the cache status. It is common practice to return this information in the X-Cache-Status header.


The following cell shows the Nginx configuration that adds $upstream_cache_status as X-Cache-Status.

docker exec -i experiment_nginx sh -c 'cat >  /etc/nginx/nginx.conf' << EOF
events {}
http {

    proxy_cache_path /var/cache/nginx/proxy_cache keys_zone=my_cache:10m;

    server {
        listen 80;
        location / {
            proxy_cache my_cache;
            proxy_cache_valid 200 5s;
            proxy_pass http://client_container/bytes/50;
            add_header X-Cache-Status \$upstream_cache_status;
        }
    }
}
EOF

docker exec -it experiment_nginx nginx -s reload
2024/09/10 09:43:30 [notice] 339#339: signal process started

The following cell requests the endpoint under caching several times and shows the values that the X-Cache-Status header takes.

# clean case for prevent influece of the other runs
docker exec experiment_nginx bash \
    -c "rm /var/cache/nginx/proxy_cache/*" &> /dev/null | true

curl -s -I localhost:80 | grep --color=never Cache
curl -s -I localhost:80 | grep --color=never Cache
sleep 10
curl -s -I localhost:80 | grep --color=never Cache
X-Cache-Status: MISS
X-Cache-Status: HIT
X-Cache-Status: EXPIRED

At the beginning, it shows MISS because there is no cache for this request. Then it shows HIT, meaning that Nginx returned a response from the cache. The last request completes after a while, and since the cache record was deleted due to being too old, it shows EXPIRED.

Expire time#

You can regulate expiration type through the proxy_cache_valid directive. Specify status codes and their expiration times within the location directive.


The following cell configures Nginx to expire cached responses with a 200 status code after 5 seconds:

docker exec -i experiment_nginx sh -c 'cat >  /etc/nginx/nginx.conf' << EOF
events {}
http {

    proxy_cache_path /var/cache/nginx/proxy_cache keys_zone=my_cache:10m;

    server {
        listen 80;
        location / {
            proxy_cache my_cache;
            proxy_cache_valid 200 5s;
            proxy_pass http://client_container/bytes/50;
        }
    }
}
EOF

docker exec -it experiment_nginx nginx -s reload
2024/09/09 13:47:11 [notice] 39#39: signal process started

Now let’s check if it works—the following cell sends a request to Nginx every second for some time. After a few seconds, the output changes, indicating that the cache has expired and a new response is generated.

for i in {1..10}; do
    echo $(curl -s localhost:80/ | tr -d '\0')
    sleep 1
done
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
:M��7�e`�|��%��f�ܳgV�L�����YJ�μ �=� [��lu3
�R���y����7���N� ?2a�BI����$�_K���As�%�=��Z
�R���y����7���N� ?2a�BI����$�_K���As�%�=��Z
�R���y����7���N� ?2a�BI����$�_K���As�%�=��Z
�R���y����7���N� ?2a�BI����$�_K���As�%�=��Z

Max size#

In the proxy_cache_path directive, you can set the max_size argument to define the maximum disk space that the corresponding cache path can use.


The following cell defines two cache paths: one with max_size=1m and another with max_size=2m. These paths are tied to different locations.

docker exec -i experiment_nginx sh -c 'cat >  /etc/nginx/nginx.conf' << EOF
events {}
http {

    proxy_cache_path /var/cache/nginx/proxy_one_megabyte 
        keys_zone=one_megabyte:10m 
        max_size=1m;
    proxy_cache_path /var/cache/nginx/proxy_two_megabyte 
        keys_zone=two_megabyte:10m 
        max_size=2m;

    server {
        listen 80;
        

        location /one_megabyte {
            proxy_cache one_megabyte;
            proxy_cache_valid 200 1h;
            proxy_pass http://client_container/bytes/50;
        }
        location /two_megabyte {
            proxy_cache two_megabyte;
            proxy_cache_valid 200 1h;
            proxy_pass http://client_container/bytes/50;
        }
    }
}
EOF

docker exec -it experiment_nginx nginx -s reload
2024/09/09 14:43:50 [notice] 103#103: signal process started

Now, by exploring the corresponding locations, we’ll create logs for both paths. We’ll deliberately generate more cache than the maximum memory specified for each path.

for i in {1..1000}; do
    echo $(curl -s localhost:80/one_megabyte?id=$i | tr -d '\0') &> /dev/null
    echo $(curl -s localhost:80/two_megabyte?id=$i | tr -d '\0') &> /dev/null
done

Now let’s check the sizes of the folders with logs.

docker exec experiment_nginx bash -c "
du -h /var/cache/nginx/proxy_one_megabyte
du -h /var/cache/nginx/proxy_two_megabyte
"
1.1M	/var/cache/nginx/proxy_one_megabyte
2.1M	/var/cache/nginx/proxy_two_megabyte

They are taking approximately 1 and 2 megabytes, as specified in the Nginx configuration.