{"id":2961,"date":"2022-06-12T21:34:29","date_gmt":"2022-06-12T21:34:29","guid":{"rendered":"https:\/\/dft.wiki\/?p=2961"},"modified":"2026-06-08T21:41:32","modified_gmt":"2026-06-09T01:41:32","slug":"setting-up-a-private-cdn-with-varnish-http-cache-on-ubuntu","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=2961","title":{"rendered":"Setting Up a Private CDN with Varnish HTTP Cache on Ubuntu"},"content":{"rendered":"<p>Varnish HTTP Cache is a dedicated open-source application for caching HTTP requests made by clients to the server. It sits in the middle as a reverse proxy, handling load balancing and offloading static content (JavaScript, images, etc.) from the web server.<\/p>\n<p>A common use case is to regionalize part of the web content (static\/immutable files) to another country or continent, improving the overall user experience.<\/p>\n<p>Another scenario is placing a local server in a company branch (or a large client&#8217;s office) to reduce internet traffic, which is especially useful for metered or slow connections.<\/p>\n<p><strong>INITIAL SETUP<\/strong><\/p>\n<pre>sudo apt update && sudo apt upgrade -y\r\nsudo apt install debian-archive-keyring curl gnupg apt-transport-https -y\r\ncurl -fsSL https:\/\/packagecloud.io\/varnishcache\/varnish70\/gpgkey|sudo gpg --dearmor -o \/etc\/apt\/trusted.gpg.d\/varnish.gpg<\/pre>\n<p><strong>IF:<\/strong> Ubuntu 20.04 LTS<\/p>\n<pre>sudo tee \/etc\/apt\/sources.list.d\/varnishcache_varnish70.list > \/dev\/null <<-EOF\r\ndeb https:\/\/packagecloud.io\/varnishcache\/varnish70\/$ID\/ $VERSION_CODENAME main\r\ndeb-src https:\/\/packagecloud.io\/varnishcache\/varnish70\/$ID\/ $VERSION_CODENAME main\r\nEOF<\/pre>\n<p><strong>OR IF:<\/strong> Ubuntu 22.04 LTS<\/p>\n<pre>sudo tee \/etc\/apt\/sources.list.d\/varnishcache_varnish70.list > \/dev\/null <<-EOF\r\ndeb https:\/\/packagecloud.io\/varnishcache\/varnish70\/ubuntu\/ focal main\r\ndeb-src https:\/\/packagecloud.io\/varnishcache\/varnish70\/ubuntu\/ focal main\r\nEOF<\/pre>\n<p><strong>INSTALLING VARNISH<\/strong><\/p>\n<pre>sudo apt update && sudo apt install varnish -y<\/pre>\n<p>Configuring the service:<\/p>\n<pre>sudo cp \/lib\/systemd\/system\/varnish.service \/etc\/systemd\/system\/\r\nsudo nano \/etc\/systemd\/system\/varnish.service<\/pre>\n<pre>ExecStart=\/usr\/sbin\/varnishd \\\r\n      -a :<strong>6081<\/strong> \\\r\n      -a localhost:8443,PROXY \\\r\n      -p feature=+http2 \\\r\n      -f \/etc\/varnish\/default.vcl \\\r\n      -s malloc,<strong>1g<\/strong><\/pre>\n<p><strong>Note:<\/strong> This is where you can adjust the port Varnish listens on and the amount of memory allocated to the cache (e.g. <strong>256m<\/strong> or <strong>2g<\/strong>). Setting a limit is highly recommended since the default is unlimited.<\/p>\n<p>Configuring the Varnish Configuration Language (VCL):<\/p>\n<pre>sudo nano \/etc\/varnish\/default.vcl<\/pre>\n<pre>backend default {\r\n    .host = \"<strong>127.0.0.1<\/strong>\";\r\n    .port = \"<strong>8000<\/strong>\";\r\n}<\/pre>\n<p>OR (for multiple backends):<\/p>\n<pre>import directors;\r\n\r\nbackend backend1 {\r\n    .host = \"srv1.example.com\";\r\n    .port = \"80\";\r\n}\r\n\r\nbackend backend2 {\r\n    .host = \"srv2.example.com\";\r\n    .port = \"80\";\r\n}\r\n\r\nsub vcl_init {\r\n    new vdir = directors.round_robin();\r\n    vdir.add_backend(backend1);\r\n    vdir.add_backend(backend2);\r\n}\r\n\r\nsub vcl_recv {\r\n    set req.backend_hint = vdir.backend();\r\n}\r\n<\/pre>\n<p>Starting the service:<\/p>\n<pre>sudo systemctl daemon-reload\r\nsudo systemctl start varnish\r\nsudo systemctl enable varnish<\/pre>\n<p><strong>TESTING<\/strong><\/p>\n<p>Monitor traffic between Varnish and the web server in one terminal:<\/p>\n<pre>tcpdump -i <strong>lo<\/strong> dst host <strong>127.0.0.1<\/strong> and port <strong>8000<\/strong><\/pre>\n<p>In another terminal, make multiple HTTP requests to Varnish and confirm whether it hits the web server each time or not:<\/p>\n<pre>curl \"http:\/\/localhost:6081\/\"\r\ncurl \"http:\/\/localhost:6081\/\"\r\ncurl \"http:\/\/localhost:6081\/\"\r\ncurl \"http:\/\/localhost:6081\/\"\r\ncurl \"http:\/\/localhost:6081\/\"\r\ncurl \"http:\/\/localhost:6081\/\"<\/pre>\n<p>Print the response headers for additional information:<\/p>\n<pre>curl -I \"http:\/\/localhost:6081\/\"<\/pre>\n<p><strong>BONUS<\/strong><\/p>\n<p>In the default VCL configuration file (<code>\/etc\/varnish\/default.vcl<\/code>), you can override headers on internal requests or define how long Varnish should cache content:<\/p>\n<pre># Changes will be applied to the internal request (between Varnish and the web server).\r\nsub vcl_backend_response {\r\n    # Change Header\r\n    unset beresp.http.Cache-Control;\r\n    set beresp.http.Cache-Control = \"max-age=1209600\";\r\n\r\n    # Define Varnish Retention\r\n    set beresp.ttl = 2w;\r\n\r\n    # Defining Conditional Retention\r\n    If (bereq.url == \"\/VoD\") {\r\n        beresp.ttl = 2d;\r\n    }\r\n}<\/pre>\n<p>Serving multiple sites with Virtual Hosts:<\/p>\n<pre>sub vcl_recv {\r\n  if (server.ip == \"<strong>10.10.10.10<\/strong>\")\r\n  {\r\n    include \"\/etc\/varnish\/<strong>site-one<\/strong>.vcl\";\r\n  }\r\n  elsif (server.ip == \"<strong>10.20.30.40<\/strong>\")\r\n  {\r\n    include \"\/etc\/varnish\/<strong>site-two<\/strong>.vcl\";\r\n  }\r\n}<\/pre>\n<p>OR:<\/p>\n<pre>sub vcl_recv {\r\n  if (! req.http.Host)\r\n  {\r\n    error 404 \"Need a host header\";\r\n  }\r\n  set req.http.Host = regsub(req.http.Host, \"^www\\.\", \"\");\r\n  set req.http.Host = regsub(req.http.Host, \":80$\", \"\");\r\n\r\n  if (req.http.Host == \"<strong>site-one.com<\/strong>\")\r\n  {\r\n    include \"\/etc\/varnish\/<strong>site-one<\/strong>.vcl\";\r\n  }\r\n  elsif (req.http.Host == \"<strong>site-two<\/strong>.com\")\r\n  {\r\n    include \"\/etc\/varnish\/<strong>site-two<\/strong>.vcl\";\r\n  }\r\n}<\/pre>\n<p>Then create the respective files (<code>\/etc\/varnish\/<strong>site-one<\/strong>.vcl<\/code> and <code>\/etc\/varnish\/<strong>site-two<\/strong>.vcl<\/code>) to specify individual configuration per IP or domain.<\/p>\n<p>To cache everything:<\/p>\n<pre>sub vcl_recv {\r\n    unset req.http.Cookie;\r\n}\r\n\r\nsub vcl_backend_response {\r\n    # Force caching of all objects for 6 hours\r\n    set beresp.ttl = 6h;\r\n    set beresp.http.Cache-Control = \"max-age=21600\";\r\n}\r\n\r\nsub vcl_deliver {\r\n    unset resp.http.Cache-Control;\r\n}<\/pre>\n<p>To cache based on URL:<\/p>\n<pre>sub vcl_recv {\r\n    if (req.url ~ \"^\/path\/(A|B|C)$\") {\r\n        unset req.http.Cookie;\r\n        return (hash);\r\n    }\r\n}\r\nsub vcl_backend_response {\r\n    # Only set TTL and cache headers for specific URLs\r\n    if (bereq.url ~ \"^\/path\/(A|B|C)$\") {\r\n        set beresp.ttl = 6h;\r\n        set beresp.http.Cache-Control = \"max-age=21600\";\r\n    }\r\n}\r\nsub vcl_deliver {\r\n    if (req.url ~ \"^\/path\/(A|B|C)$\") {\r\n        unset resp.http.Cache-Control;\r\n    }\r\n}<\/pre>\n<hr \/>\n<p><strong>WATCH STATISTICS OF FORWARDED AND CACHED REQUESTS<\/strong><\/p>\n<pre>sudo watch -n 1 'varnishstat -1 | grep -E \"client_req|cache_hit\"'<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Varnish HTTP Cache is a dedicated open-source application for caching HTTP requests made by clients [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6,7],"tags":[],"class_list":["post-2961","post","type-post","status-publish","format-standard","hentry","category-linux","category-raspberry-pi","category-web"],"_links":{"self":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2961","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2961"}],"version-history":[{"count":15,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2961\/revisions"}],"predecessor-version":[{"id":5674,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2961\/revisions\/5674"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2961"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2961"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2961"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}