Search code examples
phpcachingnginxssi

Nginx/SSI independent fragment caching


I'm trying to set up a basic working Nginx+SSI example:

Nginx config (just the relevant parts, for brevity):

ssi on;
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:20m max_size=20m inactive=60m use_temp_path=off;

server {
  listen 80;
  server_name localhost;
  location / {
    proxy_cache  my_cache;
    proxy_set_header  Host $host;
    proxy_http_version  1.1;
    proxy_buffering  on;
    proxy_pass  http://127.0.0.1:81;
  }
}

server {
  listen 81;
  root /path/to/root;
  location ~ ^/.+\.php {
    fastcgi_pass  127.0.0.1:9000;
  }
}

ssi.php:

<?php
header('Cache-Control: public, s-maxage=5');
?>
Time: <?php echo time(); ?>
Fragment time: <!--# include virtual="/time.php" -->

time.php:

<?php
header('Cache-Control: no-cache');
echo time();

The include works nicely:

Time: 1466710388 Fragment time: 1466710388

Now, a second later I would expect the page (ssi.php) to be still cached, but the time.php fragment to be fresh:

Time: 1466710388 Fragment time: 1466710389

However it stays completely the same for 5 seconds, after which the ssi page is updated along with the fragment:

Time: 1466710393 Fragment time: 1466710393

I've done this before with ESI and Varnish, and expect this to work the same with SSI. Am I wrong in assuming this? I can't find an answer online for this, and have experimented with different cache-control headers, but I'm fairly sure this is the right way to do this. What am I missing here?


Solution

  • There is a problem in your config: you have SSI enabled in both servers, due to

    ssi on;
    

    defined at http{} level. This results in SSI directives being expanded in the second server{}. The response as cached in the first server doesn't have any SSI directives in it (there are already expanded), and hence it stays the same all the time.

    If you want included fragment to be fresh on every request, you have to enable SSI only in the first server, e.g.:

    proxy_cache_path /path/to/cache keys_zone=my_cache:20m;
    
    server {
        listen 80;
        server_name first.example.com;
    
        location / {
            proxy_pass  http://127.0.0.1:81;
            proxy_cache my_cache;
            ssi on;
        }
    }
    
    server {
        listen 81;
        server_name second.example.com;
    
        location ~ ^/.+\.php {
            fastcgi_pass  127.0.0.1:9000;
        }
    }
    

    Note that ssi on is in the first server, along with proxy_cache my_cache. This way nginx will cache backend responses with SSI directives in them, and will redo SSI processing on every request, caching includes separately if needed.