0
votes

I installed Varnish in a Docker image using Plesk CPanel on a WordPress website with pretty permalinks enabled. I used this guide:

https://www.plesk.com/blog/product-technology/varnish-wordpress-docker-container/

When I click on any link to an article or category, I am redirected back to the homepage with a question mark inserted and the rest of the URL. ie

https://example.com/?news/world-leaders-to-meet/

instead of

https://example.com/news/world-leaders-to-meet/

Everything works fine when I disable Varnish. I even tried cloning the website to a subdomain and permalinks work on the subdomain with Varnish enabled. I have disabled plugins and it makes no difference.

What should I do to fix the problem?

Contents of default.vcl (with fake IP address for host)



    vcl 4.0;
    import std;

    # MAIN CONFIGURATION
    backend default {
        .host = "123.123.123.123";
        .port = "7080";
    }

    # ALLOWED IP OF PURGE REQUESTS
    acl purge {
      "localhost";
      "127.0.0.1";
      "172.17.0.1";
      "172.17.0.2";
      "123.123.123.123";
    }

    #THE RECV FUNCTION
    # Happens before we check if we have this in cache already
    sub vcl_recv {

        # SET REALIP BY TRIMMING CLOUDFLARE IP WHICH WILL BE USED FOR VARIOUS CHECKS
        set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", ""); 

        # FORWARD THE IP OF THE REQUEST
        if (req.restarts == 0) {
            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;
            }
        }

        # Purge request check sections for hash_always_miss, purge and ban
        # BLOCK IF NOT IP is not in purge acl
        # Enable smart refreshing using hash_always_miss
        if (req.http.Cache-Control ~ "no-cache") {
            if (client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) {
                set req.hash_always_miss = true;
            }
        }

        if (req.method == "PURGE") {
            if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) {
                return(synth(405,"Not allowed."));
                }
            return (purge);
        }

        if (req.method == "BAN") {
                # Same ACL check as above:
                if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) {
                                return(synth(403, "Not allowed."));
                }
                ban("req.http.host == " + req.http.host +
                        " && req.url == " + req.url);

                # Throw a synthetic page so the
                # request won't go to the backend.
                return(synth(200, "Ban added"));
        }

        # UNSET CLOUDFLARE COOKIES
        # Remove has_js and CloudFlare/Google Analytics __* cookies.
            set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");
            # Remove a ";" prefix, if present.
            set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");

        # For Testing: If you want to test with Varnish passing (not caching) uncomment
        # return( pass );

        # DO NOT CACHE RSS FEED
        if (req.url ~ "/feed(/)?") {
        return ( pass ); 
        }

        # PASS WP-CRON
        if (req.url ~ "wp-cron\.php.*") {
        return ( pass );
        }

        # DO NOT CACHE POST AND EDIT PAGES
        if (req.url ~ "(wp-admin|post\.php|edit\.php|wp-login)") {
            return(pass);
        }

        # DO NOT CACHE SEARCH RESULTS
        if (req.url ~ "/\?s\=") {
        return ( pass ); 
        }

        # CLEAN UP THE ENCODING HEADER.
        # SET TO GZIP, DEFLATE, OR REMOVE ENTIRELY.  WITH VARY ACCEPT-ENCODING
        # VARNISH WILL CREATE SEPARATE CACHES FOR EACH
        # DO NOT ACCEPT-ENCODING IMAGES, ZIPPED FILES, AUDIO, ETC.
        if (req.http.Accept-Encoding) {
            if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
            } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
            } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
            } else {
            # unknown algorithm
            unset req.http.Accept-Encoding;
            }
        }

        # PIPE ALL NON-STANDARD REQUESTS
        if (req.method != "GET" &&
            req.method != "HEAD" &&
            req.method != "PUT" && 
            req.method != "POST" &&
            req.method != "TRACE" &&
            req.method != "OPTIONS" &&
            req.method != "DELETE") {
            return (pipe);
        }

        # ONLY CACHE GET AND HEAD REQUESTS
        if (req.method != "GET" && req.method != "HEAD") {
            return (pass);
        }

        # DO NOT CACHE LOGGED IN USERS (THIS OCCURS IN FETCH TOO)
        if ( req.http.cookie ~ "wordpress_logged_in|resetpass" ) {
            return (pass);
        }

        # FIX CLOUDFLARE MIXED CONTENT WITH FLEXIBLE SSL
        if (req.http.X-Forwarded-Proto) {
            return (hash);
        }

        # IF THE REQUEST IS NOT FOR A PREVIEW, WP-ADMIN OR WP-LOGIN THEN UNSET THE COOKIES
        if (!(req.url ~ "wp-(login|admin)") && !(req.url ~ "&preview=true" )) {
            unset req.http.cookie;
        }

        # IF BASIC AUTH IS ON THEN DO NOT CACHE
        if (req.http.Authorization || req.http.Cookie) {
            return (pass);
        }

        # IF YOU GET HERE THEN THIS REQUEST SHOULD BE CACHED
        return (hash);
        # This is for phpmyadmin
        if (req.http.Host == "pmadomain.com") {
        return (pass);
        }

        # STRIP OUT URL PARAMETERS THAT LEAD TO MULTIPLE REDUNDANT CACHING
        if (req.url ~ "(\?|&)(gclid|utm_[a-z]+)=") {
        set req.url = regsuball(req.url, "(gclid|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
        set req.url = regsub(req.url, "[?|&]+$", "");
        }
    }

    sub vcl_hash {
        if (req.http.X-Forwarded-Proto) {
            hash_data(req.http.X-Forwarded-Proto);
            }
        }

    # HIT FUNCTION
    sub vcl_hit {
      return (deliver);
    }

    # MISS FUNCTION
    sub vcl_miss {
      return (fetch);
    }

    # FETCH FUNCTION
    sub vcl_backend_response {
        # IF NOT WP-ADMIN THEN UNSET COOKIES AND SET THE AMOUNT OF TIME THIS PAGE WILL STAY CACHED (TTL)
        if (!(bereq.url ~ "wp-(login|admin)") && !bereq.http.cookie ~ "wordpress_logged_in|resetpass" ) {
            unset beresp.http.set-cookie;
            set beresp.ttl = 1w;
            set beresp.grace = 3d;
        }

        if (beresp.ttl  0) {
             # IF THIS PAGE IS ALREADY CACHED THEN RETURN A 'HIT' TEXT
            set resp.http.X-Cache = "HIT";
        } else {
            # IF THIS IS A MISS RETURN THAT IN THE HEADER
            set resp.http.X-Cache = "MISS";
        }
    }

1

1 Answers

0
votes

Your VCL code looks pretty legit. My guess is that it might be the URL rewriting logic in your webserver (Apache or Nginx), or maybe even WordPress that does this redirection.

Varnishlog output

The easiest way to find out is by running varnishlog while the issue is occurring. Feel free to post a redacted record of the varnishlog output.

If you need help operating Varnishlog, have look at the following blog post I wrote a couple of years ago: https://feryn.eu/blog/varnishlog-measure-varnish-cache-performance/.

Lack of SSL awareness

Another possibility is that WordPress is not aware of the fact that an HTTP-base reverse caching proxy sits in front if it.

Varnish currently doesn't offer native SSL/TLS support, so SSL needs to be terminated and interactions between Varnish & WordPress happen over plain HTTP.

If WordPress is set up to automatically redirect plain HTTP requests into HTTPS request, you might end up in a loop.

If that is the case, please have a look at the following blog post I wrote about this: https://feryn.eu/blog/mixed-content-and-err-too-many-redirects-in-wordpress/