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.
I deleted all my cached files.
And now I check if there is any new Set-Cookie in a new cached file:
The best solution I found is to prevent in the PHP part from setting a session Cookie when the page will be cached :
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
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
if you want to cache the page