This is a collection of status and implementation notes for deploying compression dictionary content encoding when using a CDN.
To successfully deploy compression dictionary transport (as it currently stands) from behind a CDN, requires a few things from the CDN:
- Pass the
Use-As-Dictionary:
response header andSec-Available-Dictionary:
request header through between the client and the origin. - Pass
Accept-Encoding: sbr,...
andContent-Encoding: sbr
through between the client and origin even if it can't decode the contents. - Support varying the cache of resources for both request headers (i.e.
Vary: Accept-Encoding,Sec-Available-Dictionary
) so that sbr-encoded responses are cached by the dictionary that was used and are not intermixed with the full resource.
The first item, passing the new headers, is usually not a problem bot the last two tend to require special handling (or updated support) by middle-boxes, including CDN's.
Compressing content with external dictionaries will not work with edge-compute that needs to modify the payload of the response unless the compression is done after the edge compute runs. Practically, that means most edge-compute systems can route sbr-encoded responses but not modify the streams until support is added to the CDN for doing the actual compression. For these notes, we're specifically assuming compression is being done at the origin and passing through the CDN.
There is a test page at https://test.sharedbrotli.com/ (code here) that can be used to test delivering sbr-encoded resources through a CDN. It will simulate requests from a browser that supports compression dictionary transport to the origin server which has implemented support for delivering sbr-encoded versions of a script file.
If you set the test page as the origin for a site on a given CDN, you can visit that site in a browser and test what parts of the flow work and iterate on making changes until it works (if it is possible for the given CDN).
These are the settings needed to make compression dictionary transport work (if possible) for the CDNs that have been tested to date as well as the date when the testing was done. Feel free to submit a Pull Request with CDN-specific notes for any that have not been tested yet or to update notes for an existing test.
CDN | Status | As of Date |
---|---|---|
Amazon CloudFront | ✅ | April 3, 2023 |
Cloudflare | ✅ | April 4, 2023 |
Fastly | ✅ | March 29, 2023 |
CloudFront supports passing custom content-encoding through the CDN and caching the artifacts. It doesn't work in combination with "automatic compression" so all of the compression will need to be done by the origin.
To enable support, you need to create a custom cache policy.
The policy needs to have the Accept-Encoding
and Sec-Available-Dictionary
headers added as cache keys:
The policy also requires that the automatic compression settings be disabled:
For distributions that are backed by resources stored in a S3 bucket, a CloudFront function is needed for both the request and response to select appropriate assets from the bucket based on the compression and Sec-Available-Key
. i.e. append .sbr.<hash>
to the end of the file name and add the Content-Encoding: sbr
response header. It can also be done with a lambda as the origin instead of the S3 bucket with all of the selection and fallback logic in the lambda. Assuming the resources can be cached, the lambda should execute very rarely.
Cloudflare only supports gzip content-encoding between the CDN and origin and will not pass arbitrary content-encodings through by default and it will not pass the client's Accept-Encoding header through.
This can be worked around using a worker, passing the client's Accept-Encoding header in a different header and forcing the cache keys to vary on the headers explicitly. It is also important that the origin specify no-transform
on the Cache-Control:
response header when using Content-Encoding: sbr
.
export default {
async fetch(request) {
// Rewrite the host to point to the origin (if necessary)
const url = new URL(request.url);
url.hostname = 'test.sharedbrotli.com';
// Special handling when the client advertises an available dictionary
if (request.headers.has('Sec-Available-Dictionary')) {
let init = {
method: request.method,
redirect: "manual",
headers: new Headers(request.headers),
cf: {
cacheKey: request.headers.get('Sec-Available-Dictionary') + url.toString(),
cacheTtl: 2592000
}
};
try {
init.headers.set('X-Accept-Encoding', request.cf.clientAcceptEncoding);
} catch(e) {}
return fetch(url.toString(), init);
}
// By default, pass the request through unmodified
return fetch(url.toString(), request);
}
};
By default, Fastly will normalize the Accept-Encoding header before passing it to the origin. This can be fixed with a bit of VCL to pass the sbr
encoding through:
if (req.http.Fastly-Orig-Accept-Encoding ~ "sbr") {
set req.http.accept-encoding = req.http.accept-encoding ", sbr";
}
Once that is done, the content-encoding negotiation works perfectly and the cache supports Vary
with the necessary headers, correctly serving the appropriate responses from cache.