Symfony Sulu Varnish using ESI

143 views Asked by At

We are using Symfony (Sulu) with Varnish as reverse proxy, with tag based invalidation (xkey) which works as expected. Now we want to use ESI for dynamic blocks on our website, separate from the "main" cache lifetime.

But each time the webpage is cached in full, even after hard refresh/clear browser cache or using new browser, the page is always displaying the first cache entry.

Using a Docker setup with PHP(port: 9000), NGINX (port: 8000) and Varnish (port: 8081)

We have enabled ESI in Symfony:

framework:
...
    esi: true
    fragments: { path: /_fragment }

And provide render_esi in our Twig templates:

navbar.html.twig

...
            {{ render_esi(controller('App\\Controller\\Website\\LoginButtonController::renderEsiLoginButton')) }}
            <div class="navbar-main__toggler"></div>
        </div>

LoginButtonController

class LoginButtonController extends AbstractController
{
    public function renderEsiLoginButton(): Response
    {
        $response = $this->render('esi/_login_button.html.twig', ['random_nr' => rand()]);

        $response->setMaxAge(10);
        $response->setSharedMaxAge(10);
        $response->setPublic();

        return $response;
    }
}

_login_button.html.twig

<div class="navbar-main__action-account">
    <div class="account__dropdown">
        <a href="#" class="account__user">{{ random_nr }}</a>
        ...
    </div>
</div>

And our Varnish default.vcl:

# /etc/varnish/default.vcl
vcl 4.0;

include "/etc/varnish/sulu.vcl";
include "/etc/varnish/fos_user_context.vcl";
include "/etc/varnish/fos_user_context_url.vcl";
include "/etc/varnish/fos_tags_xkey.vcl";

acl invalidators {
    "localhost";
    "$VARNISH_INVALIDATOR_HOST";
}

backend default {
    .host = "$VARNISH_BACKEND_HOST";
    .port = "$VARNISH_BACKEND_PORT";
}

sub vcl_recv {
    call fos_tags_xkey_recv;
    call fos_user_context_recv;
    call sulu_recv;

    // # Add a Surrogate-Capability header to announce ESI
    set req.http.Surrogate-Capability = "abc=ESI/1.0";

    # Remove all cookies except the session ID (SULUSESSID)
    # Check configured session ID name in your config/packages/framework.yaml
    if (req.http.Cookie) {
        set req.http.Cookie = ";" + req.http.Cookie;
        set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
        set req.http.Cookie = regsuball(req.http.Cookie, ";(SULUSESSID)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

        if (req.http.Cookie == "") {
            # If there are no more cookies, remove the header to get page cached.
            unset req.http.Cookie;
        }
    }

    # No cache besides GET and HEAD
    if (req.method != "GET" && req.method != "HEAD") {
      return (pass);
    }

    # if Authorization or no-cache header we skip the cache
    if (req.http.Authorization || req.http.Cache-Control ~ "no-cache") {
        return (pass);
    }

    # Force the lookup, the backend must tell not to cache or vary on all
    # headers that are used to build the hash.
    return (hash);
}

sub vcl_hash {
    call fos_user_context_hash;
}

sub vcl_backend_response {
    set beresp.grace = 2m;

    call fos_user_context_backend_response;
    call sulu_backend_response;

    // Check for ESI acknowledgement and remove Surrogate-Control header
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }
}

sub vcl_deliver {
    call fos_tags_xkey_deliver;
    call fos_user_context_deliver;
    call sulu_deliver;

    # Include debugging info
    set resp.http.Obj-Hits = obj.hits;
    set resp.http.Seen-Cookie = req.http.Cookie;

    # set resp.http.Vary = "X-User-Context-Hash";
}

Our fos_http_cache config:

fos_http_cache:
    tags:
        enabled: true
        response_header: xkey
        max_header_value_length: 1024
    proxy_client:
        symfony:
            use_kernel_dispatcher: true

    user_context:
        enabled: true
        role_provider: true
#        user_hash_header: 'X-User-Context-Hash'
        hash_cache_ttl: 900

and sulu http cache:

when@prod: &prod
    sulu_http_cache:
        debug:
            enabled: false
        tags:
            enabled: true
        cache:
            max_age: 240
            shared_max_age: 480
        proxy_client:
            symfony:
                enabled: false
            varnish:
                enabled: true
                servers: [ '%env(VARNISH_SERVER)%' ]
                tag_mode: purgekeys

When using curl, we also see the esi:include tags correctly

curl -i -X GET 'http://localhost:8000/en' -H 'Surrogate-Capability: ESI/1.0:

<span class="navbar-main__action-search-button"></span>

<esi:include src="/_fragment?_hash=HAz3Ym5wXab4GXTRKnq8oUR3WwnGqUpprL0niBc%2BEwg%3D&_path=_format%3Dhtml%26_locale%3Den%26_controller%3DApp%255CController%255CWebsite%255CLoginButtonController%253A%253ArenderEsiLoginButton" onerror="continue" />

<div class="navbar-main__toggler"></div>

But the webpage is still cached in full, even after hard refresh/clear browser cache or using new browser, the page is always displaying the first random number. Until we clear the Varnish cache in full.

What are we missing?


Update 1: as requested the output of varnishlog:

$ varnishlog -g request -q "ReqUrl eq '/en'"
*   << Request  >> 65566
-   Begin          req 65565 rxreq
-   Timestamp      Start: 1700910701.474503 0.000000 0.000000
-   Timestamp      Req: 1700910701.474503 0.000000 0.000000
-   VCL_use        boot
-   ReqStart       192.168.207.1 60914 a0
-   ReqMethod      GET
-   ReqURL         /en
-   ReqProtocol    HTTP/1.1
-   ReqHeader      Host: localhost:8081
-   ReqHeader      Connection: keep-alive
-   ReqHeader      sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
-   ReqHeader      sec-ch-ua-mobile: ?0
-   ReqHeader      sec-ch-ua-platform: "macOS"
-   ReqHeader      Upgrade-Insecure-Requests: 1
-   ReqHeader      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
-   ReqHeader      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
-   ReqHeader      Sec-Fetch-Site: none
-   ReqHeader      Sec-Fetch-Mode: navigate
-   ReqHeader      Sec-Fetch-User: ?1
-   ReqHeader      Sec-Fetch-Dest: document
-   ReqHeader      Accept-Encoding: gzip, deflate, br, zstd
-   ReqHeader      Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
-   ReqHeader      X-Forwarded-For: 192.168.207.1
-   VCL_call       RECV
-   ReqHeader      X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
-   ReqUnset       Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
-   ReqHeader      accept: application/vnd.fos.user-context-hash
-   ReqHeader      X-Fos-Original-Url: /en
-   ReqURL         /_fos_user_context_hash
-   VCL_return     hash
-   ReqUnset       Accept-Encoding: gzip, deflate, br, zstd
-   ReqHeader      Accept-Encoding: gzip
-   VCL_call       HASH
-   VCL_return     lookup
-   ExpBan         393224 banned lookup
-   VCL_call       MISS
-   VCL_return     fetch
-   Link           bereq 65567 fetch
-   Timestamp      Fetch: 1700910703.172124 1.697620 1.697620
-   RespProtocol   HTTP/1.1
-   RespStatus     200
-   RespReason     OK
-   RespHeader     Server: nginx
-   RespHeader     Content-Type: application/vnd.fos.user-context-hash
-   RespHeader     X-Powered-By: PHP/8.2.5
-   RespHeader     X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
-   RespHeader     Cache-Control: max-age=900, public
-   RespHeader     Date: Sat, 25 Nov 2023 11:11:43 GMT
-   RespHeader     Vary: cookie, authorization
-   RespHeader     xkey: fos_http_cache_hashlookup- webspace-amfori
-   RespHeader     X-Generator: Sulu/2.5.10
-   RespHeader     x-url: /_fos_user_context_hash
-   RespHeader     x-host: localhost:8081
-   RespHeader     X-Varnish: 65566
-   RespHeader     Age: 0
-   RespHeader     Via: 1.1 varnish (Varnish/6.5)
-   VCL_call       DELIVER
-   RespUnset      xkey: fos_http_cache_hashlookup- webspace-amfori
-   ReqHeader      X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
-   VCL_return     restart
-   Timestamp      Process: 1700910703.172133 1.697630 0.000009
-   Timestamp      Restart: 1700910703.172134 1.697631 0.000000
-   Link           req 65568 restart
-   End
...

