0

I just added varnish cache to my site. Before I added it, I was able to get onto the admin panel. After adding it, though, trying to get to the admin panel results in an infinite redirect loop.

It redirects with the following http codes:

302->307->302->307...

It does this on all browsers even when I clear browser cache.

Here is my varnish config:

# VCL version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 6
vcl 4.0;

import std;
# The minimal Varnish version is 6.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'SSL-OFFLOADED: https'

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

acl purge {
    "localhost";
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purge) {
            return (synth(405, "Method not allowed"));
        }
        # To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header
        # has been added to the response in your backend server config. This is used, for example, by the
        # capistrano-magento2 gem for purging old content from varnish during it's deploy routine.
        if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) {
            return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required"));
        }
        if (req.http.X-Magento-Tags-Pattern) {
          ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
        }
        if (req.http.X-Pool) {
          ban("obj.http.X-Pool ~ " + req.http.X-Pool);
        }
        return (synth(200, "Purged"));
    }

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

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

    # Bypass shopping cart, checkout and search requests
    if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") {
        return (pass);
    }

    # Bypass health check requests
    if (req.url ~ "/pub/health_check.php") {
        return (pass);
    }

    # Set initial grace period usage status
    set req.http.grace = "none";

    # normalize url in case of leading HTTP scheme and domain
    set req.url = regsub(req.url, "^http[s]?://", "");

    # collect all cookies
    std.collect(req.http.Cookie);

    # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
            # 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" && req.http.user-agent !~ "MSIE") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            unset req.http.Accept-Encoding;
        }
    }

    # Remove all marketing get parameters to minimize the cache objects
    if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
        set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
        set req.url = regsub(req.url, "[?|&]+$", "");
    }

    # Static files caching
    if (req.url ~ "^/(pub/)?(media|static)/") {
        # Static files should not be cached by default
        return (pass);

        # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
        #unset req.http.Https;
        #unset req.http.SSL-OFFLOADED;
        #unset req.http.Cookie;
    }

    return (hash);
}

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
    }

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # To make sure http users don't see ssl warning
    if (req.http.SSL-OFFLOADED) {
        hash_data(req.http.SSL-OFFLOADED);
    }
    

    if (req.url ~ "/graphql") {
        call process_graphql_headers;
    }
}

sub process_graphql_headers {
    if (req.http.Store) {
        hash_data(req.http.Store);
    }
    if (req.http.Content-Currency) {
        hash_data(req.http.Content-Currency);
    }
}

sub vcl_backend_response {

    set beresp.grace = 3d;

    if (beresp.http.content-type ~ "text") {
        set beresp.do_esi = true;
    }

    if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;
    }

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 404) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.uncacheable = true;
        set beresp.ttl = 86400s;
        return (deliver);
    }

    # validate if we need to cache it and prevent from setting cookie
    if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
        unset beresp.http.set-cookie;
    }

   # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
   if (beresp.ttl <= 0s ||
       beresp.http.Surrogate-control ~ "no-store" ||
       (!beresp.http.Surrogate-Control &&
       beresp.http.Cache-Control ~ "no-cache|no-store") ||
       beresp.http.Vary == "*") {
        # Mark as Hit-For-Pass for the next 2 minutes
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
    }

    return (deliver);
}

sub vcl_deliver {
    if (resp.http.X-Magento-Debug) {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Magento-Cache-Debug = "HIT";
            set resp.http.Grace = req.http.grace;
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
        }
    } else {
        unset resp.http.Age;
    }

    # Not letting browser to cache non-static files.
    if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
        set resp.http.Pragma = "no-cache";
        set resp.http.Expires = "-1";
        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
    }

    unset resp.http.X-Magento-Debug;
    unset resp.http.X-Magento-Tags;
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # Hit within TTL period
        return (deliver);
    }
    if (std.healthy(req.backend_hint)) {
        if (obj.ttl + 300s > 0s) {
            # Hit after TTL expiration, but within grace period
            set req.http.grace = "normal (healthy server)";
            return (deliver);
        } else {
            # Hit after TTL and grace expiration
            return (restart);
        }
    } else {
        # server is not healthy, retrieve from cache
        set req.http.grace = "unlimited (unhealthy server)";
        return (deliver);
    }
}

