Skip to content

Instantly share code, notes, and snippets.

@DanielThomas
Created February 19, 2021 01:19
Show Gist options
  • Save DanielThomas/ce0490a3d5ae8bc16852c8465edc7182 to your computer and use it in GitHub Desktop.
Save DanielThomas/ce0490a3d5ae8bc16852c8465edc7182 to your computer and use it in GitHub Desktop.
Varnish Plus configuration for public NPM registry
vcl 4.1;
import edgestash;
import goto;
import xbody;
backend default none;
sub vcl_init {
new registry = goto.dns_director("https://registry.npmjs.org");
}
sub vcl_recv {
set req.backend_hint = registry.backend();
if (req.url == "/" || req.url ~ "^/-/npm/v1/security/audits") {
if (req.url == "/-/npm/v1/security/audits/quick" && req.http.Npm-In-CI == "true") {
set req.http.npm-notice = "quick audits as part of 'npm install' are disabled on this registry. Run 'npm audit' explicitly";
return (synth(404));
}
set req.http.X-Cache-Request-Pass = true;
} else if (req.url ~ "^/-/npm/v1") {
set req.http.npm-notice = "this registry cache only supports pass-through of audit requests";
return (synth(404));
} else if (req.method != "GET" && req.method != "HEAD") {
set req.http.npm-notice = "this is a read-only cache of the public npm registry. Add 'publishConfig' pointing to the public registry if you intend you publish there";
return (synth(405));
} else {
unset req.http.authorization;
unset req.http.cookie;
}
}
sub vcl_hash {
// The registry varies responses based on the accept header so normalize to reduce skew
if (!req.http.accept) {
set req.http.accept = "*/*";
}
// The registry supports both encoded and non-encoded request URLs, and the former can be upper or lower-case
set req.url = regsub(req.url, "%2(f|F)", "/");
if (req.url != "/") {
// The registry serves the same response with and without trailing slashes
set req.url = regsub(req.url, "/$", "");
}
}
sub vcl_synth {
set resp.http.npm-notice = req.http.npm-notice;
}
sub vcl_backend_fetch {
set bereq.http.Host = "registry.npmjs.org";
}
sub vcl_backend_response {
unset beresp.http.CF-Cache-Status;
unset beresp.http.CF-Ray;
unset beresp.http.CF-Request-Id;
unset beresp.http.Expect-CT;
unset beresp.http.Set-Cookie;
unset beresp.http.Server;
unset beresp.http.x-amz-meta-rev;
if (beresp.http.Content-Type ~ "json") {
xbody.regsub("https:\/\/registry\.npmjs\.org", "{@{proto}@}://{@{host}@}");
// Mustache packument has a mustache template in it's description so we change the default delimiter
edgestash.set_delimiter("{@{", "}@}");
edgestash.parse_response();
}
if (!bereq.uncacheable) { // avoid hit-for-pass objects as we already pass any request we don't want cached
if (beresp.status == 200) {
set beresp.ttl = 10m;
set beresp.grace = 1h;
set beresp.keep = 1d;
} else if (beresp.status == 404) {
set beresp.ttl = 2m;
set beresp.grace = 1h;
set beresp.http.npm-notice = "this is a read-only cache of the public npm registry and does not contain internal packages";
} else {
set beresp.ttl = 5s;
set beresp.grace = 5s;
}
}
}
sub vcl_deliver {
// npm 4.4.4 chokes on metadata responses with 'Error: unexpected end of file' when 'Content-Encoding: gzip' is seen on 304 responses, so we force uncompressed responses (caused by Varnish 304s returning all response headers, rather than those necessary)
if (req.http.If-None-Match || req.http.If-Modified-Since) {
unset req.http.Accept-Encoding;
}
// Apply variables to templated tarball urls
if (resp.http.Content-Type ~ "json") {
edgestash.add_json({" { "proto": ""} + req.http.X-Forwarded-Proto + {"", "host": ""} + req.http.Host + {"" } "});
edgestash.execute();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment