I've been trying to understand how Authcache's 2.0-beta7 core.vcl file (Varnish 3) handles Cache-Control:private for fragments served through AJAX (authcache_ajax). These are my findings so far:

  • Cache-Control:private is set in AuthcacheP13nAddCacheControlHeaderFilter, only for PER_USER granularity:
          if ($this->cacheGranularity->is(AuthcacheP13nCacheGranularity::PER_USER)) {
            $this->coreService->drupalAddHttpHeader('Cache-Control', 'private, max-age=' . $this->cacheMaxage);
          }
    
  • Checking for private caching + setting Vary:Cookie is done in vcl_fetch:
      // Ensure that the result is cached individually for each session if
      // Cache-Control header on the response of an Authcache ESI request
      // contains the "private" keyword.
      if (bereq.http.X-Authcache-Do-ESI && bereq.http.X-Authcache) {
        if (beresp.http.Cache-Control ~ "(private)" && beresp.http.Vary !~ "Cookie") {
          set beresp.http.Vary = beresp.http.Vary + ", Cookie";
          set beresp.http.Vary = regsub(beresp.http.Vary, "^,\s*", "");
          // Remove the private directive from the Cache-Control response header
          // such that the fragment gets stored by Varnish 4.
          set beresp.http.Cache-Control = regsub(beresp.http.Cache-Control, "(^|,\s*)private", "");
          set beresp.http.Cache-Control = regsub(beresp.http.Cache-Control, "^,\s*", "");
        }
        elseif (beresp.http.Cache-Control ~ "(public)" && beresp.http.Vary !~ "X-Authcache-Key") {
          set beresp.http.Vary = beresp.http.Vary + ", X-Authcache-Key";
          set beresp.http.Vary = regsub(beresp.http.Vary, "^,\s*", "");
        }
      }
    
  • Passing the checks requires both X-Authcache-Do-ESI and X-Authcache.
    • X-Authcache is set in authcache_ajax.js:
      xhr.setRequestHeader('X-Authcache','1');
      
    • X-Authcache-Do-ESI is filtered out if included in the incoming request (vcl_recv):
        /**
         * Do not allow the client to pass in X-Authcache-Get-Key header unless the
         * VCL is under test (varnishtest uses -n /tmp/vtc.XXXXX.YYYYYYYY).
         */
        if (req.restarts == 0 && server.identity !~ "^/tmp/vtc.") {
          unset req.http.X-Authcache-Do-ESI;
          unset req.http.X-Authcache-Get-Key;
          unset req.http.X-Authcache-Key-CID;
          unset req.http.X-Authcache-Key;
        }
      
    • X-Authcache-Do-ESI is set only if req.http.X-Authcache-Get-Key != "skip" && !req.http.X-Authcache (vcl_recv):
        // Tell the backend that ESI processing is available. The Authcache ESI
        // module will only emit tags if this header is present on the request.
        // If the X-Authcache key is already present on an incoming request (e.g.
        // triggered by Authcache Ajax), do not enable ESI.
        if (req.http.X-Authcache-Get-Key != "skip" && !req.http.X-Authcache) { // <----- Notice the "!"
          set req.http.X-Authcache-Do-ESI = 1;
        }
      
        // Add authcache-header for ESI requests going to the authcache_p13n front
        // controller.
        if (req.http.X-Authcache-Do-ESI && req.esi_level > 0) {
          set req.http.X-Authcache = 1;
        }
      
  • Given that authcache_ajax sets X-Authcache, it looks like Cache-Control:private won't be handled for fragments retrieved via AJAX.

I may be missing some other details, but I tried grepping around to see if X-Authcache-Do-ESI was set from some other place and couldn't find it.

Also, I decided to file this support request since the site I'm working in has a heavily customized vcl file, which makes it difficult to ran some requests for analyzing this behavior.

Thanks in advance!

Comments

jedihe created an issue. See original summary.

jedihe’s picture

Issue summary: View changes
znerol’s picture

it looks like Cache-Control:private won't be handled for fragments retrieved via AJAX.

Your analysis is correct. When using Ajax, the fragment is supposed to be stored in the browser cache. The VCL only attempts to store fragments which are retrieved using ESI.

Personally I think that ESI is only really useful/efficient for fragments which are reusable among multiple users. For personalized fragments (per user granularity) there is little reason to store every combination of user x fragment in a server-side cache.

The VCL is opinionated in this regard and follows that assumption.

jedihe’s picture

Status: Active » Closed (fixed)

Thx @znerol for such a quick answer!

The use-case that I'm wanting these fragments cached for is a form served via personalization in a panel pane. Given that this means a full Drupal bootstrap and that I'm seeing high load times for that specific request as I browse around and back to the same page, I'm needing to cache the fragment in Varnish. Also, the form is the same across pages of the same content type, which makes it even more useful for me to have it cached.

But now that I know what Authcache is expected to do, I can assess correctly that implementing this will be better done without touching the existing logic, but just augmenting it for my additional use-case.

Thx again!

znerol’s picture

Do you need to alter the form structure or just the default values?

jedihe’s picture

The main requirement is to personalize the form by pre-populating the e-mail field. Also, the form submits via AJAX, so I need a working form cache (cache object API is installed and configured); also, submitting the form triggers a subscription action, which means I better not remove the token.

For now, I have a working implementation that appends X-Authcache-Key-CID as Vary rule in the form constructor; I also removed the line unsetting that header in vcl_recv. At this point I'm trying to generalize the cache management so that just setting "Per User" granularity on a fragment will trigger Vary:X-Authcache-Key-CID in Varnish. I'm going with this vary rule instead of Vary:Cookie since the latter seems to not perform as well in my tests.

znerol’s picture

Authcache Form in combination with the Cache Object API module has all the logic to make AJAX enabled form cachable for authenticated users (including wrestling the CSRF token and the form build id). This is battle tested with the comment form.

The Authcache User module is capable of prepopulating user data in the contact form. You might find that helpful.

jedihe’s picture

This is great information, thx a lot!. I'll take a look at Authcache User and see what I can borrow from it.