3
votes

I'm having trouble setting up Varnish to properly handle session cookie for ESI-included subrequests.

Background, SSCCE

Three files: index.php, navigation.php and footer.php combined together using ESI, where first two files are stateful, but only index.php is cachable, while footer.php is completley stateless.

### index.php
<?php

session_start();

header('Cache-Control: public, s-maxage=10');
header('Surrogate-Control: content="ESI/1.0"');

?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />

        <title>Varnish test</title>
    </head>
    <body>
        <esi:include src="http://localhost/WWW/navigation.php" />
        <p>Primary content: <?php echo date('r') ?></p>
        <esi:include src="http://localhost/WWW/footer.php" />
    </body>
</html>

### footer.php
<?php

header('Cache-Control: public, s-maxage=20');

?>
<p>Footer: <?php echo date('r') ?></p>

### navigation.php
<?php

session_start();

if (!isset($_SESSION['counter'])) {
    $_SESSION['counter'] = 0;
}

$_SESSION['counter'] += 1;

header('Cache-Control: private');

?>
<p>Navigation: <?php echo $_SESSION['counter'] ?></p>

Varnish VCL configuration:

sub vcl_recv {
    set req.http.Surrogate-Capability = "abc=ESI/1.0";
}

sub vcl_fetch {
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;

        set beresp.do_esi = true;
    }
}

sub vcl_deliver {
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}

Desired result

I'd like to see index.php/footer.php loaded from the cache, purged every 10/20 seconds, while navigation.php loaded directly from backend server. Of course the very first request to index.php cannot be cached as Set-Cookie header must be set, however footer.php could be loaded from cache from the very begining.


How could I achieve that? I'm aware that presence of Cookie header in HTTP request causes cache-miss, but I can't simply delete it.

1
What is your current result? Is nothing cached? or everything? Is esi available? Check here.ferdynator
@byf-ferdy Opsss... it's not at all. When I started writing I've used example from Symfony, but later I decied it only obscured the question and I've decided to use "plain-PHP" example.Crozin
@byf-ferdy Right now Cookie header is available on every (sub)request therefore Varnish is not able to cache anything.Crozin

1 Answers

1
votes

Rather a comment than an answer, but to long to fit in one.

The above example lacks of escaping Varnish default VCL [1], Varnish will always append its default logic to your VCL code [2] if you don't avoid it.

In this case you need at least:

sub vcl_recv {
  set req.http.Surrogate-Capability = "abc=ESI/1.0";
  return (lookup);
}

Varnish also won't cache any response including a Set-Cookie header unless you force it.

[1] https://www.varnish-cache.org/docs/3.0/reference/vcl.html#examples

[2] https://www.varnish-software.com/static/book/VCL_Basics.html#the-vcl-state-engine