<p>As highlighted in Brad Bell's answer, those headers are likely set by PHP. By default PHP has the <code>session_cache_limiter()</code> method set to <code>nocache</code>. You can verify this in your Craft admin panel under <code>Utilities &gt; PHP Info &gt; session.cache_limiter</code>.</p>

<p>If you change this value to <code>public</code>, it will set a cache expiry according to the value of <code>session.cache_expire</code> (set to <code>180</code> by default). This is explained <a href="https://www.php.net/manual/en/function.session-cache-limiter.php" rel="nofollow noreferrer">here</a> in the PHP docs.</p>

<p>I did not heed Brandon Kelly's advice, and I set <code>session.cache_limiter=public</code> in my php.ini file. I do not know which unintended consequences he speaks of, but I like to live dangerously.</p>

<p>Here are the new headers I get:</p>

<pre><code>Cache-Control: public, max-age=10800
Expires: Wed, 27 Nov 2019 11:54:38 GMT (180 minutes from now)
</code></pre>

<p>If you do this, it will break a few things in the admin. Admin pages won't always load properly because the wrong things will be cached. It works perfectly on the website though.</p>

CC BY-SA 4.0
2019-11-27T08:51:50.437Z
2020-02-13T15:01:35.623Z
2020-02-13T15:01:35.623Z
nicbou
<p>This had me confused for a while: if you're using <a href="https://getnitro.sh/" rel="nofollow noreferrer">Craft Nitro</a>, the Nginx config adds <code>Cache-Control: no-store, no-cache ...</code> headers which cannot by overridden from PHP or Twig.</p>
<p>In Nitro 1.x you could SSH into the machine and edit the Nginx config to not add the headers. I haven't been able to get Nitro 2.0 to work and have switched to Docker Compose which is simpler and doesn't have this caching problem.</p>

CC BY-SA 4.0
2021-03-08T09:14:38.900Z
2021-03-08T09:14:38.900Z
Tamlyn
<p>Craft doesn't explicitly set <em>any</em> caching headers in the response, but you can control exactly how you want your caching headers to behave from your templates with the <a href="http://buildwithcraft.com/docs/templating/header" rel="noreferrer">{% header %}</a> tag.</p>

<p>For example:</p>

<pre><code>{# Tell the browser to cache this page for 30 days #}
{% set expiry = now|date_modify('+30 days') %}

{% header "Cache-Control: max-age=" ~ expiry.timestamp %}
{% header "Pragma: cache" %}
{% header "Expires: " ~ expiry|date('D, d M Y H:i:s', 'GMT') ~ " GMT" %}
</code></pre>

<p>By default PHP has the <a href="http://php.net/manual/en/function.session-cache-limiter.php" rel="noreferrer">session_cache_limiter()</a> method set to <code>nocache</code>, which is probably what you're seeing.</p>

CC BY-SA 3.0
2014-09-18T01:45:45.460Z
Hmmm...It seems like _something_ is (Yii or Craft) unless I'm confused.

Request headers for a static (non-craft request):
```HTTP/1.1 200 OK
Date: Thu, 18 Sep 2014 01:44:49 GMT
Server: Apache
Last-Modified: Thu, 18 Sep 2014 01:44:48 GMT
Accept-Ranges: bytes
Cache-Control: max-age=0
Expires: Thu, 18 Sep 2014 01:44:49 GMT
Vary: Accept-Encoding
Content-Encoding: gzip
X-UA-Compatible: IE=edge
Content-Length: 26
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8```

Here's one from Craft:
```
Tim Kelty
CC BY-SA 3.0
2014-09-18T01:49:27.857Z
Maybe Apache sets them by default if nothing has already been set?
Brad Bell
CC BY-SA 3.0
2014-09-18T01:51:11.480Z
Response header examples below (non-pertinent headers removed)

Response headers for a static (non-Craft request):
```HTTP/1.1 200 OK
Date: Thu, 18 Sep 2014 01:44:49 GMT
Last-Modified: Thu, 18 Sep 2014 01:44:48 GMT
Cache-Control: max-age=0
Expires: Thu, 18 Sep 2014 01:44:49 GMT
```

Here's one from Craft:
```HTTP/1.1 200 OK
Max-Age=300; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
```
Tim Kelty
CC BY-SA 3.0
2014-09-18T01:52:43.140Z
The only related header I've seen Apache set by default is `Cache-Control: max-age=0`
Tim Kelty
CC BY-SA 3.0
2014-09-18T01:55:08.953Z
If you look at craft/app/services/TemplateService->renderTemplate(), you can see the headers Craft will set when rendering a template.
Brad Bell
CC BY-SA 3.0
2014-09-18T01:55:33.410Z
Maybe a php.ini thing?  Really strange.
Brad Bell
CC BY-SA 3.0
2014-09-18T01:59:21.197Z
http://php.net/manual/en/session.configuration.php#ini.session.cache-limiter
Brad Bell
CC BY-SA 3.0
2014-09-18T04:59:22.660Z
Weird. I tested it by just bailing out of the top of index.php with an `exit('Hello');` - no no-cache headers. Once I let Craft run though, I see them. Are you not seeing the Cache-Control/Pragma headers on your Craft installs?
Tim Kelty
CC BY-SA 3.0
2014-09-18T04:59:44.540Z
For the hell of it I checked the response headers on http://straightupcraft.com/ and I see the same thing.
Tim Kelty
CC BY-SA 3.0
2014-09-18T05:02:59.247Z
Right... because session_cache_limiter() kicks in as soon as PHP's session_start() gets called, which won't have happened at the top of the root index.php. Craft/Yii will call that as soon as HttpSessionService->init() gets invoked in a request.
Brad Bell
CC BY-SA 3.0
2014-09-18T05:12:56.457Z
You're right :). Same result with this in index.php:
```<?php
session_start();
exit('Sorry, Brad.);
```
Tim Kelty
CC BY-SA 3.0
2014-09-18T05:14:31.443Z
So I suppose the moral of the story is... if you don't like the default behavior, you can change it in your php.ini file. :)
Brad Bell
CC BY-SA 3.0
2014-09-18T05:17:41.577Z
Yep! Sorry for wasting your time.

The whole reason this came up is I was playing around with Varnish w/ Craft and by default it bypasses when it gets a `Cache-control: no-cache` response.
Tim Kelty
CC BY-SA 3.0
2014-09-18T05:19:34.087Z
Not a waste at all. We both learned something!
Brad Bell
CC BY-SA 3.0
2014-09-18T13:50:06.677Z
@TimKelty I would definitely recommend **not** changing this from php.ini as it will have unintended consequences. If you don’t like PHP’s default behavior you should just start explicitly setting what you want using the [{% header %}](http://buildwithcraft.com/docs/templating/header) tag.
Brandon Kelly
CC BY-SA 3.0
2014-09-18T18:05:51.797Z
Pfft... LIVE ON THE EDGE!
Brad Bell
CC BY-SA 3.0
2014-09-17T21:09:24.087Z
2014-09-18T02:04:51.450Z
2014-09-18T02:04:51.450Z
Brad Bell
<p>Craft seems to be setting headers to tell the browser not to cache anything it serves, which seems bad.</p>

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

<p>Why it do dat?</p>

<cache>
Why does Craft set no-cache headers for all requests?
# Why does Craft set no-cache headers for all requests?

(Asked by: username_0 on 2014-09-17)

Craft seems to be setting headers to tell the browser not to cache anything it serves, which seems bad.

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

Why it do dat?

**Tags:** cache


---

## 3 answers

### Answer by username_3 on 2014-09-17. Score: 10 | Accepted answer

Craft doesn't explicitly set any caching headers in the response, but you can control exactly how you want your caching headers to behave from your templates with the {% header %} tag.
For example:
{# Tell the browser to cache this page for 30 days #}
{% set expiry = now|date_modify('+30 days') %}

{% header "Cache-Control: max-age=" ~ expiry.timestamp %}
{% header "Pragma: cache" %}
{% header "Expires: " ~ expiry|date('D, d M Y H:i:s', 'GMT') ~ " GMT" %}

By default PHP has the session_cache_limiter() method set to nocache, which is probably what you're seeing.

> **Comments:**
> **username_0**: Hmmm...It seems like _something_ is (Yii or Craft) unless I'm confused.  Request headers for a static (non-craft request): ```HTTP/1.1 200 OK Date: Thu, 18 Sep 2014 01:44:49 GMT Server: Apache Last-Modified: Thu, 18 Sep 2014 01:44:48 GMT Accept-Ranges: bytes Cache-Control: max-age=0 Expires: Thu, 18 Sep 2014 01:44:49 GMT Vary: Accept-Encoding Content-Encoding: gzip X-UA-Compatible: IE=edge Content-Length: 26 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=utf-8```  Here's one from Craft: ```  
> **username_3**: Maybe Apache sets them by default if nothing has already been set?  
> **username_0**: Response header examples below (non-pertinent headers removed)  Response headers for a static (non-Craft request): ```HTTP/1.1 200 OK Date: Thu, 18 Sep 2014 01:44:49 GMT Last-Modified: Thu, 18 Sep 2014 01:44:48 GMT Cache-Control: max-age=0 Expires: Thu, 18 Sep 2014 01:44:49 GMT ```  Here's one from Craft: ```HTTP/1.1 200 OK Max-Age=300; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache ```  
> **username_0**: The only related header I've seen Apache set by default is `Cache-Control: max-age=0`  
> **username_3**: If you look at craft/app/services/TemplateService->renderTemplate(), you can see the headers Craft will set when rendering a template.  
> **username_3**: Maybe a php.ini thing?  Really strange.  
> **username_3**: http://php.net/manual/en/session.configuration.php#ini.session.cache-limiter  
> **username_0**: Weird. I tested it by just bailing out of the top of index.php with an `exit('Hello');` - no no-cache headers. Once I let Craft run though, I see them. Are you not seeing the Cache-Control/Pragma headers on your Craft installs?  
> **username_0**: For the hell of it I checked the response headers on http://straightupcraft.com/ and I see the same thing.  
> **username_3**: Right... because session_cache_limiter() kicks in as soon as PHP's session_start() gets called, which won't have happened at the top of the root index.php. Craft/Yii will call that as soon as HttpSessionService->init() gets invoked in a request.  
> **username_0**: You're right :). Same result with this in index.php: ```<?php session_start(); exit('Sorry, Brad.); ```  
> **username_3**: So I suppose the moral of the story is... if you don't like the default behavior, you can change it in your php.ini file. :)  
> **username_0**: Yep! Sorry for wasting your time.  The whole reason this came up is I was playing around with Varnish w/ Craft and by default it bypasses when it gets a `Cache-control: no-cache` response.  
> **username_3**: Not a waste at all. We both learned something!  
> **username_4**: @TimKelty I would definitely recommend **not** changing this from php.ini as it will have unintended consequences. If you don’t like PHP’s default behavior you should just start explicitly setting what you want using the [{% header %}](http://buildwithcraft.com/docs/templating/header) tag.  
> **username_3**: Pfft... LIVE ON THE EDGE!  

---

### Answer by username_1 on 2019-11-27. Score: 2

As highlighted in username_3's answer, those headers are likely set by PHP. By default PHP has the session_cache_limiter() method set to nocache. You can verify this in your Craft admin panel under Utilities > PHP Info > session.cache_limiter.
If you change this value to public, it will set a cache expiry according to the value of session.cache_expire (set to 180 by default). This is explained here in the PHP docs.
I did not heed username_4's advice, and I set session.cache_limiter=public in my php.ini file. I do not know which unintended consequences he speaks of, but I like to live dangerously.
Here are the new headers I get:
Cache-Control: public, max-age=10800
Expires: Wed, 27 Nov 2019 11:54:38 GMT (180 minutes from now)

If you do this, it will break a few things in the admin. Admin pages won't always load properly because the wrong things will be cached. It works perfectly on the website though.


---

### Answer by username_2 on 2021-03-08. Score: 0

This had me confused for a while: if you're using [Craft Nitro](https://getnitro.sh/), the Nginx config adds `Cache-Control: no-store, no-cache ...` headers which cannot by overridden from PHP or Twig.

In Nitro 1.x you could SSH into the machine and edit the Nginx config to not add the headers. I haven't been able to get Nitro 2.0 to work and have switched to Docker Compose which is simpler and doesn't have this caching problem.


---

