3

I have recently switched an opencart instance from Apache+mod_php to nginx+fastcgi+php-fpm. I have been trying to leverage caching for most pages through fastcgi-cache.

Unfortunately, many users began reporting ghost orders or taking over others accounts (weeee!!!!) From exhaustive digging, it appears that pages were cached with set-cookie! So subsequent users who did not send a pre-existing session cookie were getting the cache-initiator's session cookie. Bad!

According to all the documentation out there, the following settings are supposed to prevent this from happening (to the best of my understanding at least:)

 fastcgi_pass_header Set-Cookie;
 fastcgi_pass_header Cookie;
 fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

When I looked through the individual caches I noticed several pages with set-cookie: [somerandomsessionid] According to nginx documentation under fastcgi_cache_valid...

If the header includes the “Set-Cookie” field, such a response will not be cached.

By including Set-Cookie with fastcgi_ignore_headers, am I telling it to cache set-cookie? In many examples, Set-Cookie is part of the arguments to fastcgi_ignore_headers. Or is it supposed to prevent Set-Cookie from being processed even though it's obviously in the cached files?

Here are the pertinent parts of my configuration:

location ~ .php$ { ...

fastcgi_next_upstream error timeout invalid_header http_500 http_503;
fastcgi_cache OPENCART;
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
fastcgi_cache_purge $purge_method;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_valid 200 5m;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_pass_header Set-Cookie;
#fastcgi_hide_header Set-Cookie;
fastcgi_pass_header Cookie;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

My cache bypass rules (called in /etc/conf.d)...

################## Fast CGI Cache Settings

# if we find a PHP session cookie, let's cache it's contents

map $http_cookie $php_session_cookie {
    default "";
    ~PHPSESSID=(?<sessionkey>[a-zA-Z0-9]+) $sessionkey; # PHP session cookie
}

fastcgi_cache_path /var/nginx/cache levels=1:2 keys_zone=OPENCART:5m max_size=10000m inactive=15m;
fastcgi_cache_key "$scheme$request_method$host$request_uri$is_mobile$php_session_cookie";

map $request_method $purge_method {
    PURGE   1;
    default 0;
}

################## Cache Header

add_header X-Cache $upstream_cache_status;

################## Cache Bypass Maps

#Don't cache the following URLs
map $request_uri $no_cache_uri {
    default 0;
    ~*/admin/ 1;
    ~*/dl/ 1;
}

# ~*/music/mp3_[^/]+/[0-9]+/.+$ 1;

map $query_string $no_cache_query {
    default 0;
    ~*route=module/cart$ 1;
    ~*route=account/ 1; #exclude account links
    ~*route=checkout/ 1; #exclude checkout links
    ~*route=module/founders 1;
    ~*route=module/cart 1;
    ~*route=product/product/captcha 1;
    ~*nocache=1 1; # exclude ajax blocks and provide for manual cache override
}

map $http_cookie $no_cache_cookie {
    default 0;
}  

map $http_x_requested_with $no_cache_ajax {
    default 0;
    XMLHttpRequest 1; # Don't cache AJAX
}

map $sent_http_x_no_cache $no_no_cache {
    default 0;
    on 1; # Don't cache generic header when present and set to "on"
}

## Combine all results to get the cache bypass mapping.
map $no_cache_uri$no_cache_query$no_cache_cookie$no_cache_ajax$no_no_cache $no_cache {
        default 1;
        00000 0;
}

Session setting in php.ini

session.auto_start = 1
session.cache_expire = 180
session.cache_limiter = nocache
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_secure = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 3600
session.gc_probability = 0
session.hash_function = "sha256"
session.name = PHPSESSID
session.serialize_handler = php
session.use_cookies = 1
session.use_only_cookies = 1
session.use_strict_mode = 1
session.use_trans_sid = 0

Opencart using session_start() on every page load so bypassing php session does me no good for the most part. If there were a way to prevent Set-Cookie headers from ending up in the cache, this would probably work for me.

Can anyone point me in the right direction?

Bretticus
  • 175
  • 3
  • 10

2 Answers2

0

You should also check what is in your cached files.

I had for example I had in "Set-Cookie" in some cached files.

vi /var/www/cache/prod/a/05/9671214cbf3a27f79135a52cbd5b305a

Set-Cookie: Mywebsite=arj4m9egloj9jhrlsps7cu29ec; expires=Fri, 08-Jun-2018 14:39:21 GMT; Max-Age=2592000; path=/

I deleted all my cached files.

rm -rf /var/www/cache/prod/*

And now I check if there is any new Set-Cookie in a new cached file:

grep -rn  "Set-Cookie" /var/www/cache/prod/

The best solution I found is to prevent in the PHP part from setting a session Cookie when the page will be cached :

if( (strpos($_SERVER['REQUEST_URI'], 'include/php/render') === FALSE) &&
    (!isBot()) ) { 
    // No session Cookie for PHP render Images
    // No session Cookie Bots (They create one for each page visisted!)
    $session_lifetime = 30*24*3600; //30 days
    session_set_cookie_params($session_lifetime,"/");
    ini_set('session.gc_maxlifetime', $session_lifetime);
    ini_set('session.name', 'Blackart');
    session_start();
}
London Smith
  • 197
  • 3
  • 8
0

Out of the box nginx doesn't cache pages with a Set-Cookie header (which makes sense - private data!) unless you put it in fastcgi_ignore_headers

fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

you told nginx to ignore Set-Cookie header and cache anyway.

But to make it clear I don't know your server but I guess all your dynamic pages sent a cookie to keep the session.

find out if opencart sends a specific cookie if the user is logged in then test for this cookie.

Other method: Remove the CacheControl header from ignore_headers and set the correct header in your code. for php

header('Cache-Control: public'); 

if you want to cache the page

Michael
  • 3
  • 1
  • 4
  • What is your answer here really? I think your entry would be better posted as a comment. – asdmin May 25 '19 at 11:19
  • he wonders why private data is cached and I. tell him because he ignores Set-Cookie. But removing Set-Cookie from ignore_headers is also wrong (because he will fail to cache many cachable pages so he better test for a specific cookie (don't know his application) – Michael May 25 '19 at 17:22