3

I have a php website running on a DigitalOcean droplet (nginx server), and I also use Cloudflare. Recently, my website was hit by something that I yet did not understand.. as my CPU usage was usually 1-2% and it went to 100% for a few hours resulting in my server crashing.

My Cloudflare analytics showed an extremely high number of visitors - of which I am sure was not human traffic.

Cloudflare asked to see my nginx error logs and access logs. Following is their reply -

You have 2 caching headers that are going to expire your assets as soon as they get stored at our edge.

< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

and

< Expires:> Thu, 19 Nov 1981 08:52:00 GMT. 

Because the Expires header is set to sometime in the past, the cache is going to be expired the moment it hits our edge. Then you have your cache-control header basically saying do not store any of the assets at our edge, but you have a caching enabled, so our edge will continue to crawl your site. This can cause what looks like a DoS attack against your origin.

Can someone please tell me how can I set my Expires header and Cache-control header on my Nginx server?

!

Dave M
  • 4,494
  • 21
  • 30
  • 30
codemode
  • 169
  • 1
  • 2
  • 8

3 Answers3

6

You have an incorrect setting in your php.ini file:

The session.cache_limiter value is set to nocache in the default php.ini file and needs to be changed.

session.cache_limiter should be defined and set, either to public which inserts public cache-control headers, or to '' (blank), which doesn't insert any cache-control headers, and the headers sent by your application will then be used, if any.

Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
  • This is not necessarily true, if using Nginx then it will overwrite the application's HTTP headers (if exist) anyways depending on the configuration. Focusing on Nginx instead of PHP is a better approach when dealing with HTTP headers, esp. for PHP-FPM... you can leave the setting `session.cache_limiter = nocache` and then set all your HTTP headers using Nginx configuration only, it's much cleaner. – Jesse Nickles Sep 01 '22 at 08:28
4

I've found that with Wordpress (some themes) and other PHP applications sometimes it's near to impossible to get the headers set correctly using PHP. My solution is that I just ignore the headers the application sets and override them with what I want in Nginx. I find this much simpler, much faster, and much more reliable.

If you can work out how to do it from your application and PHP, great, but this way gives you lots of control and it's easy. Building nginx with the required module is easier than you might think, it can probably done in 30 minutes based on a guide I link to below.

I have a guide how to set up Nginx and Cloudflare.

Short version of caching header:

  • You want cache-control
  • You don't want pragma

For images use something like this in your location - you need headers-more module compiled into nginx. See part 1 of my tutorial for that.

add_header Cache-Control "public, max-age=691200, s-maxage=691200";
more_clear_headers Server; more_clear_headers "Pragma"; more_clear_headers "Expires";

For pages I don't cache on CloudFlare, but I do use nginx fast_cgi page cache, which is in my tutorial.

For CloudFlare you may need to use page rules if you need anything tricky, but in general the default settings will cache images but not pages.

Tim
  • 30,383
  • 6
  • 47
  • 77
  • This is probably all good general advice, but it doesn't really get to the root cause of the problem. – Michael Hampton Apr 24 '16 at 01:25
  • I probably should've said why I gave this advice, rather than answering directly. I'll update my answer. – Tim Apr 24 '16 at 01:56
  • Well then. If you ever want to know why PHP screws up these headers, you can read the other answer. :) – Michael Hampton Apr 24 '16 at 02:05
  • In my case I think it was because of poor Wordpress theme that didn't consider caching at all, as the default theme was fine. Good to have different answers though :) – Tim Apr 24 '16 at 02:28
  • 1
    Tim answer is very good, and so is the articles he wrote, but Michael's solution solved the base of the problem. Thanks to both of you! – codemode Apr 25 '16 at 12:57
  • Tim's answer is more on the right track, and one of the only places I've seen online where someone is (correctly) recommending to disable `Pragma` and `Expires` and use only `Cache-Control`... this needs to be evangelized more. – Jesse Nickles Sep 01 '22 at 08:49
0

Most cloud servers using PHP and Nginx these days are going to be using PHP-FPM (or should be)... for cleaner configuration I recommend keeping session.cache_limiter = nocache in your php.ini and then overwriting HTTP headers using Nginx instead, it's more reliable.

For SlickStack, my open source LEMP script, we go even farther by universally disabling certain cache headers in the nginx.conf file like this:

more_clear_headers "Pragma Expires";

...this ensures random WordPress plugins (etc) can't mess with our caching headers.

And then in the Nginx server block, we enable only the Cache-Control header for static assets to ensure CDNs like Cloudflare will properly cache them at the edge:

location ~* \.(atom|bmp|bz2|css|doc|docx|eot|gif|gz|ico|jpeg|jpg|js|mid|midi|mp4|ogg|ogv|otf|png|ppt|rar|rss|rtf|svg|svgz|tar|tgz|ttc|ttf|wav|webp|woff|woff2|xls|zip)$ {
    add_header Cache-Control "public, max-age=691200";
}

This enables a TTL of around 1 week... but in Cloudflare, you can make this even longer by visiting the Caching tab and setting the "Browser Cache TTL" setting to something like 1 month. (For extra surety, you can also create a Page Rule in Cloudflare that forces an Edge Cache TTL of e.g. 1 month as well for any requests to your /wp-content/* directory where all your static assets should be loading from.)

To lock things down even more, you can disable the Cache-Control header in the PHP routing server block to ensure pages don't get cached in the browser:

location ~ \.php$ {
    more_clear_headers "Cache-Control";
    ...

If you have clients who love to mess with settings, this approach can be a life-saver.

Jesse Nickles
  • 250
  • 1
  • 12