And Here is Nginx config (Actual site name replaced with example.com):

## Example configuration:
upstream fastcgi_backend {
    # use tcp connection
    # server  127.0.0.1:9000;
    # or socket
    server   unix:/var/run/php/php7.3-fpm.sock;
 }
# server {
#    listen 80;
#    server_name mage.dev;
#    set $MAGE_ROOT /var/www/magento2;
#    set $MAGE_DEBUG_SHOW_ARGS 0;
#    include /vagrant/magento2/nginx.conf.sample;
# }
#
## Optional override of deployment mode. We recommend you use the
## command 'bin/magento deploy:mode:set' to switch modes instead.
##
## set $MAGE_MODE default; # or production or developer
##
## If you set MAGE_MODE in server config, you must pass the variable into the
## PHP entry point blocks, which are indicated below. You can pass
## it in using:
##
## fastcgi_param  MAGE_MODE $MAGE_MODE;
##
## In production mode, you should uncomment the 'expires' directive in the /static/ location block

# Modules can be loaded only at the very beginning of the Nginx config file, please move the line below to the main config file
# load_module /etc/nginx/modules/ngx_http_image_filter_module.so;

server {

listen 8080;

server_name example.com *.example.com;

set $MAGE_ROOT /var/www/html/example.com;
set $MAGE_DEBUG_SHOW_ARGS 0;

root $MAGE_ROOT/pub;

index index.php;
autoindex off;
charset UTF-8;
error_page 404 403 = /errors/404.php;
#add_header "X-UA-Compatible" "IE=Edge";


# Deny access to sensitive files
location /.user.ini {
    deny all;
}

# PHP entry point for setup application
location ~* ^/setup($|/) {
    root $MAGE_ROOT;
    location ~ ^/setup/index.php {
        fastcgi_pass   fastcgi_backend;

        fastcgi_param  PHP_FLAG  "session.auto_start=off \n suhosin.session.cryptua=off";
        fastcgi_param  PHP_VALUE "memory_limit=756M \n max_execution_time=600";
        fastcgi_read_timeout 600s;
        fastcgi_connect_timeout 600s;

        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location ~ ^/setup/(?!pub/). {
        deny all;
    }

    location ~ ^/setup/pub/ {
        add_header X-Frame-Options "SAMEORIGIN";
    }
}

# PHP entry point for update application
location ~* ^/update($|/) {
    root $MAGE_ROOT;

    location ~ ^/update/index.php {
        fastcgi_split_path_info ^(/update/index.php)(/.+)$;
        fastcgi_pass   fastcgi_backend;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO        $fastcgi_path_info;
        include        fastcgi_params;
    }

    # Deny everything but index.php
    location ~ ^/update/(?!pub/). {
        deny all;
    }

    location ~ ^/update/pub/ {
        add_header X-Frame-Options "SAMEORIGIN";
    }
}

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

location /pub/ {
    location ~ ^/pub/media/(downloadable|customer|import|custom_options|theme_customization/.*\.xml) {
        deny all;
    }
    alias $MAGE_ROOT/pub/;
    add_header X-Frame-Options "SAMEORIGIN";
}

location /static/ {
    # Uncomment the following line in production mode
    # expires max;

    # Remove signature of the static files that is used to overcome the browser cache
    location ~ ^/static/version\d*/ {
        rewrite ^/static/version\d*/(.*)$ /static/$1 last;
    }

    location ~* \.(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|eot|ttf|otf|woff|woff2|html|json|webmanifest)$ {
        add_header Cache-Control "public";
        add_header X-Frame-Options "SAMEORIGIN";
        expires +1y;

        if (!-f $request_filename) {
            rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last;
        }
    }
    location ~* \.(zip|gz|gzip|bz2|csv|xml)$ {
        add_header Cache-Control "no-store";
        add_header X-Frame-Options "SAMEORIGIN";
        expires    off;

        if (!-f $request_filename) {
           rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last;
        }
    }
    if (!-f $request_filename) {
        rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last;
    }
    add_header X-Frame-Options "SAMEORIGIN";
}

location /media/ {

## The following section allows to offload image resizing from Magento instance to the Nginx.
## Catalog image URL format should be set accordingly.
## See https://docs.magento.com/user-guide/configuration/general/web.html#url-options
#   location ~* ^/media/catalog/.* {
#
#       # Replace placeholders and uncomment the line below to serve product images from public S3
#       # See examples of S3 authentication at https://github.com/anomalizer/ngx_aws_auth
#       # resolver 8.8.8.8;
#       # proxy_pass https://<bucket-name>.<region-name>.amazonaws.com;
#
#       set $width "-";
#       set $height "-";
#       if ($arg_width != '') {
#           set $width $arg_width;
#       }
#       if ($arg_height != '') {
#           set $height $arg_height;
#       }
#       image_filter resize $width $height;
#       image_filter_jpeg_quality 90;
#   }

    try_files $uri $uri/ /get.php$is_args$args;

    location ~ ^/media/theme_customization/.*\.xml {
        deny all;
    }

    location ~* \.(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|eot|ttf|otf|woff|woff2)$ {
        add_header Cache-Control "public";
        add_header X-Frame-Options "SAMEORIGIN";
        expires +1y;
        try_files $uri $uri/ /get.php$is_args$args;
    }
    location ~* \.(zip|gz|gzip|bz2|csv|xml)$ {
        add_header Cache-Control "no-store";
        add_header X-Frame-Options "SAMEORIGIN";
        expires    off;
        try_files $uri $uri/ /get.php$is_args$args;
    }
    add_header X-Frame-Options "SAMEORIGIN";
}

location /media/customer/ {
    deny all;
}

location /media/downloadable/ {
    deny all;
}

location /media/import/ {
    deny all;
}

location /media/custom_options/ {
    deny all;
}

location /errors/ {
    location ~* \.xml$ {
        deny all;
    }
}

# PHP entry point for main application
location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check)\.php$ {
    try_files $uri =404;
    fastcgi_pass   fastcgi_backend;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_param HTTPS on;
    fastcgi_param  PHP_FLAG  "session.auto_start=off \n suhosin.session.cryptua=off";
    fastcgi_param  PHP_VALUE "memory_limit=756M \n max_execution_time=18000";
    fastcgi_read_timeout 600s;
    fastcgi_connect_timeout 600s;

    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

gzip on;
gzip_disable "msie6";

gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types
    text/plain
    text/css
    text/js
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    image/svg+xml;
gzip_vary on;

# Banned locations (only reached if the earlier PHP entry point regexes don't match)
location ~* (\.php$|\.phtml$|\.htaccess$|\.git) {
    deny all;
}

}

server {
    listen 443 ssl; # managed by Certbot
    
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

       location / {
       proxy_pass http://127.0.0.1:6081;
       proxy_set_header Host               $http_host;
       proxy_set_header X-Forwarded-Host   $http_host;
       proxy_set_header X-Real-IP          $remote_addr;
       proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto  https;
       proxy_set_header X-Forwarded-Port   443;
       proxy_buffer_size                   128k;
       proxy_buffers                       4 256k;
       proxy_busy_buffers_size             256k;
       fastcgi_buffer_size                 32k;
       fastcgi_buffers                     4 32k;
   }
}

server {
 listen 80;
     location / {
          proxy_pass http://127.0.0.1:6081/;
          proxy_set_header Host $host;  
          proxy_set_header X-Forwarded-Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Ssl-Offloaded "1";
      proxy_set_header      X-Forwarded-Port 443;
      proxy_set_header X-Forwarded-Proto https;
      proxy_set_header X-Forwarded-Ssl on;

}
}

I have tried the following things, none of which worked:

https://mrvts.wordpress.com/2018/12/17/magento-2-nginx-varnish-full-page-cache-backend-admin-redirect-loop-why-and-how-to-fix/ https://stackoverflow.com/questions/10915731/infinite-redirection-loop-in-varnish-cache

It only happens for the admin panel. The frontend works as expected.

UPDATE:

Here is my new nginx.conf file, I'm only showing the listen 80 and listen 443 sections as the rest is the same as above:

server {
    listen 443 ssl; # managed by Certbot

    ssl_certificate /etc/letsencrypt/live/mytestdev.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mytestdev.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

       location / {
       proxy_pass http://127.0.0.1:6081;
       proxy_set_header Host               $http_host;
       proxy_set_header X-Forwarded-Host   $http_host;
       proxy_set_header X-Real-IP          $remote_addr;
       proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
       proxy_set_header Ssl-Offloaded "1";
       proxy_set_header X-Forwarded-Proto  https;
       proxy_set_header X-Forwarded-Port   443;
       proxy_buffer_size                   128k;
       proxy_buffers                       4 256k;
       proxy_busy_buffers_size             256k;
       fastcgi_buffer_size                 32k;
       fastcgi_buffers                     4 32k;
   }
}

server {
 listen 80;
     location / {
          proxy_pass http://127.0.0.1:6081/;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-Host $http_host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header      X-Forwarded-Port 443;
          proxy_set_header Ssl-Offloaded "0";
          proxy_set_header X-Forwarded-Proto http;
          proxy_set_header X-Forwarded-Ssl on;

}
}

Here is the new varnish config, only including the vcl_hash:

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
    }

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # To make sure http users don't see ssl warning
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }


    if (req.url ~ "/graphql") {
        call process_graphql_headers;
    }
}

