Question

Varnish stripping PHPSESSID from header

It is a magento 2.3.3 site with PHP 7.3 and Varnish 6. It looks like varnish is stripping PHPSESSID. Is this normal behaviour or some workaround needs to be done in VCL code? This is how the curl request look like. PHPSESSID seems to appear in non-www headers but not the www headers. Is this varnish issue or nginx configuration issue? Below is my nginx conf.

curl -I https://example.com
HTTP/2 302 
server: nginx
date: Tue, 17 Dec 2019 12:10:46 GMT
content-type: text/html; charset=UTF-8
set-cookie: PHPSESSID=u1erp4gte3ja7d5bll9u6knsfl; expires=Wed, 18-Dec-2019 12:10:46 GMT; Max-Age=86400; path=/; domain=.example.com; secure; HttpOnly
location: https://www.example.com/
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
vary: Accept-Encoding
pragma: no-cache
expires: -1
cache-control: no-store, no-cache, must-revalidate, max-age=0


curl -I https://www.example.com
HTTP/2 200 
server: nginx
date: Tue, 17 Dec 2019 12:10:38 GMT
content-type: text/html; charset=UTF-8
content-length: 54847
vary: Accept-Encoding
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
pragma: no-cache
expires: -1
cache-control: no-store, no-cache, must-revalidate, max-age=0
accept-ranges: bytes

server {
    listen      443 ssl http2;
    server_name example.com www.example.com;
    ssl_protocols       TLSv1.2  TLSv1.3;

    ssl_certificate      /home/example/conf/web/ssl.example.com.pem;
    ssl_certificate_key  /home/example/conf/web/ssl.example.com.key;

    access_log  /var/log/nginx/domains/example.com.log combined;
    access_log  /var/log/nginx/domains/example.com.bytes bytes;
    error_log   /var/log/nginx/domains/example.com.error.log error;

    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_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen    8080;
    server_name example.com www.example.com;
    set $MAGE_ROOT /home/example/web/example.com/public_html;
    set $MAGE_MODE production; # or production

    include     /home/example/web/example.com/public_html/nginx.conf.sample;
    include     /etc/nginx/conf.d/phpmyadmin.inc*;
    include     /etc/nginx/conf.d/phppgadmin.inc*;
    include     /etc/nginx/conf.d/webmail.inc*;
    include     /home/example/conf/web/snginx.example.com.conf*;
}

Submit an answer


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Sign In or Sign Up to Answer

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

KFSys
Site Moderator
Site Moderator badge
May 7, 2024

Based on the details you provided about the behavior of PHPSESSID cookies being stripped out when accessing your Magento site with “www” as part of the URL versus without, it appears this might be related to your Varnish configuration rather than an Nginx issue.

Understanding the Problem

Varnish, by default, is designed to strip out or ignore cookies for caching purposes because cookies indicate personalized content that should not be cached. If your Magento site’s behavior relies on cookies for sessions (as is typical), and you see them being dropped, you need to configure Varnish to handle these cookies appropriately.

Steps to Troubleshoot & Solve

1. Review Varnish Configuration

You need to ensure Varnish is configured to pass through the necessary cookies. This is done typically in your VCL files, especially in sub vcl_recv and sub vcl_backend_response. Look for any lines that unset cookies or strip cookies and adjust accordingly.

For a typical Magento setup, you’d want to ensure that the PHPSESSID cookie is not being stripped. You might see lines in your Varnish VCL such as:

if (!(req.url ~ "admin|customer|checkout")) {
    unset req.http.cookie;
}

This line would typically strip cookies from pages that are not part of admin, customer, or checkout processes, which might be incorrectly configured or overly aggressive.

2. Adjust the VCL to Properly Handle Cookies

You should modify your VCL to make sure it does not strip out the PHPSESSID cookie for the pages where it’s needed. An example adjustment could be:

if (req.http.Cookie) {
    set req.http.Cookie = ";" + req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
        unset req.http.Cookie;
    }
}

This script carefully handles the PHPSESSID cookie, ensuring it is not stripped out by Varnish’s default behavior.

3. Ensure Correct Proxy Headers in Nginx

Your Nginx configuration shows you are passing the necessary headers to Varnish, which is good. Ensure that the proxy headers, particularly related to cookies and session IDs, are correctly configured so that when Varnish sends requests back to Nginx, they are handled appropriately.

4. Testing Changes

After making changes to your Varnish VCL:

  • Restart Varnish.
  • Clear your browser cookies or use a private browsing session to test the changes.
  • Use curl -I https://www.example.com to inspect the headers and confirm that the PHPSESSID cookie is being sent as expected.

5. Debugging Further

If problems persist:

  • Check the Varnish logs (varnishlog) for details on how cookies are being processed.
  • Confirm there are no additional configurations or scripts in Magento that might affect cookie handling differently on www versus non-www URLs.

Final Notes

Since caching and session management are critical aspects of both performance and functionality in web applications like Magento, ensuring that these are aligned with your caching strategy (in this case, Varnish) is crucial. Adjustments might be needed based on your specific site behavior and traffic patterns. If the issue is complex or critical, consulting with a specialist in Magento and Varnish could provide more personalized and in-depth assistance.

Thanks you for responding. Below is the VCL file.

# 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: 'X-Forwarded-Proto: https'

backend default {
    .host = "localhost";
    .port = "8080";
    .first_byte_timeout = 600s;
    .probe = {
        .url = "/pub/health_check.php";
        .timeout = 2s;
        .interval = 5s;
        .window = 10;
        .threshold = 5;
   }
}

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.X-Forwarded-Proto;
        #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.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);
    }
}

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);
    }
}

Bobby Iliev
Site Moderator
Site Moderator badge
December 20, 2019

Hello,

Your Nginx config looks all correct. I believe that this would be Varnish that’s causing the issue for you.

Could you share your Varnish config here so that I could have a look and try to advise you further?

Regards, Bobby

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel