Re: policy-uri is slow

On Fri, Apr 15, 2011 at 7:51 PM, Mark Nottingham <mnot@mnot.net> wrote:
> You seem to be saying that a site that's going through the effort of setting CSP headers for all of its resources will shy away from setting caching headers as well. Why?

Because the person setting up CSP isn't thinking about performance, or
maybe even has little to no idea what they're doing and is just
following a dodgy tutorial they found on some website.  It's not a
good idea to make assumptions about authors' competence, if it's
possible to design features that work as well and don't make such
assumptions.

> What does revalidation have to do with cache eviction?

If revalidation is required on every hit, caching is of limited
utility for small resources, because a 304 Not Modified response will
take about as much effort as resending the entire resource.

> Please re-read the thread; the idea is to use a well-known URI, which is indeed known ahead of time.

But you still don't know *if* you need to fetch it, unless you plan to
preemptively try fetching that URL for every site you visit -- which
makes no sense unless you expect the vast majority of sites to use
CSP.

>> But that sort of thing only makes sense if the resource is quite
>> large, several kilobytes at least.  If we expect the large majority of
>> CSP policies to be only a handful of bytes long, I really think
>> linking to a policy will hurt performance in nearly all cases, not
>> help it.
>
> ... and I think will help.

It will *always* hurt on a page view will cold cache, to the tune of a
full round-trip.  And that's a *lot* of views.  This research by
Yahoo! from 2007 says that 40-60% of their users have an empty cache
at least sometimes, and about 20% of all their page views have an
empty cache: <http://www.yuiblog.com/blog/2007/01/04/performance-research-part-2/>
 That's an extra round-trip on all of those page views.

By contrast, sending your CSP policy on every request will only
increase network transfer time appreciably if it happens to cause a
TCP window to fill up, so that the server has to wait for an
acknowledgement from the client before proceeding.  With current
efforts to increase default window sizes by a large margin, plus the
use of pipelining to try keeping large window sizes going, this is
going to be fairly uncommon even if the policy is a kilobyte in
length.  Surely not 25% of the time, which is what it would have to be
to outweigh a 20% empty cache rate.

So why do you think the tradeoff will typically be worth it even
*with* good caching headers, given how common it is to have a cold
cache?  With a policy-uri over regular HTTP with a cold cache, it will
take four round-trips before the user can start seeing resources
included in the page instead of three.  (One for TCP handshake, one to
start receiving the page, one to receive the policy, one to retrieve
additional content.)

> If this is *really * a concern (and I don't think it is), one could specify that the policy file has a default cacheability of the browser session (for example). HTTP allows heuristics to be used to cache responses, and this is just one example of how we could do this for CSP.

Still doesn't help the cold cache case, which is very common.

If long policies are such an issue, you could do something along the
lines of a 304 response for CSP.  So instead of policy-uri, allow a
rule called policy-id, say.  It would take an arbitrary opaque string
as its argument, which might be a number or timestamp or something in
practice.  When the browser receives such a policy, it can cache it
somewhere, remembering the site name and id.  When it next sends an
HTTP request, it can include a header like
Remembered-Content-Security-Policy: (insert id here).  When the server
sees that, it can include a special placeholder like
Content-Security-Policy: reuse-cached to instruct the browser to just
reuse the old policy, or it can include a full policy if the id is
out-of-date.

Advantages of this scheme:

* Better performance than either policy-uri or inline policies: you
send the full policy on a cold cache without an extra request (like
inline policy), and on a cache hit you reuse the cached policy (like
policy-uri).
* Does not ever add round-trips, no matter how badly you misuse it,
unlike policy-uri.

Of course, it's harder to set up than policy-uri, if you're not
serving the header from a script (in which case it's trivial).
Actually, I don't see any way to send response headers conditioned on
request headers in Apache or lighttpd.  So maybe it's not a viable
alternative . . . but I really can't see how adding a round-trip on
every cold-cache hit is ever going to be better than just including
the policy inline, unless the policy is multiple kilobytes long.  Are
we really expecting policies that long?

Received on Sunday, 17 April 2011 22:02:29 UTC