sub process_graphql_headers {
    if (req.http.Store) {
        hash_data(req.http.Store);
    }
    if (req.http.Content-Currency) {
        hash_data(req.http.Content-Currency);
    }
}

UPDATE 2:

Here is Varnish Log output. I had to add it as external text file as it was too big to fit on this question:

https://newtextdocument.com/032b7cb9a8

1 Answer 1

1

The problem you're probably experiencing is the fact that Magento thinks you're connecting through using regular HTTP request, despite the fact that you terminated your TLS connection in Nginx.

Varnish Cache doesn't handle TLS natively, but facilitates it

Varnish Cache, the open source version of Varnish, doesn't support native TLS. This means TLS has to be terminated elsewhere.

However, Varnish facilitates TLS by offering support of the PROXY protocol, which keeps track of client connection information regardless of the number of hops.

The proxy vmod in Varnish actually has a proxy.is_ssl() function to determine this.

The Varnish community also provides a very convenient and super powerful TLS proxy called Hitch.

However, a lot of people still use Nginx or other proxy servers for TLS termination. Although they are not as efficient as Hitch over the PROXY protocol, they still do a good enough job.

Magento needs to be made aware of the request protocol

Varnish Cache also doesn't support backend connections over TLS. This means the connection between Varnish and Magento is done over plain HTTP, despite the fact that the incoming request was done over HTTPS.

The PROXY protocol is not supported in the open source version of Nginx, so that doesn't help either.

Luckily there is a convention in HTTP to keep track of proxy information. The X-Forwarded-Proto header is often used to announce the request protocol that was used by the client. The value is either https or http and Magento supports it.

How does one get stuck in a redirect loop and what does TLS have to do with this?

If the first request to a resource is done over plain HTTP, the X-Forwarded-Proto header will be set to http by Nginx, Varnish will not have it in cache and will issue a cache miss. This means that Magento, which is served via another Nginx vhost, will try to ebforce an HTTPS connection.

An HTTP/301 redirect will be issued by Magento and the HTTPS version of that resource will be requested.

Varnish is not aware of the context and will cache this 301 response. The next request, which might be an HTTPS request will result in a Varnis cache hit, but that cache object contains the 301 redirect.

That's how you get stuck in a redirect loop.

How the standard VCL file for Magento tackles this redirect loop

Magento is aware of this behavior and tackles the issue of the redirect loop by adding the following if-statement to the sub vcl_hash {} part of its standard VCL file:

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

As you can see, before looking up the object in cache, a cache key is created. The standard behavior uses the req.url and req.http.host headers as the basis for this key. This makes sense because the host header and the URL are the unique identifier of an HTTP resource.

The standard behavior is not protocol aware, so the Magento VCL extends the cache key by adding the req.http.X-Forwarded-Proto header.

The end result is a cache variation for HTTP requests, and a cache variation for the HTTPS requests. Potential HTTPS redirects only get cached if X-Forwarded-Proto is http.

Why are you experiencing this problem?

Your VCL file doesn't use X-Forwarded-Proto, but uses SSL-OFFLADED as you can see in the summarized output below:

sub vcl_hash {
    if (req.http.SSL-OFFLOADED) {
        hash_data(req.http.SSL-OFFLOADED);
    }
}

It is entirely possible to Magento supports the SSL-OFFLOADED header if configured properly, but the problem is that you didn't set it in your Nginx HTTPS vhost.

This is what you set in the listen 443 vhost:

proxy_set_header X-Forwarded-Proto  https;

And this is what you set in the listen 80 vhost:

proxy_set_header Ssl-Offloaded "1";
proxy_set_header X-Forwarded-Proto  https;

Your HTTPS vhost is missing the SSL-OFFLOADED header, and your HTTP vhost has the wrong values for these headers

How to solve the problem:

Set the following values in your listen 443 Nginx vhost:

proxy_set_header Ssl-Offloaded "1";
proxy_set_header X-Forwarded-Proto  https;

Set the following values in your listen 80 Nginx vhost:

proxy_set_header Ssl-Offloaded "0";
proxy_set_header X-Forwarded-Proto  http;

Remove the SSL-OFFLOADED if-statement from your VCL file and make sure the following statement is used inside sub vcl_hash {}:

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

How to test if the fix works

I'm obviously making assumptions on the reason why you're stuck in a redirect loop. Although the problem and solution I'm describing is a very common one, there's no guarantee that you're experiencing the same issue.

Luckily, Varnish has excellent logging tools that go into tremendous detail.

If your problem wasn't solved using my solution, please run the following command and then try to simulate the problem again:

varnishlog -g request -q "ReqUrl eq '/'"

Important detail: I'm assuming the problem occurs on the homepage. If that's not the case, please modify the URL in the varnishlog command.

Please add the output of the log to your question. It may clarify what's going on.

Update

Now that you updated your question and attached a link to the varnishlog output, I have noticed something interesting.

When you request http://example.com/admin, the request contains all the necessary headers to announce that the initial request scheme was https://.

However, the 302 redirect sends you to an http:// endpoint. Here's the one that was listed in the logs:

Isn't this supposed to be an https:// URL? And can that explain why you're stuck?

My suggestion is to look at your Magento configuration and ensure that your admin URL uses HTTPS.

According to HTTPS not working on Magento2 backend, you can check this using the following SQL query:

select * from  core_config_data  where path = 'web/secure/use_in_adminhtml';

You can potentially fix it using the following query:

INSERT INTO core_config_data (scope,scope_id,path,value) VALUES ('default',0,'web/secure/use_in_adminhtml',1);

7
  • Thanks for your reply. I made the above changes, but it didn't work. The admin panel still gets caught in an infinite redirect loop. I restarted varnish and nginx after making changes. I posted the new varnish and nginx configs to make sure I did it right.
    – Eric Brown
    Apr 19, 2021 at 15:57
  • I also cleared browser cache and cookies and tested on multiple browsers. The infinite redirect still occurs on all of them.
    – Eric Brown
    Apr 19, 2021 at 15:59
  • @EricBrown I added a section about logging to my answer. Please run the logging command, then simulate the problem and add the output to your question. Because you're stuck in an infinite loop, you'll get a whole lot of output. Please just send add the logs for the first 2 full transactions. Please also Varnish's cache is empty before attempting this. Apr 20, 2021 at 7:05
  • Sorry it took so long. I finally got around to posting the log file. I updated my question.
    – Eric Brown
    Apr 22, 2021 at 16:17
  • 1
    @EricBrown Long live varnishlog, because without it we wouldn't have found the answer. Apr 23, 2021 at 15:00

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.