I have a WP site with managed hosting that uses Varnish. I have a landing page that displays the appropriate images based on a couple arguments passed through the query string. So for example, depending on where the user is requesting this page from, the URL might be www.example.com/landing?v=1 or www.example.com/landing?v=2... and each of those pages would contain a different logo image that is determined by the value of v in the query string.

It seems that the default behavior for Varnish is to serve the cached version of the page and ignore (or maybe strip) any query string parameters. As a result, both of the URLs above display the same exact page (using the default image coded into the page), which is not correct for either.

$logoBase = 'path/to/logo-';
$logoSrc = isset($_GET['v']) ? $logoBase . $_GET['v'] : 'default';
$logoSrc .= '.svg';

<img src="<?php echo $logoSrc ?>" alt="logo"> 

With that, a couple questions:

Ideally I'd like to use the cached version of that page and still display the logo dynamically, but something tells me that's not possible. Is it? If so, is there some configuration file that will tell Varnish to do just that rather than rendering it 100% from the cached version as if there was no query string?

If that is not possible, is there a way to specify that a page is not to be served from the cache and to render it dynamically, fresh from scratch on each request while still serving all other pages from the cache?

And what about Google Analytics UTM parameters? Even though it doesn't affect how the page displays, having those parameters ignored is very problematic. How is that typically handled?

Unless there is a very simple config answer, I'm not asking for any code. I just need a starting point (advice on WP best practice as it pertains too caching and dynamic content) so I can figure out what I can configure myself (and what should be configured differently) and what I'll need to ask the hosting admins to modify.

1 Answer 1


Caching content with query string parameters

Varnish hashes the URL and uses this value as its cache key. When a single value in the URL changes, the cache key changes. This would result in a cache miss.

Query string parameters are exceptionally prone to this: omitting a parameter, adding a parameter, or changing the order of parameters can cause a cache miss.

Adding the following VCL snippet, will ensure that query string parameters are sorted alphabetically, which will increase your hit rate:

import std;

sub vcl_recv {
    set req.url = std.querysort(req.url);

Dynamic logo

The VCL programming language, gives you the flexibility to decide how certain decisions are made on the edge. Despite the fact that values are cached, or that the origin server returns a certain value, you can still change what the client sees by writing some VCL code.

You could in fact capture requests for the logo, and still re-route the request to a different URL internally. You could even introspect query string parameters to compose the URL of the dynamic logo.

The VCL code you need to make this happens, depends on a lot of factors. It's up to you to describe these rules and the required logic.

Cleaning up UTM parameters

Query string parameters that are used by JS libraries for tracking, can be stripped off in VCL. The origin server doesn't need them to render the page, and adding these parameters will only cause more cache misses.

Here's some VCL to clean up your URL:

sub vcl_recv {
  # Remove tracking parameters
  if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
    set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
    set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
    set req.url = regsub(req.url, "\?&", "?");
    set req.url = regsub(req.url, "\?$", "");

  # Strip HTML anchors
  if (req.url ~ "\#") {
    set req.url = regsub(req.url, "\#.*$", "");

  # Strip a trailing ? if it exists
  if (req.url ~ "\?$") {
    set req.url = regsub(req.url, "\?$", "");
  • First of all, some known tracking query string parameters are stripped using a regsuball() find and replace call
  • The next step is to remove HTML anchors from the URL
  • Finally we remove trailing question marks, because they imply that no query string parameters are used


VCL has the necessary syntax to tackle your problems. You'll probably use the regsub() and regsuball() functions to remove the proverbial garbage from your URL to ensure a better hit rate.

You can also perform dynamic decision making using VCL, but you'll have to describe the necessary logic and rules before we can talk about the VCL implementation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.