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?


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
    ini_set('session.gc_maxlifetime', $session_lifetime);
    ini_set('session.name', 'Blackart');

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

  • What is your answer here really? I think your entry would be better posted as a comment. – asdmin May 25 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 at 17:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.