Incapsula does not obey "Cache-Control: private" directives

We use Incapsula as a reverse proxy for some of our sites, and recently ran into an issue with what appears to be a wilful violation of the HTTP specification by Incapsula.

Incapsula’s documentation says the following about how it handles Cache-Control headers:

Cache-Control - if it's set to no-cache / private / must-revalidate / proxy-revalidate/ no-store – then it'll prevent caching by Incapsula, unless site Acceleration Mode is set to Aggressive.

Unfortunately this isn’t the case. The following response headers from an origin server:

    Cache-Control: max-age=86400, private
    Content-Encoding: gzip
    Content-Length: 2431
    Content-Type: text/html; charset=utf-8
    ...

Get converted into this by Incapsula’s proxy:

    Cache-Control: max-age=86398, public
    Content-Encoding: gzip
    Content-Length: 2431
    Content-Type: text/html; charset=utf-8
    ...

This is a problem because what was meant to be a private resource has been cached and made public by Incapsula.

We contacted Incapsula to report this issue. This was the explanation we received:

private: Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache.

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.

Since Incapsula is a CDN, which considers as a shared cache, the value of max-age=NUMBER and Cache-Control=private contradict each other and must not configured in this way.

The statement about max-age implying that a resource is public is false. The HTTP specification explicitly allows private resources to have a max-age:

private

Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache. This allows an origin server to state that the specified parts of the response are intended for only one user and are not a valid response for requests by other users. A private (non-shared) cache MAY cache the response.

The correct behaviour would be for Incapsula to give the private directive precendence over max-age (rather than the other way around). Instead it takes the rather dangerous step of simply removing the private directive!

In any case, the point is that Incapsula will violate a Cache-Control: private directive unless you also set max-age=0.