To me this problem reads "I was doing something wrong, but it has worked, it should keep on working". In this case, if there has always been a working proper way, I think that it is OK to break the bad behaviour and force people to use the proper one.
As other have pointed out, the spec says: "POST responses should not be cached, unless you use a Cache-Control header. In that case do whatever Cache-Control says."
Now, "Cache-Control: max-age=0" may have been working fine up to now, but what its meaning is "this content is cacheable; please retain it only for 0 second". The first part of the sentence explicitly says that the content is cacheable, the second part is used by the browser as part of its cache policy. Please note that you _explicitly_ declared the content as cacheable. Do you want to _explicitly_ declare your content as not cacheable? Use "Content-Control: no-cache". Or just do not set Cache-Control and rely on the default non-cacheable status of POST responses.
The only problem here is that Safari is caching POST responses that have no Cache-Control header set. Yes that is a bug. But one cannot complain about the fact that a browser cached a response that has explicitly been said by its producer to be cacheable.
You also declared that it instantly becomes stale, so the browser must revalidate the entry before returning it. Safari returning a resource with Cache-Control: max-age=0 without contacting the server beforehand is an incorrect behavior, goes against the spec and is not defensible.
> But one cannot complain about the fact that a browser cached a response that has explicitly been said by its producer to be cacheable.
Yes one can, and the problem is not that it was cached but that it was returned. A stale cached response returned without validation is an incorrect behavior unless the response also included staleness specifications (which I can't remember ever seeing in the wild). Revalidating is not optional: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13... http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13...
> A cache MAY be configured to return stale responses without validation, but only if this does not conflict with any "MUST"-level requirements concerning cache validation (e.g., a "must-revalidate" cache-control directive).
> Because a cache MAY be configured to ignore a server's specified expiration time, and because a client request MAY include a max-stale directive (which has a similar effect), the protocol also includes a mechanism for the origin server to require revalidation of a cache entry on any subsequent use.
As a developer, this is one of the most irritating things to see in a bug report: a smirky uninformed assumption about why the bug exists.
A friend once worked in a company, where "snark" was considered a valid reason to mark a bug "wontfix". The company didn't last long. It did sound like fun while it lasted though.
Maybe I'm missing something, but reading the StackOverflow discussion in the source link, it looks like the HTTP RFC actually allows this case:
> I suspect that Apple is taking advantage of this from the HTTP spec in section 9.5 about POST:
"Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource."
The response does not include a cache-control or expires header, as such the method is not cacheable. iOS is in violation with the specification if that's what iOS does and the above specification is the reference.
> The expiration time of an entity MAY be specified by the origin
server using the Expires header (see section 14.21). Alternatively,
it MAY be specified using the max-age directive in a response. When
the max-age cache-control directive is present in a cached response,
the response is stale if its current age is greater than the age
value given (in seconds) at the time of a new request for that
resource. The max-age directive on a response implies that the
response is cacheable (i.e., "public") unless some other, more
restrictive cache directive is also present.
So, people were using "max-age=0" as a substitute for "don't cache", when the actual meaning is "don't cache for more than 0 seconds". The key here is:
1) You can just use "no-cache" in the "Cache-Control" header.
2) I always felt it was okay for caches to be "sloppy", to cache things too long if the software decides it's okay, as long as it's not extreme and as long as they're not caching things they're not supposed to cache.
3) Cache ages are integral seconds, so I'd expect "max-age=0" to mean that it expires when 1000 ms passes.
I've dealt with a few terrible problems involving the "Cache-Control" header, mostly with IE. And PDFs. And both at the same time.
14.9.1 What is Cacheable
By default, a response is cacheable if the requirements of the
request method, request header fields, and the response status
indicate that it is cacheable.
Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.
> unless the response includes appropriate Cache-Control
Forgive me if I'm dense, but doesn't this "unless" clause mean exactly that if you include a Cache-Control header, the POST will be cacheable unless the Cache-Control header says otherwise? The discussion is then really about whether max-age 0 means "for 1 second" or "never". I don't see why it would be necessary for it to mean "never" when there are two other ways you can get that behavior: by specifying "no-cache" or by simply omitting the header.
The entire discussion is about a developer who uses the following header:
max-age 0 means "cacheable resource fresh for 0 seconds". It means neither "don't cache" nor "cache for n seconds". The resource is cacheable and always stale, and must be revalidated before returning it to a client.
> I don't see why it would be necessary for it to mean "never"
It doesn't, but returning the resource without checking its validity against the source is forbidden by the spec. So Safari is buggy on that, and buggy when there's no Cache-Control as well.
But I come from a system software background and definitely expect that age=0 means age=0. If '0' is allowed for age, then I expect Apple to handle that case according to the rules of normal arithmetic.
This would be supported by max-age: 0 failing to make resources "uncacheable".