Caching strategy for a page that varies by IP I've created a custom cache context that determines if a request comes from in my campus network or from outside my campus network:

class NetworkContext extends RequestStackCacheContextBase implements CacheContextInterface {

  /**
   * {@inheritdoc}
   */
  public static function getLabel() {
    return t('Network');
  }

  /**
   * {@inheritdoc}
   */
  public function getContext() {
    if (Util::requestFromCampus($this-&gt;requestStack-&gt;getCurrentRequest()-&gt;getClientIp())) {
      return 'oncampus';
    }
    else {
      return 'offcampus';
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata() {
    return new CacheableMetadata();
  }

}


This custom cache context is registered in my module's services file and given the name "network".

I have a custom controller action that returns some data that varies based on if the user is on campus or off campus:

public function myCustomAction(Request $request) {
  $build = [];

  $build['my_data'] = [
    '#theme' =&gt; 'my_custom_theme_function',
    '#rawdata' =&gt; $this-&gt;fetchData(Util::requestIsFromCampus($request-&gt;getClientIp()));
  ];

  // Add some cache metadata that indicates this render array varies
  // based on the user's network. We can't effectively use cache tags
  // here since the data is retrieved from an external service and we
  // don't know when it's updated. Instead, expire the result after
  // one hour.
  $build['#cache'] = [
    'max-age' =&gt; 60 * 60,
    'contexts' =&gt; [
      'network',
    ],
  ];

  return $build;
}


Here's what I've found:

Internal Page Cache won't work here:
The Internal Page Cache module, to my knowledge, doesn't support cache contexts at all. It will store the entire page result for ALL anonymous traffic. If the request that warmed the cache for this page happened to be on campus, off campus users will also see that result, despite their cache context being different. This means I have to tell Internal Page Cache to not cache this page. One way is to add the "no_cache" option to the routing definition, which is what I've done.

Dynamic Page Cache DOES work, but not when I have the "no_cache" option set. I've found that dynamic page cache DOES support cache contexts entirely, so it will correctly vary the response by my custom cache tag! However, I can't keep Dynamic Page Cache working while also turning off Internal Page Cache, because the mechanism to disable one will disable both.

My questions:


Is there a way I can disable internal page cache for a page but NOT dynamic page cache?
Is using the dynamic page cache appropriate here, or am I using it incorrectly? I looked into auto placeholdering but that seems to be for things that are highly dynamic, but my cache context is not. It's either "yes" or "no".
One approach could be to use a middleware to set a query parameter:

  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    $ip = $request-&gt;getClientIp();
    if (Util::requestFromCampus($ip)) {
      $request-&gt;query-&gt;add(['oncampus' =&gt; '1']);
    }
    else {
      // remove the query parameter in case an off-campus user sets it incorrectly
      $request-&gt;query-&gt;remove('oncampus');  
    }
    return $this-&gt;httpKernel-&gt;handle($request, $type, $catch);
  }


Set a priority higher than 200, so that it runs before the page cache. The page cache uses the url including the query string as cache key and so both versions can be delivered from cache, which is good for performance.

As example how to set this up see Drupal\ban\BanMiddleware.

If you use this approach you don't need the custom cache context anymore, you can use a context for the query argument in content that varies for off- and on-campus users.