0
votes

We have been having an issue in which users schedule Gravity Forms in Wordpress to be available and unavailable at certain dates/times. The issue is that Varnish is caching the page so unless we manually go in and re-save the page (which on save is causing a purge of the page) or manually purge the cache these scheduled forms are not appearing or disappearing as expected. I can't seem to find a way to get around this aside from purging the entire cache nightly or some other "hacks" like making a forms sub-site that's not cached.

Below is our very ugly VCL file for reference as well.

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

# Only allow purging from specific IPs
include "lib/purge.vcl";
acl purge {
    "localhost";
    "127.0.0.1";
}

# Cache static assets
include "lib/static.vcl";

sub vcl_recv {
    # Handle compression correctly. Different browsers send different
    # "Accept-Encoding" headers, even though they mostly support the same
    # compression mechanisms. By consolidating compression headers into
    # a consistent format, we reduce the cache size and get more hits.
    # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
    if (req.http.Accept-Encoding) {
        if (req.http.Accept-Encoding ~ "gzip") {
            # If the browser supports it, we'll use gzip.
            set req.http.Accept-Encoding = "gzip";
        }
        else if (req.http.Accept-Encoding ~ "deflate") {
            # Next, try deflate if it is supported.
            set req.http.Accept-Encoding = "deflate";
        }
        else {
            # Unknown algorithm. Remove it and send unencoded.
            unset req.http.Accept-Encoding;
        }
    }

    # Set client IP
    if (req.http.x-forwarded-for) {
        set req.http.X-Forwarded-For =
        req.http.X-Forwarded-For + ", " + client.ip;
    } else {
        set req.http.X-Forwarded-For = client.ip;
    }

    # Check if we may purge (only localhost)
    if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
            error 405 "Not allowed.";
        }
        return(lookup);
    }

    if (req.request != "GET" &&
        req.request != "HEAD" &&
        req.request != "PUT" &&
        req.request != "POST" &&
        req.request != "TRACE" &&
        req.request != "OPTIONS" &&
        req.request != "DELETE") {
            # /* Non-RFC2616 or CONNECT which is weird. */
            return (pipe);
    }

    if (req.request != "GET" && req.request != "HEAD") {
        # /* We only deal with GET and HEAD by default */
        return (pass);
    }

    # admin users always miss the cache
    if( req.url ~ "^/wp-(login|admin)" ||
        req.http.Cookie ~ "wordpress_logged_in_" ){
            return (pass);
    }

    # Remove cookies set by Google Analytics (pattern: '__utmABC')
    if (req.http.Cookie) {
        set req.http.Cookie = regsuball(req.http.Cookie,
            "(^|; ) *__utm.=[^;]+;? *", "\1");
        if (req.http.Cookie == "") {
            remove req.http.Cookie;
        }
    }

    # always pass through POST requests and those with basic auth
    if (req.http.Authorization || req.request == "POST") {
        return (pass);
    }

    # Do not cache these paths
    if (req.url ~ "^/wp-cron\.php$" ||
        req.url ~ "^/xmlrpc\.php$" ||
        req.url ~ "^/wp-admin/.*$" ||
        req.url ~ "^/wp-includes/.*$" ||
        req.url ~ "\?s=") {
            return (pass);
    }

    # Define the default grace period to serve cached content
    set req.grace = 30s;

    # Allow native WP page passwords to function
    if( req.http.Cookie ~ "wp-postpass_" ){
            return (pass);
    }

    # Allow password protected plugin to function
    if( req.http.Cookie ~ "bid_" ){

    # Drop any other cookies
    if (!(req.url ~ "wp-(login|admin)")) {
       unset req.http.cookie;
    }

    # By ignoring any other cookies, it is now ok to get a page
    unset req.http.Cookie;
    return (lookup);
}

sub vcl_fetch {
    # remove some headers we never want to see
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;

    # only allow cookies to be set if we're in admin area
    if (!(req.url ~ "wp-(login|admin)")) {
        unset beresp.http.set-cookie;
    }

    # don't cache response to posted requests or those with basic auth
    if ( req.request == "POST" || req.http.Authorization) {
        return (hit_for_pass);
    }

    # don't cache search results
    if( req.url ~ "\?s=" ){
        return (hit_for_pass);
    }

    # only cache status ok
    if ( beresp.status != 200 ) {
        return (hit_for_pass);
    }

    # If our backend returns 5xx status this will reset the grace time
    # set in vcl_recv so that cached content will be served and
    # the unhealthy backend will not be hammered by requests
    if (beresp.status == 500) {
        set beresp.grace = 60s;
        return (restart);
    }

    # GZip the cached content if possible
    if (beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;
    }

    # if nothing abovce matched it is now ok to cache the response
    set beresp.ttl = 24h;
    return (deliver);
}
sub vcl_deliver {
    # remove some headers added by varnish
    unset resp.http.Via;
    unset resp.http.X-Varnish;
}

sub vcl_hit {
    # Set up invalidation of the cache so purging gets done properly
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
    return (deliver);
}

sub vcl_miss {
    # Set up invalidation of the cache so purging gets done properly
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
    return (fetch);
}

sub vcl_error {
    if (obj.status == 503) {
                # set obj.http.location = req.http.Location;
                set obj.status = 404;
        set obj.response = "Not Found";
                return (deliver);
    }
}
1

1 Answers

1
votes

The easiest solution would be to just not cache the pages with forms at all. For example:

sub vcl_recv {
    if( req.url ~ "^/(contact|order)" )
        return (pass);
    }
}

sub vcl_fetch {
    if( req.url ~ "^/(contact|order)" )
        return (hit_for_pass);
    }
}

Alternatively you could specify the Expires headers of the form pages so that they expire when the form is out of schedule. That would require changes to the Gravity Forms add-on, though.

A third option would be to set a TTL shorter than your now default 24h to pages using forms. For example 15 minutes. That way they would still be cached, but wouldn't be available for more than 15 minutes beyond the scheduled time.