142

I'm trying to use http caching. In my controller I'm setting a response as follows:

$response->setPublic();
$response->setMaxAge(120);
$response->setSharedMaxAge(120);
$response->setLastModified($lastModifiedAt);

dev mode

In dev environment first response is a 200 with following headers:

cache-control:max-age=120, public, s-maxage=120
last-modified:Wed, 29 Feb 2012 19:00:00 GMT

For next 2 minutes every response is a 304 with following headers:

cache-control:max-age=120, public, s-maxage=120

This is basically what I expect it to be.

prod mode

In prod mode response headers are different. Note that in app.php I wrap the kernel in AppCache.

First response is a 200 with following headers:

cache-control:must-revalidate, no-cache, private
last-modified:Thu, 01 Mar 2012 11:17:35 GMT

So it's a private no-cache response.

Every next request is pretty much what I'd expect it to be; a 304 with following headers:

cache-control:max-age=120, public, s-maxage=120

Should I worry about it? Is it an expected behaviour?

What will happen if I put Varnish or Akamai server in front of it?

I did a bit of debugging and I figured that response is private because of last-modified header. HttpCache kernel uses EsiResponseCacheStrategy to update the cached response (HttpCache::handle() method).

if (HttpKernelInterface::MASTER_REQUEST === $type) {
    $this->esiCacheStrategy->update($response);
}

EsiResponseCacheStrategy turns a response into non cacheable if it uses either Last-Response or ETag (EsiResponseCacheStrategy::add() method):

if ($response->isValidateable()) {
    $this->cacheable = false;
} else {
    // ... 
}

Response::isValidateable() returns true if Last-Response or ETag header is present.

It results in overwriting the Cache-Control header (EsiResponseCacheStrategy::update() method):

if (!$this->cacheable) {
    $response->headers->set('Cache-Control', 'no-cache, must-revalidate');

    return;
}

I asked this question on Symfony2 user group but I didn't get an answer so far: https://groups.google.com/d/topic/symfony2/6lpln11POq8/discussion

Update.

Since I no longer have access to the original code I tried to reproduce the scenario with the latest Symfony standard edition.

Response headers are more consistent now, but still seem to be wrong.

As soon as I set a Last-Modified header on the response, the first response made by a browser has a:

Cache-Control:must-revalidate, no-cache, private

Second response has an expected:

Cache-Control:max-age=120, public, s-maxage=120

If I avoid sending If-Modified-Since header, every request returns must-revalidate, no-cache, private.

It doesn't matter if the request was made in prod or dev environment anymore.

hakre
  • 193,403
  • 52
  • 435
  • 836
Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
  • 3
    when i disable the $kernel = new AppCache($kernel); it is showed as public to me. but then it will always response with a code 200 ... i use as a revery proxy nginx. – Michael Feb 06 '13 at 23:51
  • are your `app.php` and `app_dev.php` the same ? (ignoring debug and env) – Florian Klein Apr 09 '13 at 07:27
  • 1
    I have no access to that project anymore so I can't confirm this. I remember controllers were default ones with AppCache enabled. – Jakub Zalas Apr 09 '13 at 18:17
  • 1
    @Florian I tried reproducing the problem and I've got a bit different behaviour with the latest Symfony version (see an update). – Jakub Zalas Apr 10 '13 at 10:00
  • What is your *concrete* question? Which *concrete* issue do you have with those headers? What did you expect instead? Which practical problem is this causing to you? What did you wanted to do instead? Do you think a specific specification (like an RFC) is violated? If so which one and which part of it? The headers per-se *are* HTTP conform so your question is really broad. – hakre Jul 18 '13 at 09:16
  • The behaviour changed since I first asked the question (it was initially inconsistent between the environments). I guess it's now the question of interpretation of RFC and if the current behaviour is valid according to it or not. Since the first response with the Last-Modified header is private, I can answer myself that it's not valid according to the spec since it describes it as: response_is_fresh = (freshness_lifetime > current_age). – Jakub Zalas Jul 22 '13 at 18:12
  • I'll double check that... – Jakub Zalas Jul 22 '13 at 18:19
  • Could this be the case of small time differences between client and server? – Marek Nov 15 '13 at 11:27
  • 2
    Would you set `debug=>true` into getOptions() in AppCache so that you get `X-Symfony-Cache` header? – denkiryokuhatsuden Dec 11 '13 at 01:52
  • @Marek with Last-Modified time on the client doesn't matter as it should send the same value back (via If-Modified-Since). – Jakub Zalas Jan 29 '14 at 16:11

2 Answers2

9

I have faced same problem. I had to supply 'public' headers my cdn. By default when gateway caching is enabled in prod mode, it returns 200 OK with private, nocache must validate headers.

I solved problem this way.

In app.php, before I send response to user ($respond->send), I have overwritten the cache control header to blank and set cache headers to public and max age(some value).

//code snippet from app.php

    $response = $kernel->handle($request);
    $response->headers->set('Cache-Control', '');
    $response->setPublic();
    $response->setMaxAge(86400);
    $response->send();        
srikanthsatturi
  • 157
  • 1
  • 6
-4

The behavior you experience is intended. Symfony2 Docs explicitly describe the situations when private and public are used, default being private.

Udan
  • 5,429
  • 2
  • 28
  • 34