Update 2: modified VCL moving Surrogate-Capability to the top:

sub vcl_recv {
    // # Add a Surrogate-Capability header to announce ESI
    set req.http.Surrogate-Capability = "abc=ESI/1.0";

    call fos_tags_xkey_recv;
    call fos_user_context_recv;
    call sulu_recv;
...

Results in working ESI _fragment calls in Varnish varnishlog:

$ varnishlog -g request -q "ReqUrl eq '/en'"
*   << Request  >> 295260
-   Begin          req 295259 rxreq
-   Timestamp      Start: 1701104453.954200 0.000000 0.000000
-   Timestamp      Req: 1701104453.954200 0.000000 0.000000
-   VCL_use        boot
-   ReqStart       192.168.207.1 45193 a0
-   ReqMethod      GET
-   ReqURL         /en
-   ReqProtocol    HTTP/1.1
-   ReqHeader      Host: localhost:8081
-   ReqHeader      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0
-   ReqHeader      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   ReqHeader      Accept-Language: en-US,en;q=0.5
-   ReqHeader      Accept-Encoding: gzip, deflate, br
-   ReqHeader      Connection: keep-alive
-   ReqHeader      Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; userTimez
-   ReqHeader      Upgrade-Insecure-Requests: 1
-   ReqHeader      Sec-Fetch-Dest: document
-   ReqHeader      Sec-Fetch-Mode: navigate
-   ReqHeader      Sec-Fetch-Site: none
-   ReqHeader      Sec-Fetch-User: ?1
-   ReqHeader      Pragma: no-cache
-   ReqHeader      Cache-Control: no-cache
-   ReqHeader      X-Forwarded-For: 192.168.207.1
-   VCL_call       RECV
-   ReqHeader      Surrogate-Capability: abc=ESI/1.0
-   ReqHeader      X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   ReqUnset       Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   ReqHeader      accept: application/vnd.fos.user-context-hash
-   ReqHeader      X-Fos-Original-Url: /en
-   ReqURL         /_fos_user_context_hash
-   VCL_return     hash
-   ReqUnset       Accept-Encoding: gzip, deflate, br
-   ReqHeader      Accept-Encoding: gzip
-   VCL_call       HASH
-   VCL_return     lookup
-   ExpBan         32789 banned lookup
-   VCL_call       MISS
-   VCL_return     fetch
-   Link           bereq 295261 fetch
-   Timestamp      Fetch: 1701104455.867260 1.913060 1.913060
-   RespProtocol   HTTP/1.1
-   RespStatus     200
-   RespReason     OK
-   RespHeader     Server: nginx
-   RespHeader     Content-Type: application/vnd.fos.user-context-hash
-   RespHeader     X-Powered-By: PHP/8.2.5
-   RespHeader     X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
-   RespHeader     Cache-Control: max-age=900, public
-   RespHeader     Date: Mon, 27 Nov 2023 17:00:55 GMT
-   RespHeader     Vary: cookie, authorization
-   RespHeader     xkey: fos_http_cache_hashlookup- webspace-amfori
-   RespHeader     X-Generator: Sulu/2.5.10
-   RespHeader     x-url: /_fos_user_context_hash
-   RespHeader     x-host: localhost:8081
-   RespHeader     X-Varnish: 295260
-   RespHeader     Age: 0
-   RespHeader     Via: 1.1 varnish (Varnish/6.5)
-   VCL_call       DELIVER
-   RespUnset      xkey: fos_http_cache_hashlookup- webspace-amfori
-   ReqHeader      X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
-   VCL_return     restart
-   Timestamp      Process: 1701104455.867277 1.913076 0.000016
-   Timestamp      Restart: 1701104455.867278 1.913077 0.000000
-   Link           req 295262 restart
-   End
**  << BeReq    >> 295261
--  Begin          bereq 295260 fetch
--  VCL_use        boot
--  Timestamp      Start: 1701104453.954671 0.000000 0.000000
--  BereqMethod    GET
--  BereqURL       /_fos_user_context_hash
--  BereqProtocol  HTTP/1.1
--  BereqHeader    Host: localhost:8081
--  BereqHeader    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0
--  BereqHeader    Accept-Language: en-US,en;q=0.5
--  BereqHeader    Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; userTimez
--  BereqHeader    Upgrade-Insecure-Requests: 1
--  BereqHeader    Sec-Fetch-Dest: document
--  BereqHeader    Sec-Fetch-Mode: navigate
--  BereqHeader    Sec-Fetch-Site: none
--  BereqHeader    Sec-Fetch-User: ?1
--  BereqHeader    Pragma: no-cache
--  BereqHeader    X-Forwarded-For: 192.168.207.1
--  BereqHeader    Surrogate-Capability: abc=ESI/1.0
--  BereqHeader    X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
--  BereqHeader    accept: application/vnd.fos.user-context-hash
--  BereqHeader    X-Fos-Original-Url: /en
--  BereqHeader    Accept-Encoding: gzip
--  BereqHeader    X-Varnish: 295261
--  VCL_call       BACKEND_FETCH
--  VCL_return     fetch
--  BackendOpen    25 default 192.168.207.8 8000 192.168.207.10 37256 connect
--  Timestamp      Bereq: 1701104453.955471 0.000800 0.000800
--  Timestamp      Beresp: 1701104455.866936 1.912265 1.911465
--  BerespProtocol HTTP/1.1
--  BerespStatus   200
--  BerespReason   OK
--  BerespHeader   Server: nginx
--  BerespHeader   Content-Type: application/vnd.fos.user-context-hash
--  BerespHeader   Transfer-Encoding: chunked
--  BerespHeader   Connection: keep-alive
--  BerespHeader   X-Powered-By: PHP/8.2.5
--  BerespHeader   X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
--  BerespHeader   Cache-Control: max-age=900, public
--  BerespHeader   Date: Mon, 27 Nov 2023 17:00:55 GMT
--  BerespHeader   Vary: cookie
--  BerespHeader   Vary: authorization
--  BerespHeader   xkey: fos_http_cache_hashlookup- webspace-amfori
--  BerespHeader   X-Generator: Sulu/2.5.10
--  TTL            RFC 900 10 0 1701104456 1701104456 1701104455 0 900 cacheable
--  VCL_call       BACKEND_RESPONSE
--  TTL            VCL 900 120 0 1701104456 cacheable
--  BerespHeader   x-url: /_fos_user_context_hash
--  BerespHeader   x-host: localhost:8081
--  VCL_return     deliver
--  Filters         esi
--  Storage        malloc s0
--  Fetch_Body     2 chunked -
--  BackendClose   25 default recycle
--  Timestamp      BerespBody: 1701104455.867224 1.912553 0.000287
--  Length         0
--  BereqAcct      901 0 901 435 0 435
--  End
*   << Request  >> 295262
-   Begin          req 295260 restart
-   Timestamp      Start: 1701104455.867278 1.913077 0.000000
-   ReqStart       192.168.207.1 45193 a0
-   ReqMethod      GET
-   ReqURL         /_fos_user_context_hash
-   ReqProtocol    HTTP/1.1
-   ReqHeader      Host: localhost:8081
-   ReqHeader      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0
-   ReqHeader      Accept-Language: en-US,en;q=0.5
-   ReqHeader      Connection: keep-alive
-   ReqHeader      Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; userTimez
-   ReqHeader      Upgrade-Insecure-Requests: 1
-   ReqHeader      Sec-Fetch-Dest: document
-   ReqHeader      Sec-Fetch-Mode: navigate
-   ReqHeader      Sec-Fetch-Site: none
-   ReqHeader      Sec-Fetch-User: ?1
-   ReqHeader      Pragma: no-cache
-   ReqHeader      Cache-Control: no-cache
-   ReqHeader      X-Forwarded-For: 192.168.207.1
-   ReqHeader      Surrogate-Capability: abc=ESI/1.0
-   ReqHeader      X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   ReqHeader      accept: application/vnd.fos.user-context-hash
-   ReqHeader      X-Fos-Original-Url: /en
-   ReqHeader      Accept-Encoding: gzip
-   ReqHeader      X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
-   VCL_call       RECV
-   ReqUnset       Surrogate-Capability: abc=ESI/1.0
-   ReqHeader      Surrogate-Capability: abc=ESI/1.0
-   ReqURL         /en
-   ReqUnset       X-Fos-Original-Url: /en
-   ReqUnset       accept: application/vnd.fos.user-context-hash
-   ReqHeader      accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   ReqUnset       X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-   VCL_return     hash
-   VCL_call       HASH
-   VCL_return     lookup
-   ExpBan         32783 banned lookup
-   VCL_call       MISS
-   VCL_return     fetch
-   Link           bereq 295263 fetch
-   Timestamp      Fetch: 1701104456.583858 2.629658 0.716580
-   RespProtocol   HTTP/1.1
-   RespStatus     200
-   RespReason     OK
-   RespHeader     Server: nginx
-   RespHeader     Content-Type: text/html; charset=UTF-8
-   RespHeader     X-Powered-By: PHP/8.2.5
-   RespHeader     Cache-Control: max-age=240, public, s-maxage=480
-   RespHeader     Date: Mon, 27 Nov 2023 17:00:56 GMT
-   RespHeader     Link: </build/website/runtime.67e3af91.js>; rel="preload"; as="script",</build/website/main.c77e8851.js>; rel="preload"; as="script",</build/website/app.56c210d4.css>; rel="preload"; as="style"
-   RespHeader     xkey: account-1 media-9750 media-9268 media-9269 media-9270 media-9586 media-9585 media-9276 media-9582 media-9583 media-9274 media-9610 media-9658 category-2 category-4 category-5 category-6 category-7 a280a8cf-ef54-41af-a576-e2ee364cde7c f76cf5c1-0dd5-
-   RespHeader     X-Generator: Sulu/2.5.10
-   RespHeader     x-url: /en
-   RespHeader     x-host: localhost:8081
-   RespHeader     Content-Encoding: gzip
-   RespHeader     Vary: X-User-Context-Hash, Accept-Encoding
-   RespHeader     X-Varnish: 295262
-   RespHeader     Age: 0
-   RespHeader     Via: 1.1 varnish (Varnish/6.5)
-   VCL_call       DELIVER
-   RespUnset      xkey: account-1 media-9750 media-9268 media-9269 media-9270 media-9586 media-9585 media-9276 media-9582 media-9583 media-9274 media-9610 media-9658 category-2 category-4 category-5 category-6 category-7 a280a8cf-ef54-41af-a576-e2ee364cde7c f76cf5c1-0dd5-
-   RespUnset      Vary: X-User-Context-Hash, Accept-Encoding
-   RespHeader     Vary: , Accept-Encoding
-   RespUnset      Vary: , Accept-Encoding
-   RespHeader     Vary: Accept-Encoding
-   RespUnset      x-url: /en
-   RespUnset      x-host: localhost:8081
-   RespHeader     Obj-Hits: 0
-   RespHeader     Seen-Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; user
-   VCL_return     deliver
-   Timestamp      Process: 1701104456.583879 2.629679 0.000020
-   Filters         esi
-   RespHeader     Accept-Ranges: bytes
-   RespHeader     Connection: keep-alive
-   RespHeader     Transfer-Encoding: chunked
-   Link           req 295264 esi
-   Timestamp      Resp: 1701104456.805554 2.851354 0.221675
-   ReqAcct        770 0 770 880 3654 4534
-   End
**  << BeReq    >> 295263
--  Begin          bereq 295262 fetch
--  VCL_use        boot
--  Timestamp      Start: 1701104455.867403 0.000000 0.000000
--  BereqMethod    GET
--  BereqURL       /en
--  BereqProtocol  HTTP/1.1
--  BereqHeader    Host: localhost:8081
--  BereqHeader    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0
--  BereqHeader    Accept-Language: en-US,en;q=0.5
--  BereqHeader    Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; userTimez
--  BereqHeader    Upgrade-Insecure-Requests: 1
--  BereqHeader    Sec-Fetch-Dest: document
--  BereqHeader    Sec-Fetch-Mode: navigate
--  BereqHeader    Sec-Fetch-Site: none
--  BereqHeader    Sec-Fetch-User: ?1
--  BereqHeader    Pragma: no-cache
--  BereqHeader    X-Forwarded-For: 192.168.207.1
--  BereqHeader    Accept-Encoding: gzip
--  BereqHeader    X-User-Context-Hash: 5224d8f5b85429624e2160e538a3376a479ec87b89251b295c44ecbf7498ea3c
--  BereqHeader    Surrogate-Capability: abc=ESI/1.0
--  BereqHeader    accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
--  BereqHeader    X-Varnish: 295263
--  VCL_call       BACKEND_FETCH
--  VCL_return     fetch
--  BackendOpen    25 default 192.168.207.8 8000 192.168.207.10 37256 reuse
--  Timestamp      Bereq: 1701104455.867506 0.000103 0.000103
--  Timestamp      Beresp: 1701104456.582952 0.715549 0.715446
--  BerespProtocol HTTP/1.1
--  BerespStatus   200
--  BerespReason   OK
--  BerespHeader   Server: nginx
--  BerespHeader   Content-Type: text/html; charset=UTF-8
--  BerespHeader   Transfer-Encoding: chunked
--  BerespHeader   Connection: keep-alive
--  BerespHeader   X-Powered-By: PHP/8.2.5
--  BerespHeader   Cache-Control: max-age=240, public, s-maxage=480
--  BerespHeader   Date: Mon, 27 Nov 2023 17:00:56 GMT
--  BerespHeader   X-Reverse-Proxy-TTL: 86400
--  BerespHeader   Surrogate-Control: content="ESI/1.0"
--  BerespHeader   Link: </build/website/runtime.67e3af91.js>; rel="preload"; as="script",</build/website/main.c77e8851.js>; rel="preload"; as="script",</build/website/app.56c210d4.css>; rel="preload"; as="style"
--  BerespHeader   xkey: account-1 media-9750 media-9268 media-9269 media-9270 media-9586 media-9585 media-9276 media-9582 media-9583 media-9274 media-9610 media-9658 category-2 category-4 category-5 category-6 category-7 a280a8cf-ef54-41af-a576-e2ee364cde7c f76cf5c1-0dd5-
--  BerespHeader   Vary: X-User-Context-Hash
--  BerespHeader   X-Generator: Sulu/2.5.10
--  BerespHeader   Content-Encoding: gzip
--  TTL            RFC 480 10 0 1701104457 1701104457 1701104456 0 480 cacheable
--  VCL_call       BACKEND_RESPONSE
--  TTL            VCL 480 120 0 1701104457 cacheable
--  BerespHeader   x-url: /en
--  BerespHeader   x-host: localhost:8081
--  TTL            VCL 86400 120 0 1701104457 cacheable
--  BerespUnset    X-Reverse-Proxy-TTL: 86400
--  BerespUnset    Surrogate-Control: content="ESI/1.0"
--  VCL_return     deliver
--  Filters         gunzip esi_gzip
--  BerespUnset    Content-Encoding: gzip
--  BerespHeader   Content-Encoding: gzip
--  BerespUnset    Vary: X-User-Context-Hash
--  BerespHeader   Vary: X-User-Context-Hash, Accept-Encoding
--  Storage        malloc s0
--  Fetch_Body     2 chunked -
--  Gzip           G F E 19864 3649 80 29112 29122
--  Gzip           U F - 3997 19864 80 80 31905
--  BackendClose   25 default recycle
--  Timestamp      BerespBody: 1701104456.583813 0.716410 0.000860
--  Length         3649
--  BereqAcct      881 0 881 1087 0 1087
--  End
**  << Request  >> 295264
--  Begin          req 295262 esi
--  Timestamp      Start: 1701104456.583969 0.000000 0.000000
--  ReqURL         /_fragment?_hash=8bu9984nZ8dzK0ti7IeJWjYw%2BVIgXHNOzgK9K%2FxsZak%3D&_path=absolute_uri%3D0%26_format%3Dhtml%26_locale%3Den%26_controller%3DApp%255CController%255CWebsite%255CLoginButtonController%253A%253ArenderEsiLoginButton
--  ReqUnset       Accept-Encoding: gzip, deflate, br
--  ReqHeader      Accept-Encoding: gzip
--  ReqStart       192.168.207.1 45193 a0
--  ReqMethod      GET
--  ReqURL         /_fragment?_hash=8bu9984nZ8dzK0ti7IeJWjYw%2BVIgXHNOzgK9K%2FxsZak%3D&_path=absolute_uri%3D0%26_format%3Dhtml%26_locale%3Den%26_controller%3DApp%255CController%255CWebsite%255CLoginButtonController%253A%253ArenderEsiLoginButton
--  ReqProtocol    HTTP/1.1
--  ReqHeader      Host: localhost:8081
--  ReqHeader      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0
--  ReqHeader      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
--  ReqHeader      Accept-Language: en-US,en;q=0.5
--  ReqHeader      Connection: keep-alive
--  ReqHeader      Cookie: trusted_device=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDA4MzEwNzYuMjIxNzQ1LCJleHAiOjE3MDYwMTUwNzYuMjE5NDQ2LCJ1c3IiOiJzYW5kZXIuaG9mbWFuQGljYXBwcy5jb20iLCJmd2wiOiJhZG1pbiIsInZzbiI6MH0.haaj4WNSp4inor3rWD1FlUm52qXwmWEJ3mkmgTEtX4Q; userTimez
--  ReqHeader      Upgrade-Insecure-Requests: 1
--  ReqHeader      Sec-Fetch-Dest: document
--  ReqHeader      Sec-Fetch-Mode: navigate
--  ReqHeader      Sec-Fetch-Site: none
--  ReqHeader      Sec-Fetch-User: ?1
--  ReqHeader      Pragma: no-cache
--  ReqHeader      Cache-Control: no-cache
--  ReqHeader      Accept-Encoding: gzip
--  ReqHeader      X-Forwarded-For: 192.168.207.1
--  VCL_call       RECV
--  ReqHeader      Surrogate-Capability: abc=ESI/1.0
--  ReqHeader      X-Fos-Original-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
--  ReqUnset       Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
--  ReqHeader      accept: application/vnd.fos.user-context-hash
--  ReqHeader      X-Fos-Original-Url: /_fragment?_hash=8bu9984nZ8dzK0ti7IeJWjYw%2BVIgXHNOzgK9K%2FxsZak%3D&_path=absolute_uri%3D0%26_format%3Dhtml%26_locale%3Den%26_controller%3DApp%255CController%255CWebsite%255CLoginButtonController%253A%253ArenderEsiLoginButton
--  ReqURL         /_fos_user_context_hash
...
2

There are 2 answers

6
Thijs Feryn On BEST ANSWER

By the looks of it, you're doing everything right. But some targeted varnishlog commands will give us a better understanding of how Varnish is caching the various fragments.

I'm going to need you to send me the output of the following command:

sudo varnishlog -g request -q "ReqUrl eq '/'"

My assumption is that the issue is occurring on the homepage. If that's not the case, alter the filter in the varnishlog command.

It's important that you run this command on an empty cache!

When Varnish performs the various fetches, we can have a look at the TTL tags in the logs. This will tell us how Varnish decided on the TTL of each fragment (either through a TTL set in the VCL code, or through the Cache-Control header.

The logs will also return the various ESI subrequests as separate log transactions, making it easier to understand what happened when.

Please put the log output in your original question and I'll help you figure out what's going on.

Update

I'm not seeing the Surrogate-Capability header in your (backend) request. Without this header, Symfony will not trigger the Surrogate-Control header which enables ESI processing in Varnish. Despite seeing Surrogate-Capability in your VCL code.

However I'm seeing call fos_user_context_backend_response; , call sulu_backend_response; & call sulu_recv; in the VCL code right before you set the header. Chances are that a return statement is made there, which bypasses your header.

My suggestion would be to move set req.http.Surrogate-Capability = "abc=ESI/1.0"; to the beginning of the vcl_recv subroutine.

Just ensure the header gets set and the logic doesn't get bypassed by other calls.

An easy way to fix this is to move this logic to the vcl_backend_fetch subroutine because setting the header is related to a fetch and not to receiving a request. Here's the VCL code:

sub vcl_backend_fetch {
    set bereq.http.Surrogate-Capability = "abc=ESI/1.0";
}
1
john Smith On

Only thing that comes directly to my eye comparing this to the symfony ESI-Docs

is that

each ESI tag requires a fully-qualified URL.

you have a relative link in the src attribute of the esi tag, you could try using the absolute_uri option of render_esi (IMHO it does not really make sense though that this defaults to false)

But yeah, i think its worth a try