Skip to content

Instantly share code, notes, and snippets.

@vvuksan
Last active September 10, 2018 17:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vvuksan/ce3f7726b1b994430955368a0b392f7c to your computer and use it in GitHub Desktop.
Save vvuksan/ce3f7726b1b994430955368a0b392f7c to your computer and use it in GitHub Desktop.
EZ Publish VCL snippets
# Stale object handling
if (resp.status >= 500 && resp.status < 600) {
/* restart if the stale object is available */
if (stale.exists) {
restart;
}
}
// On receiving the hash response, copy the hash header to the original
// request and restart.
if (req.restarts == 0
&& resp.http.content-type ~ "application/vnd.fos.user-context-hash"
) {
set req.http.x-user-hash = resp.http.x-user-hash;
restart;
}
// If we get here, this is a real response that gets sent to the client.
// Remove the vary on user context hash, this is nothing public. Keep all
// other vary headers.
if (!req.http.Fastly-FF && resp.http.Vary ~ "X-User-Hash") {
set resp.http.Vary = regsub(resp.http.Vary, "(?i),? *X-User-Hash *", "");
set resp.http.Vary = regsub(resp.http.Vary, "^, *", "");
if (resp.http.Vary == "") {
unset resp.http.Vary;
}
// If we vary by user hash, we'll also adjust the cache control headers going out by default to avoid sending
// large ttl meant for Varnish to shared proxies and such. We assume only session cookie is left after vcl_recv.
if (req.http.cookie) {
// When in session where we vary by user hash we by default avoid caching this in shared proxies & browsers
// For browser cache with it revalidating against varnish, use for instance "private, no-cache" instead
set resp.http.cache-control = "private, no-cache, no-store, must-revalidate";
} else if (resp.http.cache-control ~ "public") {
// For non logged in users we allow caching on shared proxies (mobile network accelerators, planes, ...)
// But only for a short while, as there is no way to purge them
set resp.http.cache-control = "public, s-maxage=600, stale-while-revalidate=300, stale-if-error=300";
}
}
// Sanity check to prevent ever exposing the hash to a non debug client.
if ( !req.http.Fastly-FF ) {
unset resp.http.x-user-hash;
}
/* handle 503s */
if (obj.status >= 500 && obj.status < 600) {
/* deliver stale object if it is available */
if (stale.exists) {
return(deliver_stale);
}
}
/* handle 5XX (or any other unwanted status code) */
if (beresp.status >= 500 && beresp.status < 600) {
/* deliver stale if the object is available */
if (stale.exists) {
return(deliver_stale);
}
if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
restart;
}
/* else go to vcl_error to deliver a synthetic */
error beresp.status beresp.response;
}
/* # VLADIMIR COMMENT OUT
# This will never hit unless status code is 600+. It's handled by block above
if (bereq.http.accept ~ "application/vnd.fos.user-context-hash" && beresp.status >= 500 ) {
error beresp.status "Internal Server Error";
}
*/
// Check for ESI acknowledgement and remove Surrogate-Control header
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
esi;
# Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
# compress it
set beresp.http.x-compress-hint = "on";
}
if (beresp.http.Cache-Control ~ "no-cache") {
return(pass);
}
sub ez_user_context_hash {
// Prevent tampering attacks on the hash mechanism
if (req.restarts == 0
&& (req.http.accept ~ "application/vnd.fos.user-context-hash"
|| req.http.x-user-hash
)
) {
error 400 "Bad request";
}
if (req.restarts == 0 && (req.request == "GET" || req.request == "HEAD")) {
// Backup accept header, if set
if (req.http.accept) {
set req.http.x-fos-original-accept = req.http.accept;
}
set req.http.accept = "application/vnd.fos.user-context-hash";
// Backup original URL
set req.http.x-fos-original-url = req.url;
set req.url = "/_fos_user_context_hash";
// Force the lookup, the backend must tell not to cache or vary on all
// headers that are used to build the hash.
// varnish : return(hash);
return (lookup);
}
// Rebuild the original request which now has the hash.
if (req.restarts > 0 && req.http.accept == "application/vnd.fos.user-context-hash") {
// By default, fastly treats a restart as a failure and wont distribute hits in the PoP cluster
// Setting Fastly-Force-Shield fixes that behaviour
set req.http.Fastly-Force-Shield = "1";
set req.url = req.http.x-fos-original-url;
unset req.http.x-fos-original-url;
if (req.http.x-fos-original-accept) {
set req.http.accept = req.http.x-fos-original-accept;
unset req.http.x-fos-original-accept;
} else {
// If accept header was not set in original request, remove the header here.
unset req.http.accept;
}
// Force the lookup, the backend must tell not to cache or vary on the
// user context hash to properly separate cached data.
// varnish : return(hash);
//return (lookup);
//return (pass);
}
}
// Advertise Symfony for ESI support
set req.http.Surrogate-Capability = "abc=ESI/1.0";
// Varnish, in its default configuration, sends the X-Forwarded-For header but does not filter out Forwarded header
if ( req.restarts == 0 ) {
unset req.http.Forwarded;
}
// Preserve X-Forwarded-For in all requests.
if (!req.http.Fastly-FF) {
if (req.http.X-Forwarded-For) {
set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;
} else {
set req.http.Fastly-Temp-XFF = client.ip;
}
} else {
# We are on Origin Shield
set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;
set req.max_stale_while_revalidate = 0s;
# VLADIMIR ADD - disable ESI processing on Origin Shield
set req.esi = false;
}
/* -- VLADIMIR COMMENT OUT -- early returns are discouraged as they may avoid shielding logic
if (req.http.Authenticate || req.http.Authorization) {
return(lookup);
}
*/
// Remove all cookies besides Session ID, as JS tracker cookies and so will make the responses effectively un-cached
// VLADIMIR -- I would consider removing this altogether
if (req.http.cookie) {
set req.http.cookie = ";" + req.http.cookie;
set req.http.cookie = regsuball(req.http.cookie, "; +", ";");
set req.http.cookie = regsuball(req.http.cookie, ";(eZSESSID[^=]*)=", "; \1=");
set req.http.cookie = regsuball(req.http.cookie, ";[^ ][^;]*", "");
set req.http.cookie = regsuball(req.http.cookie, "^[; ]+|[; ]+$", "");
if (req.http.cookie == "") {
// If there are no more cookies, remove the header to get page cached.
unset req.http.cookie;
}
}
/* VLADIMIR COMMENT OUT-- this is unnecessary as most these things are already set ie. duplicates X-Forwarded-For logic from above
if( req.url.ext ~ "(?i)(css|js|gif|jpe?g|bmp|png|tiff?|ico|img|tga|wmf|svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv|zip|gz|pdf|ttf|eot|wof)" ) {
if (!req.http.Fastly-FF) {
if (req.http.X-Forwarded-For) {
set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;
} else {
set req.http.Fastly-Temp-XFF = client.ip;
}
} else {
set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;
}
return(lookup);
}
*/
// Sort the query string for cache normalization.
set req.url = boltsort.sort(req.url);
// Retrieve client user context hash and add it to the forwarded request.
call ez_user_context_hash;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment