Hacker News new | past | comments | ask | show | jobs | submit login
iOS6 Safari Caching $.ajax Results (stackoverflow.com)
70 points by mmastrac on Sept 21, 2012 | hide | past | favorite | 25 comments



I would like to defend Safari's behaviour.

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.


> Please note that you _explicitly_ declared the content as 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...


Revalidating is optional. A browser MAY not revalidate if it likes so, unless you use `must-revalidate`:

https://tools.ietf.org/html/rfc2616#section-14.9.3

> 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).

https://tools.ietf.org/html/rfc2616#section-14.9.4

> must-revalidate

> 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.


Additionally, I will point out that the "cache: false" that people are discussing does not seem to be documented as part of the XHR interface; it is only something jQuery manipulates, and it seems to do nothing very useful with it: all it means is "if this is a GET/HEAD request, append a query-string with a millisecond-accurate timestamp"; it does nothing for POST requests, and it does not add any cache control headers to the request that could be used to indicate to either the browser or to upstream proxies that the content should not be returned from a cache.


Must be in Apple's haste to make iOS 6 zip along impressively they got too happy with the cache settings.

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.


Irritating as it maybe, users are doing you a favor by even filing a bug, something we devs often forget. Bug reports are a gift allowing you to improve your project, and keep on deving, vs, say, waiting tables.

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.


In my experience, it's very easy to get irritated at bug reports for style issues or tone, when you are really upset with yourself for the bug and want to blame someone else for something, anything.


This particular issue is probably lurking in code that I've written. The gist of the fix is to ensure that Cache-Control: no-cache is explicitly set on anything you don't want cached.


It is a bug to ignore the max-age parameter that is presently widely used.


Also consider max-age=0


The stackoverflow post says that max-age=0 doesn't work for this particular case, but no-cache does.


If you've read the article, the issue is with unique POST requests coming from client-side. That kind of behavior is absolutely unacceptable.


> If you've read the article, the issue is with unique POST requests coming from client-side. That kind of behavior is absolutely unacceptable.

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."


> Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields.

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.


Indeed, this behavior may be compliant with the HTTP spec. From RFC 2616 §14.9.3:

> 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.


RFC 2616 Section 14.9.1 states:

    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.
Section 9.5 (for the POST request method):

    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.
Responses to the POST request method are not cacheable by default according to the spec.


I keep on seeing this get thrown around, that POST is not cacheable:

> 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:

    Cache-Control: max-age=0


What's broken is this: when you make a POST request to a server that omits the Cache-Control header, iOS 6 caches that response by default. That behavior violates the HTTP spec.


> The discussion is then really about whether max-age 0 means "for 1 second" or "never".

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.


OK, I guess I should salute for doing the ultimate in defensive programming.

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.


Yeah, and there's no indication that 303 response was generated.


The 303 status is one possible behavior presented, it's not even a recommendation, never mind a SHOULD or MUST.


Apple has the power to change the web on a whim. If this isn't fixed it could cause almost every web-service to actually send a `Cache-Control: no-cache` header on it's POST responses. A long shot, but maybe they are doing this deliberately (like they did with <video> vs flash) for some reason?


Pretty unlikely, especially undocumented anywhere. I'd ascribe that to stupidity rather than malice and would guess they tried to change something in the caching subsystem (e.g. cache POST with an explicit cache-control spec — makes sense for a mobile phone) and they broke the default case.

This would be supported by max-age: 0 failing to make resources "uncacheable".





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: