├── .alpine ├── .github ├── actions │ ├── action.yml │ └── release.sh └── workflows │ └── workflow.yml ├── .gitignore ├── Dockerfile ├── GeoIP.dat.gz ├── LICENSE.md ├── Makefile ├── README.md ├── bin └── actions.mk ├── docker-entrypoint.sh ├── patches └── 6 │ ├── musl-include-vpf.patch │ └── musl-include-vsb.patch ├── templates ├── default.vcl.tmpl ├── defaults │ ├── vcl_backend_error.vcl.tmpl │ ├── vcl_backend_response.vcl.tmpl │ ├── vcl_deliver.vcl.tmpl │ ├── vcl_hash.vcl.tmpl │ ├── vcl_hit.vcl.tmpl │ ├── vcl_miss.vcl.tmpl │ ├── vcl_pipe.vcl.tmpl │ └── vcl_recv.vcl.tmpl ├── includes │ ├── backend.vcl.tmpl │ ├── mobile.vcl.tmpl │ ├── purge.vcl.tmpl │ └── static.vcl.tmpl ├── presets │ ├── drupal.vcl.tmpl │ └── wordpress.vcl.tmpl └── varnishd.init.d.tmpl └── tests ├── basic ├── compose.yml └── run.sh ├── drupal ├── compose.yml └── run.sh └── wordpress ├── compose.yml └── run.sh /.alpine: -------------------------------------------------------------------------------- 1 | 3.20#2025-05-31T07:14:02.639429Z -------------------------------------------------------------------------------- /.github/actions/action.yml: -------------------------------------------------------------------------------- 1 | name: push 2 | description: combine multi-arch image and push 3 | inputs: 4 | version: 5 | description: version 6 | required: true 7 | latest: 8 | description: if tag latest 9 | required: false 10 | latest_major: 11 | description: if tag latest major version 12 | required: false 13 | runs: 14 | using: "composite" 15 | steps: 16 | - name: Build image 17 | env: 18 | VARNISH_VER: ${{ inputs.version }} 19 | LATEST: ${{ inputs.latest }} 20 | LATEST_MAJOR: ${{ inputs.latest_major }} 21 | run: | 22 | . $GITHUB_ACTION_PATH/release.sh 23 | shell: bash 24 | -------------------------------------------------------------------------------- /.github/actions/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -exo pipefail 4 | 5 | if [[ "${GITHUB_REF}" == refs/heads/master || "${GITHUB_REF}" == refs/tags/* ]]; then 6 | minor_ver="${VARNISH_VER%.*}" 7 | major_ver="${minor_ver%.*}" 8 | 9 | tags=("${minor_ver}") 10 | 11 | if [[ -n "${LATEST_MAJOR}" ]]; then 12 | tags+=("${major_ver}") 13 | fi 14 | 15 | if [[ "${GITHUB_REF}" == refs/tags/* ]]; then 16 | stability_tag=("${GITHUB_REF##*/}") 17 | tags=("${minor_ver}-${stability_tag}") 18 | if [[ -n "${LATEST_MAJOR}" ]]; then 19 | tags+=("${major_ver}-${stability_tag}") 20 | fi 21 | else 22 | if [[ -n "${LATEST}" ]]; then 23 | tags+=("latest") 24 | fi 25 | fi 26 | 27 | for tag in "${tags[@]}"; do 28 | make buildx-imagetools-create TAG=${tag} 29 | done 30 | fi 31 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Build docker image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | tags: 9 | - '*' 10 | 11 | env: 12 | BASE_IMAGE_STABILITY_TAG: 2.18.0 13 | VARNISH60: '6.0.14' 14 | 15 | jobs: 16 | varnish60-build: 17 | strategy: 18 | matrix: 19 | arch: 20 | - amd64 21 | - arm64 22 | include: 23 | - arch: amd64 24 | runner: ubuntu-24.04 25 | - arch: arm64 26 | runner: ubuntu-24.04-arm 27 | runs-on: ${{ matrix.runner }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: docker/login-action@v3 31 | with: 32 | username: ${{ secrets.DOCKER_USERNAME }} 33 | password: ${{ secrets.DOCKER_PASSWORD }} 34 | - name: build and push 35 | env: 36 | VARNISH_VER: ${{ env.VARNISH60 }} 37 | ARCH: ${{ matrix.arch }} 38 | run: | 39 | make 40 | make test 41 | make push 42 | varnish60-push: 43 | runs-on: ubuntu-latest 44 | needs: 45 | - varnish60-build 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: docker/login-action@v3 49 | with: 50 | username: ${{ secrets.DOCKER_USERNAME }} 51 | password: ${{ secrets.DOCKER_PASSWORD }} 52 | - uses: ./.github/actions 53 | with: 54 | version: ${{ env.VARNISH60 }} 55 | latest: true 56 | latest_major: true 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE_TAG 2 | 3 | FROM wodby/alpine:${BASE_IMAGE_TAG} 4 | 5 | ARG VARNISH_VER 6 | ARG TARGETPLATFORM 7 | ARG BUILDPLATFORM 8 | 9 | ENV VARNISH_VER="${VARNISH_VER}" 10 | 11 | COPY patches /tmp/patches/ 12 | COPY GeoIP.dat.gz /usr/share/GeoIP/ 13 | 14 | RUN set -ex; \ 15 | \ 16 | addgroup -g 101 -S varnish; \ 17 | adduser -u 100 -D -S -s /bin/bash -G varnish varnish; \ 18 | echo "PS1='\w\$ '" >> /home/varnish/.bashrc; \ 19 | \ 20 | apk --update --no-cache -t .varnish-run-deps add \ 21 | gcc \ 22 | libc-dev \ 23 | libedit \ 24 | libmhash --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/ \ 25 | libunwind \ 26 | geoip \ 27 | libgcc \ 28 | make \ 29 | ncurses-libs \ 30 | openssl \ 31 | pcre \ 32 | pwgen; \ 33 | \ 34 | apk --update --no-cache -t .varnish-build-deps add \ 35 | attr \ 36 | autoconf \ 37 | automake \ 38 | build-base \ 39 | git \ 40 | geoip-dev \ 41 | libedit-dev \ 42 | libmhash-dev --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/ \ 43 | libunwind-dev \ 44 | libtool \ 45 | linux-headers \ 46 | ncurses-dev \ 47 | openssl-dev \ 48 | pcre-dev \ 49 | py3-docutils \ 50 | py3-sphinx \ 51 | python3 \ 52 | rsync; \ 53 | \ 54 | varnish_url="http://varnish-cache.org/_downloads/varnish-${VARNISH_VER}.tgz"; \ 55 | wget -qO- "${varnish_url}" | tar xz -C /tmp/; \ 56 | cd /tmp/varnish-*; \ 57 | mkdir -p /tmp/pkg; \ 58 | # Patch from alpine varnish package repository. 59 | for i in /tmp/patches/"${VARNISH_VER:0:1}"/*.patch; do patch -p1 -i "${i}"; done; \ 60 | \ 61 | ./configure \ 62 | --prefix=/usr \ 63 | --sysconfdir=/etc \ 64 | --mandir=/usr/share/man \ 65 | --infodir=/usr/share/info \ 66 | --localstatedir=/var/lib \ 67 | --with-unwind \ 68 | --without-jemalloc; \ 69 | \ 70 | make -j "$(nproc)"; \ 71 | # fails. 72 | # make check; \ 73 | make DESTDIR=/tmp/pkg install; \ 74 | \ 75 | mkdir -p /usr/share/varnish; \ 76 | mv LICENSE /usr/share/varnish/; \ 77 | \ 78 | pkgdir="/tmp/pkg"; \ 79 | binfiles=$(scanelf -R "$pkgdir" | grep ET_DYN | sed "s:$pkgdir\/::g" | sed "s:ET_DYN ::g"); \ 80 | for f in $binfiles; do \ 81 | srcdir=$(dirname $pkgdir/$f); \ 82 | srcfile=$(basename $pkgdir/$f); \ 83 | cd $srcdir; \ 84 | XATTR=$(getfattr --match="" --dump "${srcfile}"); \ 85 | strip $srcfile; \ 86 | [ -n "$XATTR" ] && { echo "$XATTR" | setfattr --restore=-; } \ 87 | done; \ 88 | \ 89 | # Remove info, help, docs (default_docs from alpinelinux/abuild). 90 | pkgdir=/tmp/pkg; \ 91 | for i in doc man info html sgml gtk-doc ri help; do \ 92 | if [ -d "$pkgdir/usr/share/$i" ]; then \ 93 | rm -rf "$pkgdir/usr/share/$i"; \ 94 | fi; \ 95 | done; \ 96 | \ 97 | # Collect info about dev packages to delete after we run make check for modules. 98 | # (modified version of default_dev from alpinelinux/abuild). 99 | cd /tmp/pkg; \ 100 | libdirs=usr/; \ 101 | [ -d lib/ ] && libdirs="lib/ $libdirs"; \ 102 | for i in usr/include/* usr/lib/pkgconfig/varnish* usr/share/aclocal/varnish* \ 103 | usr/share/gettext usr/bin/*-config \ 104 | usr/share/vala/vapi usr/share/gir-[0-9]* \ 105 | usr/share/qt*/mkspecs \ 106 | usr/lib/qt*/mkspecs \ 107 | usr/lib/cmake \ 108 | $(find $libdirs -name '*.[acho]' \ 109 | -o -name '*.prl' 2>/dev/null); do \ 110 | if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then \ 111 | echo "/$i" >> /tmp/varnish-dev-files; \ 112 | fi; \ 113 | done; \ 114 | \ 115 | for i in lib/*.so usr/lib/*.so; do \ 116 | if [ -L "$i" ]; then \ 117 | echo "/$i" >> /tmp/varnish-dev-files; \ 118 | fi; \ 119 | done; \ 120 | \ 121 | rsync -a --links /tmp/pkg/ /; \ 122 | \ 123 | libvmod_geoip_ver="1.0.3"; \ 124 | libvmod_geoip_url="https://github.com/varnish/libvmod-geoip/archive/libvmod-geoip-${libvmod_geoip_ver}.tar.gz"; \ 125 | wget -qO- "${libvmod_geoip_url}" | tar xz -C /tmp/; \ 126 | # @todo use .mmdb db instead of legacy .dat https://github.com/varnish/libvmod-geoip/issues/18 127 | # wget -qP /usr/share/GeoIP http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz; \ 128 | gunzip /usr/share/GeoIP/GeoIP.dat.gz; \ 129 | cd /tmp/libvmod-geoip-*; \ 130 | ./autogen.sh; \ 131 | ./configure; \ 132 | make; \ 133 | make install; \ 134 | if [[ "${TARGETPLATFORM}" != "linux/arm64" ]]; then \ 135 | make check; \ 136 | fi; \ 137 | \ 138 | libvmod_digest_ver="1.0.2"; \ 139 | libvmod_digest_url="https://github.com/varnish/libvmod-digest/archive/libvmod-digest-${libvmod_digest_ver}.tar.gz"; \ 140 | wget -qO- "${libvmod_digest_url}" | tar xz -C /tmp/; \ 141 | cd /tmp/libvmod-digest-*; \ 142 | ./autogen.sh; \ 143 | ./configure; \ 144 | make; \ 145 | make install; \ 146 | if [[ "${TARGETPLATFORM}" != "linux/arm64" ]]; then \ 147 | make check; \ 148 | fi; \ 149 | \ 150 | # we're using 6.0 branch instead of releases https://github.com/varnish/varnish-modules/issues/144 151 | git clone --depth 1 -b 6.0 --single-branch https://github.com/varnish/varnish-modules /tmp/varnish-modules; \ 152 | cd /tmp/varnish-modules; \ 153 | ./bootstrap; \ 154 | ./configure; \ 155 | make; \ 156 | make install; \ 157 | if [[ "${TARGETPLATFORM}" != "linux/arm64" ]]; then \ 158 | make check; \ 159 | fi; \ 160 | \ 161 | install -d -o varnish -g varnish -m750 \ 162 | /var/cache/varnish \ 163 | /var/log/varnish \ 164 | /var/lib/varnish; \ 165 | \ 166 | install -d -o root -g varnish -m750 \ 167 | /etc/varnish \ 168 | /etc/varnish/defaults \ 169 | /etc/varnish/includes; \ 170 | \ 171 | mkdir -p /etc/init.d/; \ 172 | touch /etc/varnish/preset.vcl /etc/init.d/varnishd; \ 173 | chown varnish:varnish /etc/varnish/preset.vcl /etc/init.d/varnishd; \ 174 | chmod +x /etc/init.d/varnishd; \ 175 | \ 176 | while IFS= read -r file ; do rm -rf -- "${file}" ; done < /tmp/varnish-dev-files; \ 177 | apk del --purge .varnish-build-deps; \ 178 | rm -rf /tmp/*; \ 179 | rm -rf /var/cache/apk/* 180 | 181 | EXPOSE 6081 6082 182 | 183 | VOLUME /var/lib/varnish 184 | 185 | COPY templates /etc/gotpl/ 186 | COPY bin /usr/local/bin/ 187 | COPY docker-entrypoint.sh / 188 | 189 | ENTRYPOINT ["/docker-entrypoint.sh"] 190 | CMD ["/etc/init.d/varnishd"] 191 | -------------------------------------------------------------------------------- /GeoIP.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wodby/varnish/321eca1abb18c72f43fc418d10c0d3bb4c3a2d31/GeoIP.dat.gz -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Wodby, Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include env.mk 2 | 3 | VARNISH_VER ?= 6.0.14 4 | VARNISH_VER_MINOR = $(shell echo "${VARNISH_VER}" | grep -oE '^[0-9]+\.[0-9]+') 5 | 6 | ALPINE_VER ?= 3.20 7 | 8 | PLATFORM ?= linux/arm64 9 | 10 | ifeq ($(BASE_IMAGE_STABILITY_TAG),) 11 | BASE_IMAGE_TAG := $(ALPINE_VER) 12 | else 13 | BASE_IMAGE_TAG := $(ALPINE_VER)-$(BASE_IMAGE_STABILITY_TAG) 14 | endif 15 | 16 | TAG ?= $(VARNISH_VER_MINOR) 17 | 18 | REPO = wodby/varnish 19 | NAME = varnish-$(VARNISH_VER_MINOR) 20 | 21 | ifneq ($(ARCH),) 22 | override TAG := $(TAG)-$(ARCH) 23 | endif 24 | 25 | .PHONY: build buildx-build buildx-push test test-clean push shell run start stop logs clean release 26 | 27 | default: build 28 | 29 | build: 30 | docker build -t $(REPO):$(TAG) \ 31 | --build-arg BASE_IMAGE_TAG=$(BASE_IMAGE_TAG) \ 32 | --build-arg VARNISH_VER=$(VARNISH_VER) ./ 33 | 34 | buildx-build: 35 | docker buildx build --platform $(PLATFORM) -t $(REPO):$(TAG) \ 36 | --build-arg BASE_IMAGE_TAG=$(BASE_IMAGE_TAG) \ 37 | --build-arg VARNISH_VER=$(VARNISH_VER) \ 38 | --load \ 39 | ./ 40 | 41 | buildx-push: 42 | docker buildx build --push --platform $(PLATFORM) -t $(REPO):$(TAG) \ 43 | --build-arg BASE_IMAGE_TAG=$(BASE_IMAGE_TAG) \ 44 | --build-arg VARNISH_VER=$(VARNISH_VER) \ 45 | ./ 46 | 47 | buildx-imagetools-create: 48 | docker buildx imagetools create -t $(REPO):$(TAG) \ 49 | $(REPO):$(VARNISH_VER_MINOR)-amd64 \ 50 | $(REPO):$(VARNISH_VER_MINOR)-arm64 51 | .PHONY: buildx-imagetools-create 52 | 53 | test: 54 | cd ./tests/basic && IMAGE=$(REPO):$(TAG) ./run.sh 55 | cd ./tests/drupal && IMAGE=$(REPO):$(TAG) ./run.sh 56 | cd ./tests/wordpress && IMAGE=$(REPO):$(TAG) ./run.sh 57 | 58 | push: 59 | docker push $(REPO):$(TAG) 60 | 61 | shell: 62 | docker run --rm --name $(NAME) -i -t $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) /bin/bash 63 | 64 | run: 65 | docker run --rm --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) $(CMD) 66 | 67 | start: 68 | docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) 69 | 70 | stop: 71 | docker stop $(NAME) 72 | 73 | logs: 74 | docker logs $(NAME) 75 | 76 | clean: 77 | -docker rm -f $(NAME) 78 | 79 | release: build push 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Varnish Docker Container Image 2 | 3 | [![Build Status](https://github.com/wodby/varnish/workflows/Build%20docker%20image/badge.svg)](https://github.com/wodby/varnish/actions) 4 | [![Docker Pulls](https://img.shields.io/docker/pulls/wodby/varnish.svg)](https://hub.docker.com/r/wodby/varnish) 5 | [![Docker Stars](https://img.shields.io/docker/stars/wodby/varnish.svg)](https://hub.docker.com/r/wodby/varnish) 6 | 7 | - [Docker images](#docker-images) 8 | - [Environment variables](#environment-variables) 9 | - [Installed modules](#installed-modules) 10 | - [Default behaviour](#default-behaviour) 11 | - [Caching rules](#caching-rules) 12 | - [Cache personification](#cache-personification) 13 | - [GeoIP](#geoip) 14 | - [Currency](#currency) 15 | - [Flushing](#cache-flushing) 16 | - [Miscellaneous](#miscellaneous) 17 | - [Config presets](#config-presets) 18 | - [Drupal](#drupal) 19 | - [WordPress](#wordpress) 20 | - [PageSpeed downstream caching](#pagespeed-downstream-caching) 21 | - [Orchestration actions](#orchestration-actions) 22 | - [Deployment](#deployment) 23 | 24 | ## Docker Images 25 | 26 | ❗For better reliability we release images with stability tags (`wodby/varnish:6-X.X.X`) which correspond to [git tags](https://github.com/wodby/varnish/releases). We strongly recommend using images only with stability tags. 27 | 28 | Overview: 29 | 30 | - All images based on Alpine Linux 31 | - Base image: [wodby/alpine](https://github.com/wodby/alpine) 32 | - [GitHub actions builds](https://github.com/wodby/varnish/actions) 33 | - [Docker Hub](https://hub.docker.com/r/wodby/varnish) 34 | 35 | All images built for `linux/amd64` and `linux/arm64` 36 | 37 | Supported tags and respective `Dockerfile` links: 38 | 39 | - `6.0`, `6`, `latest` [_(Dockerfile)_](https://github.com/wodby/varnish/tree/master/Dockerfile) 40 | 41 | ## Environment Variables 42 | 43 | | Variable | Default Value | Description | 44 | |--------------------------------------------|----------------------------|-------------------------------------------------| 45 | | `VARNISH_ALLOW_UNRESTRICTED_PURGE` | | Used for ban requests as well | 46 | | `VARNISH_BACKEND_BETWEEN_BYTES_TIMEOUT` | `60s` | | 47 | | `VARNISH_BACKEND_CONNECT_TIMEOUT` | `3.5s` | | 48 | | `VARNISH_BACKEND_FIRST_BYTE_TIMEOUT` | `60s` | | 49 | | `VARNISH_BACKEND_GRACE` | `2m` | | 50 | | `VARNISH_BACKEND_HOST` | | Mandatory | 51 | | `VARNISH_BACKEND_PORT` | `80` | | 52 | | `VARNISH_CACHE_PER_COUNTRY` | | Separate caches based on [country code](#geoip) | 53 | | `VARNISH_CACHE_PER_CURRENCY` | | Separate caches based on [currency](#currency) | 54 | | `VARNISH_CURRENCY_EUR_COUNTRY_CODES` | | See [currencies](#currency) | 55 | | `VARNISH_CURRENCY_USD_COUNTRY_CODES` | | See [currencies](#currency) | 56 | | `VARNISH_BIG_FILES_SIZE` | `10485760` | 10MB | 57 | | `VARNISH_BIG_FILES_TTL` | `120s` | | 58 | | `VARNISH_CACHE_STATIC_FILES` | | | 59 | | `VARNISH_CONFIG_PRESET` | | | 60 | | `VARNISH_DEFAULT_TTL` | `120s` | | 61 | | `VARNISH_ERRORS_GRACE` | `15s` | | 62 | | `VARNISH_PURGE_EXTERNAL_REQUEST_HEADER` | | | 63 | | `VARNISH_KEEP_ALL_COOKIES` | | | 64 | | `VARNISH_KEEP_ALL_PARAMS` | | | 65 | | `VARNISH_IMPORT_MODULES` | | Separated by comma | 66 | | `VARNISH_MOBILE_DISABLE_CASH` | | | 67 | | `VARNISH_MOBILE_SEPARATE_CASH` | | | 68 | | `VARNISH_MOBILE_USER_AGENT` | | See default value below | 69 | | `VARNISH_PIPE_CLOSE_CONNECTION` | | | 70 | | `VARNISH_PURGE_KEY` | | Randomly generated if missing | 71 | | `VARNISH_SECONDARY_STORAGE_CONDITION` | | Must be valid VCL | 72 | | `VARNISH_SECRET` | | Generated automatically if missing | 73 | | `VARNISH_STATIC_FILES` | | See default value below | 74 | | `VARNISH_STATIC_TTL` | `86400` | In seconds | 75 | | `VARNISH_STRIP_COOKIES` | | See default value below | 76 | | `VARNISH_STRIP_PARAMS` | | See default value below | 77 | | `VARNISH_STRIP_ALL_PARAMS` | | Ignored if `$VARNISH_KEEP_ALL_PARAMS` is set | 78 | | `VARNISH_PAGESPEED_SECRET_KEY` | | Should be used if mod_pagespeed is enabled | 79 | | `VARNISHD_DEFAULT_TTL` | `120` | | 80 | | `VARNISHD_MEMORY_SIZE` | `64m` | | 81 | | `VARNISHD_PARAM_BAN_LURKER_AGE` | `60.000` | | 82 | | `VARNISHD_PARAM_BAN_LURKER_BATCH` | `1000` | | 83 | | `VARNISHD_PARAM_BAN_LURKER_SLEEP` | `0.010` | | 84 | | `VARNISHD_PARAM_BETWEEN_BYTES_TIMEOUT` | `60.000` | | 85 | | `VARNISHD_PARAM_CONNECT_TIMEOUT` | `3.500` | | 86 | | `VARNISHD_PARAM_DEFAULT_GRACE` | `10.000` | | 87 | | `VARNISHD_PARAM_DEFAULT_KEEP` | `0.000` | | 88 | | `VARNISHD_PARAM_DEFAULT_TTL` | `120.000` | | 89 | | `VARNISHD_PARAM_FETCH_CHUNKSIZE` | `16k` | | 90 | | `VARNISHD_PARAM_FIRST_BYTE_TIMEOUT` | `60.000` | | 91 | | `VARNISHD_PARAM_GZIP_BUFFER` | `32k` | | 92 | | `VARNISHD_PARAM_GZIP_LEVEL` | `6` | | 93 | | `VARNISHD_PARAM_GZIP_MEMLEVEL` | `8` | | 94 | | `VARNISHD_PARAM_HTTP_GZIP_SUPPORT` | `on` | | 95 | | `VARNISHD_PARAM_HTTP_MAX_HDR` | `64` | | 96 | | `VARNISHD_PARAM_HTTP_REQ_HDR_LEN` | `8k` | | 97 | | `VARNISHD_PARAM_HTTP_REQ_SIZE` | `32k` | | 98 | | `VARNISHD_PARAM_HTTP_RESP_HDR_LEN` | `8k` | | 99 | | `VARNISHD_PARAM_HTTP_RESP_SIZE` | `32k` | | 100 | | `VARNISHD_PARAM_IDLE_SEND_TIMEOUT` | `60.000` | | 101 | | `VARNISHD_PARAM_MAX_ESI_DEPTH` | `5` | | 102 | | `VARNISHD_PARAM_MAX_RESTARTS` | `4` | | 103 | | `VARNISHD_PARAM_MAX_RETRIES` | `4` | | 104 | | `VARNISHD_PARAM_NUKE_LIMIT` | `50` | | 105 | | `VARNISHD_PARAM_PING_INTERVAL` | `3` | | 106 | | `VARNISHD_PARAM_PIPE_TIMEOUT` | `60.000` | | 107 | | `VARNISHD_PARAM_POOL_REQ` | `10,100,10` | | 108 | | `VARNISHD_PARAM_POOL_SESS` | `10,100,10` | | 109 | | `VARNISHD_PARAM_PREFER_IPV6` | `off` | | 110 | | `VARNISHD_PARAM_RUSH_EXPONENT` | `3` | | 111 | | `VARNISHD_PARAM_SEND_TIMEOUT` | `600` | | 112 | | `VARNISHD_PARAM_SHORTLIVED` | `10.000` | | 113 | | `VARNISHD_PARAM_TCP_KEEPALIVE_INTVL` | `75.000` | | 114 | | `VARNISHD_PARAM_TCP_KEEPALIVE_PROBES` | `8` | | 115 | | `VARNISHD_PARAM_TCP_KEEPALIVE_TIME` | `7200.000` | | 116 | | `VARNISHD_PARAM_THREAD_POOL_ADD_DELAY` | `0.000` | | 117 | | `VARNISHD_PARAM_THREAD_POOL_DESTROY_DELAY` | `1.000` | | 118 | | `VARNISHD_PARAM_THREAD_POOL_FAIL_DELAY` | `0.200` | | 119 | | `VARNISHD_PARAM_THREAD_POOL_MAX` | `5000` | | 120 | | `VARNISHD_PARAM_THREAD_POOL_MIN` | `100` | | 121 | | `VARNISHD_PARAM_THREAD_POOL_STACK` | `48k` | | 122 | | `VARNISHD_PARAM_THREAD_POOL_TIMEOUT` | `300.000` | | 123 | | `VARNISHD_PARAM_THREAD_POOLS` | `2` | | 124 | | `VARNISHD_PARAM_THREAD_QUEUE_LIMIT` | `20` | | 125 | | `VARNISHD_PARAM_TIMEOUT_IDLE` | `5.000` | | 126 | | `VARNISHD_PARAM_TIMEOUT_LINGER` | `0.050` | | 127 | | `VARNISHD_PARAM_VSL_BUFFER` | `4k` | | 128 | | `VARNISHD_PARAM_VSL_RECLEN` | `255b` | | 129 | | `VARNISHD_PARAM_VSL_SPACE` | `80M` | | 130 | | `VARNISHD_PARAM_VSM_SPACE` | `1M` | | 131 | | `VARNISHD_PARAM_WORKSPACE_BACKEND` | `64k` | | 132 | | `VARNISHD_PARAM_WORKSPACE_CLIENT` | `64k` | | 133 | | `VARNISHD_PARAM_WORKSPACE_SESSION` | `0.50k` | | 134 | | `VARNISHD_PARAM_WORKSPACE_THREAD` | `2k` | | 135 | | `VARNISHD_SECONDARY_STORAGE` | | See example below | 136 | | `VARNISHD_SECRET_FILE` | `/etc/varnish/secret` | | 137 | | `VARNISHD_VCL_SCRIPT` | `/etc/varnish/default.vcl` | | 138 | 139 | ###### `VARNISH_MOBILE_USER_AGENT`: 140 | 141 | Backslashes must be escaped as `\\` 142 | 143 | ``` 144 | ipod|android|blackberry|phone|mobile|kindle|silk|fennec|tablet|webos|palm|windows ce|nokia|philips|samsung|sanyo|sony|panasonic|ericsson|alcatel|series60|series40|opera mini|opera mobi|au-mic|audiovox|avantgo|blazer|danger|docomo|epoc|ericy|i-mode|ipaq|midp-|mot-|netfront|nitro|pocket|portalmmm|rover|sie-|symbian|cldc-|j2me|up\\.browser|up\\.link|vodafone|wap1\\.|wap2\\. 145 | ``` 146 | 147 | ###### `VARNISH_STATIC_FILES`: 148 | 149 | ``` 150 | asc|doc|xls|ppt|csv|svg|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm|webp 151 | ``` 152 | 153 | ###### `VARNISH_STRIP_COOKIES` 154 | 155 | Ignored if `$VARNISH_KEEP_ALL_COOKIES` is set 156 | 157 | ``` 158 | __[a-z]+|wooTracker|VCKEY-[a-zA-Z0-9-_]+ 159 | ``` 160 | 161 | ###### `VARNISH_STRIP_PARAMS` 162 | 163 | Ignored if `$VARNISH_KEEP_ALL_PARAMS` is set 164 | 165 | ``` 166 | utm_[a-z]+|gclid|cx|ie|cof|siteurl|fbclid 167 | ``` 168 | 169 | ###### `VARNISH_SECONDARY_STORAGE_CONDITION`: 170 | 171 | Allows defining custom conditions for storing the cache object in the secondary storage; as it is injected into an `if` it has to contain valid VCL syntax for it. 172 | 173 | Please note that `VARNISHD_SECONDARY_STORAGE` must be defined as well, otherwise the secondary storage would not be available. 174 | 175 | Example: instruct varnish to store in the secondary storage from the backend via custom header `X-Cache-Bin`: 176 | 177 | ``` 178 | VARNISH_STORAGE_CONDITION='beresp.http.x-cache-bin = "secondary"' 179 | ``` 180 | 181 | ## Installed Modules 182 | 183 | | Module | Version | Imported | 184 | | ------ | ------- | -------- | 185 | | [geoip](https://github.com/varnish/libvmod-geoip) | 1.0.3 | ✓ | 186 | | [digest](https://github.com/varnish/libvmod-digest) | 1.0.2 | | 187 | | [cookie](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_cookie.rst) | latest | | 188 | | [vsthrottle](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_cookie.rst) | latest | | 189 | | [header](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_header.rst) | latest | | 190 | | [saintmode](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_saintmode.rst) | latest | | 191 | | [softpurge](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_softpurge.rst) | latest | | 192 | | [tcp](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_tcp.rst) | latest | | 193 | | [var](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_var.rst) | latest | | 194 | | [xkey](https://github.com/varnish/varnish-modules/blob/master/docs/vmod_xkey.rst) | latest | | 195 | | bodyaccess | latest | | 196 | 197 | Modules can be imported as `$VARNISH_IMPORT_MODULES=xkey,softpurge`. 198 | 199 | ## Default Behaviour 200 | 201 | ### Caching Rules 202 | 203 | - Only GET or HEAD requests are cached 204 | - Backend responses with `Set-Cookie` header not cached 205 | - Static files (see `$VARNISH_STATIC_FILES`) not cached by default, set `$VARNISH_CACHE_STATIC_FILES` to cache 206 | - Error pages 404 and >500 not cached with grace period `$VARNISH_ERRORS_GRACE` 207 | - All AJAX requests not cached 208 | - Big files (larger than `$VARNISH_BIG_FILES_SIZE`) not cached 209 | 210 | ### Cache Personification 211 | 212 | White-listed cookies starting with `VCKEY-` followed by alphanumeric characters, underscores or hyphens are used to build cache hash. You can use such cookies to personify cache by a certain criteria, e.g. set `VCKEY-lang` to `en` or `fr` to cache different versions for English and French users. 213 | 214 | On your backend you should check whether `VCKEY-` cookie exists, if it does generate a personified version of a page and do not set cookie again, otherwise it won't be cached on Varnish. 215 | 216 | ### GeoIP 217 | 218 | We identify client's two-letter country code ([ISO 3166](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)) and pass it to a backend in `X-Country-Code` header. If Varnish could not recognize the country the default value will be `Unknown`. You can optionally uniquify cache per country by setting `$VARNISH_CACHE_PER_COUNTRY=1`. We use GeoLite database from MaxMind. 219 | 220 | 221 | If we see CloudFlare country code header we use it instead. 222 | 223 | ### Currency 224 | 225 | We use [the country code](#geoip) to identify the currency and pass it to a backend in `X-Currency` header. You can optionally uniquify cache per currency by setting `$VARNISH_CACHE_PER_CURRENCY=1`. 226 | 227 | We use data from [IBAN](https://www.iban.com/currency-codes) to identify which country uses which currency, currently only USD and EUR supported. 228 | 229 | Country codes for USD (`$VARNISH_CURRENCY_USD_COUNTRY_CODES`): 230 | ``` 231 | US|AS|BQ|IO|EC|SV|GU|HT|MH|FM|MP|PA|PW|PR|TL|TC|UM|VG|VI 232 | ``` 233 | 234 | Country codes for EUR (`$VARNISH_CURRENCY_EUR_COUNTRY_CODES`): 235 | ``` 236 | AD|AT|BE|CY|EE|FI|FR|GF|TF|DE|GP|GR|VA|IE|IT|LV|LT|LU|MT|MQ|YT|MC|ME|NL|PT|RE|BL|MF|PM|SM|SK|SI|ES|CE|CH|AX 237 | ``` 238 | 239 | ### Cache Flushing 240 | 241 | - Purge and ban requests both use Varnish's `ban` method to flush cache and restricted by the purge key `$VARNISH_PURGE_KEY` (generated if missing). Use header `X-VC-Purge-Key` to pass the key for purge/ban requests 242 | - Purge requests look up for exact match but ignores query params, you can change the method by setting `X-VC-Purge-Method` to `regex` or `exact` (respects query params) 243 | - Additionally for ban requests cache flushed by `Cache-Tags` header (Drupal's case) 244 | - If you want to allow unrestricted purge/ban requests in internal network specify a header via `$VARNISH_PURGE_EXTERNAL_REQUEST_HEADER` that exists only for external requests (e.g. `X-Real-IP`). If specified header is not set Varnish will skip purge key check 245 | 246 | ### Miscellaneous 247 | 248 | - Header `X-VC-Cache` set to `HIT` or `MISS` when varnish delivers content 249 | - Cache hash includes host (or ip) and request protocol 250 | - Varnish adds client's IP added to `X-Forwarded-For` 251 | - [Websocket requests supported](https://varnish-cache.org/docs/4.1/users-guide/vcl-example-websockets.html) 252 | - Query params (`$VARNISH_STRIP_PARAMS`) stripped unless `$VARNISH_KEEP_ALL_PARAMS` is set 253 | - Cookies (`$VARNISH_STRIP_COOKIES`) stripped unless `$VARNISH_KEEP_ALL_COOKIES` is set 254 | - Hashes and trailing `?` stripped from URL before passing to backend 255 | - By default cache mobile devices is identical. You can separate it by setting `$VARNISH_MOBILE_SEPARATE_CASH` or completely disable by setting `$VARNISH_MOBILE_DISABLE_CASH`. Regex `$VARNISH_MOBILE_USER_AGENT` used to identify mobile devices by `User-Agent` header 256 | - Set one of the following headers from backend to disable caching for a page: 257 | ``` 258 | X-VC-Cacheable: NO 259 | Cache-control: private 260 | Cache-control: no-cache 261 | ``` 262 | - Set `X-VC-Debug` to show cache hashes and pass through header `X-VC-DebugMessage` 263 | - `BigPipe` supported 264 | - Secondary storage can be defined via `$VARNISH_STORAGE_CONDITION` 265 | - `./vchealthz` is a liveness endpoint with 204 response code 266 | 267 | ## Config Presets 268 | 269 | You can use one of the following config presets to extend the default behaviour: 270 | 271 | ### Drupal 272 | 273 | Add `VARNISH_CONFIG_PRESET=drupal` to use this preset. 274 | 275 | - Pages matching `$VARNISH_DRUPAL_EXCLUDE_URLS` will not be cached 276 | - If a cookie from `$VARNISH_DRUPAL_PRESERVED_COOKIES` is set a page will not be cached. All other cookies stripped 277 | 278 | ###### `VARNISH_DRUPAL_EXCLUDE_URLS`: 279 | 280 | Backslashes must be escaped as `\\` 281 | 282 | ``` 283 | ^(/update\\.php|/([a-z]{2}/)?admin|/([a-z]{2}/)?admin/.*|/([a-z]{2}/)?system/files/.*|/([a-z]{2}/)?flag/.*|.*/ajax/.*|.*/ahah/.*)$ 284 | ``` 285 | 286 | ###### `VARNISH_DRUPAL_PRESERVED_COOKIES`: 287 | 288 | Not affected by `$VARNISH_KEEP_ALL_COOKIES` 289 | 290 | ``` 291 | SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE 292 | ``` 293 | 294 | ### WordPress 295 | 296 | Add `VARNISH_CONFIG_PRESET=wordpress` to use this preset. 297 | 298 | - Requests with `ak_action|app-download` query params or `akm_mobile` cookie not cached (Jetpack plugin) 299 | - Strips `replytocom=` query param 300 | - Use `$VARNISH_WP_ADMIN_SUBDOMAIN` if you have your admin on a subdomain to disable caching 301 | - If a cookie from `$VARNISH_WP_PRESERVED_COOKIES` is set a page will not be cached. All other cookies stripped 302 | 303 | ###### `VARNISH_WP_PRESERVED_COOKIES`: 304 | 305 | Not affected by `$VARNISH_KEEP_ALL_COOKIES` 306 | 307 | ``` 308 | PHPSESSID|wp-postpass_[a-z0-9]+|wordpress_[_a-z0-9]+|wordpress_logged_in_[a-z0-9]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-z0-9]+|akm_mobile 309 | ``` 310 | 311 | ## PageSpeed Downstream Caching 312 | 313 | This image contains implementation for modpagespeed downstream caching as described at https://www.modpagespeed.com/doc/downstream-caching. You can enable this behavior by specifying `$VARNISH_PAGESPEED_SECRET_KEY` to the value that matches `DownstreamCacheRebeaconingKey` in your Nginx/Apache config. This value will be used as `PS-ShouldBeacon` for 5% of hits and 25% of misses. Also, when static files cache enabled on Varnish, `PS-CapabilityList` will be set to `fully general optimizations only` to [unify behavior for all browsers](https://www.modpagespeed.com/doc/downstream-caching#ps-capabilitylist). 314 | 315 | ## Orchestration Actions 316 | 317 | ``` 318 | make COMMAND [params ...] 319 | 320 | commands: 321 | check-ready [host max_try wait_seconds delay_seconds] 322 | flush [host] 323 | 324 | default params values: 325 | host localhost 326 | max_try 1 327 | wait_seconds 1 328 | delay_seconds 0 329 | ``` 330 | 331 | ## Deployment 332 | 333 | Deploy Varnish container to your own server via [![Wodby](https://www.google.com/s2/favicons?domain=wodby.com) Wodby](https://wodby.com/stacks/varnish). 334 | -------------------------------------------------------------------------------- /bin/actions.mk: -------------------------------------------------------------------------------- 1 | .PHONY: flush check-ready check-live 2 | 3 | max_try ?= 1 4 | wait_seconds ?= 1 5 | delay_seconds ?= 0 6 | host ?= localhost 7 | command = varnishadm -T ${host}:6082 -S /etc/varnish/secret 'status' | grep -q 'Child in state running' 8 | service = Varnish 9 | 10 | default: check-ready 11 | 12 | flush: 13 | @varnishadm -T $(host):6082 -S /etc/varnish/secret "ban req.http.host ~ ." 14 | 15 | check-ready: 16 | wait_for "$(command)" $(service) $(host) $(max_try) $(wait_seconds) $(delay_seconds) 17 | 18 | check-live: 19 | @echo "OK" 20 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | function _gotpl { 10 | if [[ -f "/etc/gotpl/$1" ]]; then 11 | gotpl "/etc/gotpl/$1" > "$2" 12 | fi 13 | } 14 | 15 | init_varnish_secret() { 16 | if [[ -z "${VARNISH_SECRET}" ]]; then 17 | export VARNISH_SECRET=$(pwgen -s 128 1) 18 | echo "Generated Varnish secret: ${VARNISH_SECRET}" 19 | fi 20 | 21 | echo -e "${VARNISH_SECRET}" > /etc/varnish/secret 22 | } 23 | 24 | init_purge_key() { 25 | if [[ -z "${VARNISH_PURGE_KEY}" ]]; then 26 | export VARNISH_PURGE_KEY=$(pwgen -s 64 1) 27 | echo "Varnish purge key is missing. Generating random: ${VARNISH_PURGE_KEY}" 28 | fi 29 | } 30 | 31 | process_templates() { 32 | _gotpl 'varnishd.init.d.tmpl' '/etc/init.d/varnishd' 33 | _gotpl 'default.vcl.tmpl' '/etc/varnish/default.vcl' 34 | 35 | if [[ -n "${VARNISH_CONFIG_PRESET}" ]]; then 36 | _gotpl "presets/${VARNISH_CONFIG_PRESET}.vcl.tmpl" '/etc/varnish/preset.vcl' 37 | fi 38 | 39 | for f in /etc/gotpl/defaults/*.tmpl; do 40 | _gotpl "defaults/${f##*/}" "/etc/varnish/defaults/$(basename "${f%.tmpl}")"; 41 | done 42 | 43 | for f in /etc/gotpl/includes/*.tmpl; do 44 | _gotpl "includes/${f##*/}" "/etc/varnish/includes/$(basename "${f%.tmpl}")"; 45 | done 46 | } 47 | 48 | init_varnish_secret 49 | init_purge_key 50 | process_templates 51 | 52 | exec_init_scripts 53 | 54 | if [[ "${1}" == "make" ]]; then 55 | exec "${@}" -f /usr/local/bin/actions.mk 56 | else 57 | exec $@ 58 | fi 59 | -------------------------------------------------------------------------------- /patches/6/musl-include-vpf.patch: -------------------------------------------------------------------------------- 1 | diff --git a/include/vpf.h b/include/vpf.h 2 | index 822f2d3..61e2ee1 100644 3 | --- a/include/vpf.h 4 | +++ b/include/vpf.h 5 | @@ -27,6 +27,10 @@ 6 | * $FreeBSD: src/lib/libutil/libutil.h,v 1.41 2005/08/24 17:21:38 pjd Exp $ 7 | */ 8 | 9 | +#ifdef HAVE_SYS_STAT_H 10 | +# include 11 | +#endif 12 | + 13 | #ifndef VPF_H_INCLUDED 14 | #define VPF_H_INCLUDED 15 | 16 | -------------------------------------------------------------------------------- /patches/6/musl-include-vsb.patch: -------------------------------------------------------------------------------- 1 | diff --git a/include/vsb.h b/include/vsb.h 2 | index d510884..d9820bf 100644 3 | --- a/include/vsb.h 4 | +++ b/include/vsb.h 5 | @@ -28,6 +28,10 @@ 6 | * $FreeBSD: head/sys/sys/vsb.h 221993 2011-05-16 16:18:40Z phk $ 7 | */ 8 | 9 | +#ifdef HAVE_SYS_TYPES_H 10 | +# include 11 | +#endif 12 | + 13 | #ifndef VSB_H_INCLUDED 14 | #define VSB_H_INCLUDED 15 | 16 | -------------------------------------------------------------------------------- /templates/default.vcl.tmpl: -------------------------------------------------------------------------------- 1 | vcl 4.0; 2 | 3 | include "includes/backend.vcl"; 4 | 5 | import std; 6 | import geoip; 7 | 8 | sub vcl_recv { 9 | if (req.method == "GET" && req.url == "/.vchealthz") { 10 | return (synth(204, "OK")); 11 | } 12 | } 13 | 14 | {{ if getenv "VARNISH_IMPORT_MODULES" }} 15 | {{ $modules := split (getenv "VARNISH_IMPORT_MODULES") "," }} 16 | {{ range $modules }}import {{ . }}; 17 | {{ end }}{{ end }} 18 | 19 | sub vcl_recv { 20 | if (req.http.CF-IPCountry) { 21 | set req.http.X-Country-Code = req.http.CF-IPCountry; 22 | } elseif (req.http.X-Real-IP) { 23 | set req.http.X-Country-Code = geoip.country_code(req.http.X-Real-IP); 24 | } else { 25 | set req.http.X-Country-Code = geoip.country_code(client.ip); 26 | } 27 | 28 | if (req.http.X-Country-Code) { 29 | {{ $usd_codes := (getenv "VARNISH_CURRENCY_USD_COUNTRY_CODES" "US|AS|BQ|IO|EC|SV|GU|HT|MH|FM|MP|PA|PW|PR|TL|TC|UM|VG|VI") }} 30 | {{ $eur_codes := (getenv "VARNISH_CURRENCY_EUR_COUNTRY_CODES" "AD|AT|BE|CY|EE|FI|FR|GF|TF|DE|GP|GR|VA|IE|IT|LV|LT|LU|MT|MQ|YT|MC|ME|NL|PT|RE|BL|MF|PM|SM|SK|SI|ES|CE|CH|AX") }} 31 | 32 | if (req.http.X-Country-Code ~ "{{ $usd_codes }}") { 33 | set req.http.X-Currency = "USD"; 34 | } else if (req.http.X-Country-Code ~ "{{ $eur_codes }}") { 35 | set req.http.X-Currency = "EUR"; 36 | } 37 | } 38 | } 39 | 40 | include "includes/purge.vcl"; 41 | include "includes/mobile.vcl"; 42 | 43 | # See https://book.varnish-software.com/4.0/chapters/VCL_Basics.html 44 | 45 | include "defaults/vcl_recv.vcl"; 46 | 47 | include "preset.vcl"; 48 | include "includes/static.vcl"; 49 | 50 | include "defaults/vcl_hash.vcl"; 51 | include "defaults/vcl_pipe.vcl"; 52 | include "defaults/vcl_backend_response.vcl"; 53 | include "defaults/vcl_backend_error.vcl"; 54 | include "defaults/vcl_deliver.vcl"; 55 | include "defaults/vcl_hit.vcl"; 56 | include "defaults/vcl_miss.vcl"; -------------------------------------------------------------------------------- /templates/defaults/vcl_backend_error.vcl.tmpl: -------------------------------------------------------------------------------- 1 | # In the event of an error, show friendlier messages. 2 | sub vcl_backend_error { 3 | set beresp.http.Content-Type = "text/html; charset=utf-8"; 4 | synthetic ({" 5 | 6 | 7 | Not Found 8 | 14 | 15 | 16 |

Page Unavailable

17 |

The page you requested is temporarily unavailable.

18 | 19 | 20 | "}); 21 | return (deliver); 22 | } 23 | -------------------------------------------------------------------------------- /templates/defaults/vcl_backend_response.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_backend_response { 2 | set beresp.http.X-VC-Req-Host = bereq.http.host; 3 | set beresp.http.X-VC-Req-URL = bereq.url; 4 | set beresp.http.X-VC-Req-URL-Base = regsub(bereq.url, "\?.*$", ""); 5 | 6 | if (beresp.grace < {{ getenv "VARNISH_BACKEND_GRACE" "2m" }}) { 7 | set beresp.grace = {{ getenv "VARNISH_BACKEND_GRACE" "2m" }}; 8 | } 9 | 10 | # Overwrite ttl with X-VC-TTL. 11 | if (beresp.http.X-VC-TTL) { 12 | set beresp.ttl = std.duration(beresp.http.X-VC-TTL + "s", 0s); 13 | } 14 | 15 | if (beresp.http.Set-Cookie) { 16 | set beresp.ttl = 0s; 17 | } 18 | 19 | if (bereq.http.X-VC-Cacheable ~ "^NO") { 20 | set beresp.http.X-VC-Cacheable = bereq.http.X-VC-Cacheable; 21 | set beresp.uncacheable = true; 22 | set beresp.ttl = {{ getenv "VARNISH_DEFAULT_TTL" "120s" }}; 23 | 24 | } else if (beresp.ttl <= 0s) { 25 | if (!beresp.http.X-VC-Cacheable) { 26 | set beresp.http.X-VC-Cacheable = "NO:Not cacheable, ttl: "+ beresp.ttl; 27 | } 28 | set beresp.uncacheable = true; 29 | set beresp.ttl = {{ getenv "VARNISH_DEFAULT_TTL" "120s" }}; 30 | 31 | } else if (beresp.http.Cache-Control ~ "private") { 32 | set beresp.http.X-VC-Cacheable = "NO:Cache-Control=private"; 33 | set beresp.uncacheable = true; 34 | set beresp.ttl = {{ getenv "VARNISH_DEFAULT_TTL" "120s" }}; 35 | 36 | } else if (beresp.http.Cache-Control ~ "no-cache") { 37 | set beresp.http.X-VC-Cacheable = "NO:Cache-Control=no-cache"; 38 | set beresp.uncacheable = true; 39 | set beresp.ttl = {{ getenv "VARNISH_DEFAULT_TTL" "120s" }}; 40 | 41 | } else if (beresp.http.X-VC-Enabled ~ "true") { 42 | if (!beresp.http.X-VC-Cacheable) { 43 | set beresp.http.X-VC-Cacheable = "YES:Is cacheable, ttl: " + beresp.ttl; 44 | } 45 | 46 | } else if (beresp.http.X-VC-Enabled ~ "false") { 47 | if (!beresp.http.X-VC-Cacheable) { 48 | set beresp.http.X-VC-Cacheable = "NO:Disabled"; 49 | } 50 | set beresp.ttl = 0s; 51 | } 52 | 53 | if (beresp.status == 404 || beresp.status >= 500) { 54 | set beresp.ttl = 0s; 55 | set beresp.grace = {{ getenv "VARNISH_ERRORS_GRACE" "15s" }}; 56 | } 57 | 58 | # Set ban-lurker friendly custom headers. 59 | set beresp.http.X-Url = bereq.url; 60 | set beresp.http.X-Host = bereq.http.host; 61 | 62 | if (beresp.http.Surrogate-Control ~ "BigPipe/1.0") { 63 | set beresp.do_stream = true; 64 | set beresp.ttl = 0s; 65 | } 66 | 67 | {{ if and (getenv "VARNISH_SECONDARY_STORAGE_CONDITION") (getenv "VARNISHD_SECONDARY_STORAGE") }} 68 | if ({{ getenv "VARNISH_SECONDARY_STORAGE_CONDITION" }}) { 69 | set beresp.http.x-varnish-storage = "secondary"; 70 | } 71 | {{ end }} 72 | 73 | return(deliver); 74 | } 75 | -------------------------------------------------------------------------------- /templates/defaults/vcl_deliver.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_deliver { 2 | unset resp.http.X-VC-Req-Host; 3 | unset resp.http.X-VC-Req-URL; 4 | unset resp.http.X-VC-Req-URL-Base; 5 | unset resp.http.Via; 6 | 7 | # Remove ban-lurker friendly custom headers when delivering to client. 8 | unset resp.http.X-Url; 9 | unset resp.http.X-Host; 10 | unset resp.http.Cache-Tags; 11 | 12 | if (obj.hits > 0) { 13 | set resp.http.X-VC-Cache = "HIT"; 14 | } else { 15 | set resp.http.X-VC-Cache = "MISS"; 16 | } 17 | 18 | if (req.http.X-VC-Debug ~ "true" || resp.http.X-VC-Debug ~ "true") { 19 | set resp.http.X-VC-Hash = req.http.hash; 20 | if (req.http.X-VC-DebugMessage) { 21 | set resp.http.X-VC-DebugMessage = req.http.X-VC-DebugMessage; 22 | } 23 | } else { 24 | unset resp.http.X-VC-Enabled; 25 | unset resp.http.X-VC-Debug; 26 | unset resp.http.X-VC-DebugMessage; 27 | unset resp.http.X-VC-Cacheable; 28 | unset resp.http.X-VC-Purge-Key-Auth; 29 | unset resp.http.X-VC-TTL; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /templates/defaults/vcl_hash.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_hash { 2 | set req.http.hash = req.url; 3 | if (req.http.host) { 4 | set req.http.hash = req.http.hash + "#" + req.http.host; 5 | } else { 6 | set req.http.hash = req.http.hash + "#" + server.ip; 7 | } 8 | 9 | if (req.http.X-Forwarded-Proto ~ "https") { 10 | set req.http.hash = req.http.hash + req.http.X-Forwarded-Proto; 11 | } 12 | 13 | if (req.http.vckey) { 14 | hash_data(req.http.vckey); 15 | unset req.http.vckey; 16 | } 17 | 18 | {{ if getenv "VARNISH_CACHE_PER_COUNTRY" }} 19 | if (req.http.X-Country-Code) { 20 | hash_data(req.http.X-Country-Code); 21 | } 22 | {{ end }} 23 | 24 | {{ if getenv "VARNISH_CACHE_PER_CURRENCY" }} 25 | if (req.http.X-Currency) { 26 | hash_data(req.http.X-Currency); 27 | } 28 | {{ end }} 29 | } 30 | -------------------------------------------------------------------------------- /templates/defaults/vcl_hit.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_hit { 2 | ### Pagespeed 3 | # Based on the suggestions https://www.modpagespeed.com/doc/downstream-caching 4 | {{ if getenv "VARNISH_PAGESPEED_SECRET_KEY" }} 5 | # 5% of the time ignore that we got a cache hit and send the request to the 6 | # backend anyway for instrumentation. 7 | if (std.random(0, 100) < 5) { 8 | set req.http.PS-ShouldBeacon = "{{ getenv "VARNISH_PAGESPEED_SECRET_KEY" }}"; 9 | return (pass); 10 | } 11 | {{ end }} 12 | ### End of Pagespeed 13 | } 14 | -------------------------------------------------------------------------------- /templates/defaults/vcl_miss.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_miss { 2 | ### Pagespeed 3 | # Based on the suggestions https://www.modpagespeed.com/doc/downstream-caching 4 | {{ if getenv "VARNISH_PAGESPEED_SECRET_KEY" }} 5 | # Instrument 25% of cache misses. 6 | if (std.random(0, 100) < 25) { 7 | set req.http.PS-ShouldBeacon = "{{ getenv "VARNISH_PAGESPEED_SECRET_KEY" }}"; 8 | return (pass); 9 | } 10 | {{ end }} 11 | ### End of Pagespeed 12 | } 13 | -------------------------------------------------------------------------------- /templates/defaults/vcl_pipe.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_pipe { 2 | # Called upon entering pipe mode. 3 | # In this mode, the request is passed on to the backend, and any further data from both the client 4 | # and backend is passed on unaltered until either end closes the connection. Basically, Varnish will 5 | # degrade into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, 6 | # no other VCL subroutine will ever get called after vcl_pipe. 7 | 8 | # Note that only the first request to the backend will have 9 | # X-Forwarded-For set. If you use X-Forwarded-For and want to 10 | # have it set for all requests, make sure to have: 11 | # set bereq.http.connection = "close"; 12 | # here. It is not set by default as it might break some broken web 13 | # applications, like IIS with NTLM authentication. 14 | 15 | # Implementing websocket support 16 | if (req.http.upgrade) { 17 | set bereq.http.upgrade = req.http.upgrade; 18 | set bereq.http.connection = req.http.connection; 19 | } 20 | 21 | {{ if getenv "VARNISH_PIPE_CLOSE_CONNECTION" }} 22 | set bereq.http.Connection = "Close"; 23 | {{ end }} 24 | 25 | return (pipe); 26 | } 27 | -------------------------------------------------------------------------------- /templates/defaults/vcl_recv.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_recv { 2 | # Only cache GET and HEAD requests (pass through POST requests). 3 | if (req.method != "GET" && req.method != "HEAD") { 4 | set req.http.X-VC-Cacheable = "NO:Request method:" + req.method; 5 | return(pass); 6 | } 7 | 8 | # Implementing websocket support. 9 | if (req.http.Upgrade ~ "(?i)websocket") { 10 | return (pipe); 11 | } 12 | 13 | # Do not cache ajax requests. 14 | if (req.http.X-Requested-With == "XMLHttpRequest") { 15 | set req.http.X-VC-Cacheable = "NO:Requested with: XMLHttpRequest"; 16 | return(pass); 17 | } 18 | 19 | # Strip hash, server does not need it. 20 | if (req.url ~ "\#") { 21 | set req.url = regsub(req.url, "\#.*$", ""); 22 | } 23 | 24 | # Strip a trailing ? if it exists 25 | if (req.url ~ "\?$") { 26 | set req.url = regsub(req.url, "\?$", ""); 27 | } 28 | 29 | {{ if not (getenv "VARNISH_KEEP_ALL_COOKIES") }} 30 | set req.http.vckey = ";" + req.http.Cookie; 31 | set req.http.vckey = regsuball(req.http.vckey, "; +", ";"); 32 | set req.http.vckey = regsuball(req.http.vckey, ";(VCKEY-[a-zA-Z0-9-_]+)=", "; \1="); 33 | set req.http.vckey = regsuball(req.http.vckey, ";[^ ][^;]*", ""); 34 | set req.http.vckey = regsuball(req.http.vckey, "^[; ]+|[; ]+$", ""); 35 | 36 | {{ $cookies := (getenv "VARNISH_STRIP_COOKIES" "__[a-z]+|wooTracker|VCKEY-[a-zA-Z0-9-_]+") }} 37 | set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)({{ $cookies }})=[^;]*", ""); 38 | set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 39 | if (req.http.Cookie ~ "^\s*$") { 40 | unset req.http.Cookie; 41 | } 42 | {{ end }} 43 | 44 | {{ if not (getenv "VARNISH_KEEP_ALL_PARAMS") }} 45 | # Strip query parameters from all urls (so they cache as a single object). 46 | {{ if getenv "VARNISH_STRIP_ALL_PARAMS" }} 47 | if (req.url ~ "\?.*") { 48 | set req.url = regsub(req.url, "\?.*", ""); 49 | } 50 | {{ else }} 51 | {{ $params := (getenv "VARNISH_STRIP_PARAMS" "utm_[a-z]+|gclid|cx|ie|cof|siteurl|fbclid") }} 52 | if (req.url ~ "(\?|&)({{ $params }})=") { 53 | set req.url = regsuball(req.url, "&({{ $params }})=([A-z0-9_\-\.%25]+)", ""); 54 | set req.url = regsuball(req.url, "\?({{ $params }})=([A-z0-9_\-\.%25]+)", "?"); 55 | set req.url = regsub(req.url, "\?&", "?"); 56 | set req.url = regsub(req.url, "\?$", ""); 57 | } 58 | {{ end }} 59 | {{ end }} 60 | 61 | ### Pagespeed 62 | # Based on the suggestions https://www.modpagespeed.com/doc/downstream-caching 63 | {{ if getenv "VARNISH_PAGESPEED_SECRET_KEY" }} 64 | {{ if getenv "VARNISH_CACHE_STATIC_FILES" }} 65 | # Tell PageSpeed not to use optimizations specific to this request. 66 | set req.http.PS-CapabilityList = "fully general optimizations only"; 67 | {{ end }} 68 | {{ end }} 69 | ### End of Pagespeed 70 | } -------------------------------------------------------------------------------- /templates/includes/backend.vcl.tmpl: -------------------------------------------------------------------------------- 1 | import directors; 2 | 3 | backend backend1 { 4 | .host = "{{ getenv "VARNISH_BACKEND_HOST" }}"; 5 | .port = "{{ getenv "VARNISH_BACKEND_PORT" "80" }}"; 6 | .first_byte_timeout = {{ getenv "VARNISH_BACKEND_FIRST_BYTE_TIMEOUT" "60s" }}; 7 | .connect_timeout = {{ getenv "VARNISH_BACKEND_CONNECT_TIMEOUT" "3.5s" }}; 8 | .between_bytes_timeout = {{ getenv "VARNISH_BACKEND_BETWEEN_BYTES_TIMEOUT" "60s" }}; 9 | } 10 | 11 | sub vcl_init { 12 | new backends = directors.round_robin(); 13 | backends.add_backend(backend1); 14 | } 15 | 16 | sub vcl_recv { 17 | set req.backend_hint = backends.backend(); 18 | } 19 | -------------------------------------------------------------------------------- /templates/includes/mobile.vcl.tmpl: -------------------------------------------------------------------------------- 1 | {{ $mobile := (getenv "VARNISH_MOBILE_USER_AGENT" "ipod|android|blackberry|phone|mobile|kindle|silk|fennec|tablet|webos|palm|windows ce|nokia|philips|samsung|sanyo|sony|panasonic|ericsson|alcatel|series60|series40|opera mini|opera mobi|au-mic|audiovox|avantgo|blazer|danger|docomo|epoc|ericy|i-mode|ipaq|midp-|mot-|netfront|nitro|pocket|portalmmm|rover|sie-|symbian|cldc-|j2me|up\\.browser|up\\.link|vodafone|wap1\\.|wap2\\.") }} 2 | 3 | {{ if getenv "VARNISH_MOBILE_SEPARATE_CASH" }} 4 | sub vcl_hash { 5 | if (req.http.User-Agent ~ "(?i){{ $mobile }}") { 6 | hash_data("mobile"); 7 | } 8 | } 9 | {{ end }} 10 | 11 | {{ if getenv "VARNISH_MOBILE_DISABLE_CASH" }} 12 | sub vcl_recv { 13 | if (req.http.User-Agent ~ "(?i){{ $mobile }}") { 14 | return(pass); 15 | } 16 | } 17 | {{ end }} 18 | -------------------------------------------------------------------------------- /templates/includes/purge.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub purge_regex { 2 | ban("obj.http.X-VC-Req-URL ~ " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 3 | } 4 | 5 | sub purge_exact { 6 | ban("obj.http.X-VC-Req-URL == " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 7 | } 8 | 9 | # Use the exact request URL, but ignore any query params 10 | sub purge_page { 11 | set req.url = regsub(req.url, "\?.*$", ""); 12 | ban("obj.http.X-VC-Req-URL-Base == " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 13 | } 14 | 15 | # The purge behavior can be controlled with the X-VC-Purge-Method header. 16 | # 17 | # Setting the X-VC-Purge-Method header to contain "regex" or "exact" will use 18 | # those respective behaviors. Any other value for the X-Purge header will 19 | # use the default ("page") behavior. 20 | # 21 | # The X-VC-Purge-Method header is not case-sensitive. 22 | # 23 | # If no X-VC-Purge-Method header is set, the request url is inspected to attempt 24 | # a best guess as to what purge behavior is expected. This should work for 25 | # most cases, although if you want to guarantee some behavior you should 26 | # always set the X-VC-Purge-Method header. 27 | 28 | sub vcl_recv { 29 | set req.http.X-VC-My-Purge-Key = "{{ getenv "VARNISH_PURGE_KEY" }}"; 30 | if (req.method == "PURGE" || req.method == "BAN") { 31 | {{ if not (getenv "VARNISH_ALLOW_UNRESTRICTED_PURGE") }} 32 | if (req.http.X-VC-Purge-Key == req.http.X-VC-My-Purge-Key) { 33 | set req.http.X-VC-Purge-Key-Auth = "true"; 34 | } else { 35 | set req.http.X-VC-Purge-Key-Auth = "false"; 36 | } 37 | # Allow unrestricted purge from internal network. 38 | {{ if (getenv "VARNISH_PURGE_EXTERNAL_REQUEST_HEADER") }} 39 | # If the header is set then it's an external request if we should check the purge key. 40 | if (req.http.{{ getenv "VARNISH_PURGE_EXTERNAL_REQUEST_HEADER" }} && req.http.X-VC-Purge-Key-Auth != "true") { 41 | return (synth(405, "Not allowed from " + client.ip)); 42 | } 43 | # Usual authorization by the purge key. 44 | {{ else }} 45 | if (req.http.X-VC-Purge-Key-Auth != "true") { 46 | return (synth(405, "Not allowed from " + client.ip)); 47 | } 48 | {{ end }} 49 | {{ end }} 50 | 51 | if (req.method == "BAN" && req.http.Cache-Tags) { 52 | ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags); 53 | } 54 | 55 | if (req.http.X-VC-Purge-Method) { 56 | if (req.http.X-VC-Purge-Method ~ "(?i)regex") { 57 | call purge_regex; 58 | } elsif (req.http.X-VC-Purge-Method ~ "(?i)exact") { 59 | call purge_exact; 60 | } else { 61 | call purge_page; 62 | } 63 | } else { 64 | # No X-VC-Purge-Method header was specified. 65 | # Do our best to figure out which one they want. 66 | if (req.url ~ "\.\*" || req.url ~ "^\^" || req.url ~ "\$$" || req.url ~ "\\[.?*+^$|()]") { 67 | call purge_regex; 68 | } elsif (req.url ~ "\?") { 69 | call purge_exact; 70 | } else { 71 | call purge_page; 72 | } 73 | } 74 | return (synth(200,"Purged " + req.url + " " + req.http.host)); 75 | } 76 | unset req.http.X-VC-My-Purge-Key; 77 | # unset Varnish Caching custom headers from client 78 | unset req.http.X-VC-Cacheable; 79 | unset req.http.X-VC-Debug; 80 | } 81 | -------------------------------------------------------------------------------- /templates/includes/static.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_backend_response { 2 | # Bypass cache for files > N MB 3 | if (std.integer(beresp.http.Content-Length, 0) > {{ getenv "VARNISH_BIG_FILES_SIZE" "10485760" }}) { 4 | set beresp.uncacheable = true; 5 | set beresp.ttl = {{ getenv "VARNISH_BIG_FILES_TTL" "120s" }}; 6 | return (deliver); 7 | } 8 | } 9 | 10 | {{ $static_files := (getenv "VARNISH_STATIC_FILES" "asc|doc|xls|ppt|csv|svg|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm|webp") }} 11 | 12 | sub vcl_recv { 13 | if (req.url ~ "(?i)\.({{ $static_files }})(\?.*)?$") { 14 | {{ if not (getenv "VARNISH_CACHE_STATIC_FILES") }} 15 | # Do not use memory to cache static files. 16 | return (pass); 17 | {{ end }} 18 | # unset cookie only if no http auth 19 | if (!req.http.Authorization) { 20 | unset req.http.Cookie; 21 | } 22 | return(hash); 23 | } 24 | } 25 | 26 | sub vcl_backend_response { 27 | {{ if getenv "VARNISH_CACHE_STATIC_FILES" }} 28 | if (bereq.url ~ "(?i)\.({{ $static_files }})(\?.*)?$") { 29 | # Don't allow static files to set cookies. 30 | unset beresp.http.set-cookie; 31 | # Overwrite ttl with X-VC-TTL 32 | set beresp.http.X-VC-TTL = {{ getenv "VARNISH_STATIC_TTL" "86400" }}; 33 | set beresp.ttl = std.duration(beresp.http.X-VC-TTL + "s", 0s); 34 | set beresp.http.X-VC-Cacheable = "YES:Is cacheable, ttl: " + beresp.ttl; 35 | } 36 | {{ end }} 37 | } 38 | -------------------------------------------------------------------------------- /templates/presets/drupal.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_recv { 2 | {{ $exclude_urls := (getenv "VARNISH_DRUPAL_EXCLUDE_URLS" "^(/update\\.php|/([a-z]{2}/)?admin|/([a-z]{2}/)?admin/.*|/([a-z]{2}/)?system/files/.*|/([a-z]{2}/)?flag/.*|.*/ajax/.*|.*/ahah/.*)$") }} 3 | 4 | # Pass through any administrative or AJAX-related paths. 5 | if (req.url ~ "{{ $exclude_urls }}") { 6 | return (pass); 7 | } 8 | 9 | if (req.url ~ "(^/([a-z]{2}/)?batch)") { 10 | return (pipe); 11 | } 12 | 13 | # Remove all cookies that Drupal doesn't need to know about. We explicitly 14 | # list the ones that Drupal does need, the SESS and NO_CACHE. If, after 15 | # running this code we find that either of these two cookies remains, we 16 | # will pass as the page cannot be cached. 17 | if (req.http.Cookie) { 18 | {{ $preserved_cookies := (getenv "VARNISH_DRUPAL_PRESERVED_COOKIES" "SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE") }} 19 | 20 | # 1. Append a semi-colon to the front of the cookie string. 21 | # 2. Remove all spaces that appear after semi-colons. 22 | # 3. Match the cookies we want to keep, adding the space we removed 23 | # previously back. (\1) is first matching group in the regsuball. 24 | # 4. Remove all other cookies, identifying them by the fact that they have 25 | # no space after the preceding semi-colon. 26 | # 5. Remove all spaces and semi-colons from the beginning and end of the 27 | # cookie string. 28 | set req.http.Cookie = ";" + req.http.Cookie; 29 | set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); 30 | set req.http.Cookie = regsuball(req.http.Cookie, ";({{ $preserved_cookies }})=", "; \1="); 31 | set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); 32 | set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); 33 | 34 | if (req.http.Cookie == "") { 35 | # If there are no remaining cookies, remove the cookie header. If there 36 | # aren't any cookie headers, Varnish's default behavior will be to cache 37 | # the page. 38 | unset req.http.Cookie; 39 | } 40 | else { 41 | # If there is any cookies left (a session or NO_CACHE cookie), do not 42 | # cache the page. Pass it on to Apache directly. 43 | return (pass); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /templates/presets/wordpress.vcl.tmpl: -------------------------------------------------------------------------------- 1 | sub vcl_recv { 2 | {{ if getenv "VARNISH_WP_ADMIN_SUBDOMAIN" }} 3 | if (req.http.host ~ "{{getenv "VARNISH_WP_ADMIN_SUBDOMAIN" }}") { 4 | set req.http.X-VC-Cacheable = "NO:Admin domain"; 5 | return(pass); 6 | } 7 | {{ else }} 8 | # pass wp-admin urls 9 | if (req.url ~ "(wp-login|wp-admin)" || req.url ~ "preview=true" || req.url ~ "xmlrpc.php") { 10 | set req.http.X-VC-Cacheable = "NO:Admin"; 11 | return (pass); 12 | } 13 | {{ end }} 14 | 15 | # Pass through some requests that are specifically for the WordPress Jetpack mobile plugin. 16 | if (req.url ~ "\?(.*&)?(ak_action|app-download)=") { 17 | return(pass); 18 | } 19 | 20 | # Remove replytocom= param. 21 | set req.url = regsub(req.url, "\?replytocom=.*$", ""); 22 | 23 | # Remove all cookies that WordPress doesn't need to know about. We explicitly 24 | # list the ones that WordPress does need. If, after 25 | # running this code we find that either of these two cookies remains, we 26 | # will pass as the page cannot be cached. 27 | if (req.http.Cookie) { 28 | {{ $preserved_cookies := (getenv "VARNISH_WP_PRESERVED_COOKIES" "PHPSESSID|wp-postpass_[a-z0-9]+|wordpress_[_a-z0-9]+|wordpress_logged_in_[a-z0-9]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-z0-9]+|akm_mobile") }} 29 | 30 | # 1. Append a semi-colon to the front of the cookie string. 31 | # 2. Remove all spaces that appear after semi-colons. 32 | # 3. Match the cookies we want to keep, adding the space we removed 33 | # previously back. (\1) is first matching group in the regsuball. 34 | # 4. Remove all other cookies, identifying them by the fact that they have 35 | # no space after the preceding semi-colon. 36 | # 5. Remove all spaces and semi-colons from the beginning and end of the 37 | # cookie string. 38 | set req.http.Cookie = ";" + req.http.Cookie; 39 | set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); 40 | set req.http.Cookie = regsuball(req.http.Cookie, ";({{ $preserved_cookies }})=", "; \1="); 41 | set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); 42 | set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); 43 | 44 | if (req.http.Cookie == "") { 45 | # If there are no remaining cookies, remove the cookie header. If there 46 | # aren't any cookie headers, Varnish's default behavior will be to cache 47 | # the page. 48 | unset req.http.Cookie; 49 | } 50 | else { 51 | # If there is any cookies left (a session or NO_CACHE cookie), do not 52 | # cache the page. Pass it on to Apache directly. 53 | return (pass); 54 | } 55 | } 56 | 57 | return(hash); 58 | } 59 | -------------------------------------------------------------------------------- /templates/varnishd.init.d.tmpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | exec varnishd \ 10 | -j unix,user=varnish \ 11 | -F \ 12 | -a :6081 \ 13 | -T :6082 \ 14 | -f {{ getenv "VARNISHD_VCL_SCRIPT" "/etc/varnish/default.vcl" }} \ 15 | -S {{ getenv "VARNISHD_SECRET_FILE" "/etc/varnish/secret" }} \ 16 | -s main=malloc,{{ getenv "VARNISHD_MEMORY_SIZE" "64M" }} \ 17 | {{ if getenv "VARNISHD_SECONDARY_STORAGE" }}-s secondary={{ getenv "VARNISHD_SECONDARY_STORAGE" }}{{ end }} \ 18 | -t {{ getenv "VARNISHD_DEFAULT_TTL" "120" }} \ 19 | -p ban_lurker_age={{ getenv "VARNISHD_PARAM_BAN_LURKER_AGE" "60.000" }} \ 20 | -p ban_lurker_batch={{ getenv "VARNISHD_PARAM_BAN_LURKER_BATCH" "1000" }} \ 21 | -p ban_lurker_sleep={{ getenv "VARNISHD_PARAM_BAN_LURKER_SLEEP" "0.010" }} \ 22 | -p between_bytes_timeout={{ getenv "VARNISHD_PARAM_BETWEEN_BYTES_TIMEOUT" "60.000" }} \ 23 | -p connect_timeout={{ getenv "VARNISHD_PARAM_CONNECT_TIMEOUT" "3.500" }} \ 24 | -p default_grace={{ getenv "VARNISHD_PARAM_DEFAULT_GRACE" "10.000" }} \ 25 | -p default_keep={{ getenv "VARNISHD_PARAM_DEFAULT_KEEP" "0.000" }} \ 26 | -p default_ttl={{ getenv "VARNISHD_PARAM_DEFAULT_TTL" "120.000" }} \ 27 | -p fetch_chunksize={{ getenv "VARNISHD_PARAM_FETCH_CHUNKSIZE" "16k" }} \ 28 | -p first_byte_timeout={{ getenv "VARNISHD_PARAM_FIRST_BYTE_TIMEOUT" "60.000" }} \ 29 | -p gzip_buffer={{ getenv "VARNISHD_PARAM_GZIP_BUFFER" "32k" }} \ 30 | -p gzip_level={{ getenv "VARNISHD_PARAM_GZIP_LEVEL" "6" }} \ 31 | -p gzip_memlevel={{ getenv "VARNISHD_PARAM_GZIP_MEMLEVEL" "8" }} \ 32 | -p http_gzip_support={{ getenv "VARNISHD_PARAM_HTTP_GZIP_SUPPORT" "on" }} \ 33 | -p http_max_hdr={{ getenv "VARNISHD_PARAM_HTTP_MAX_HDR" "64" }} \ 34 | -p http_req_hdr_len={{ getenv "VARNISHD_PARAM_HTTP_REQ_HDR_LEN" "8k" }} \ 35 | -p http_req_size={{ getenv "VARNISHD_PARAM_HTTP_REQ_SIZE" "32k" }} \ 36 | -p http_resp_hdr_len={{ getenv "VARNISHD_PARAM_HTTP_RESP_HDR_LEN" "8k" }} \ 37 | -p http_resp_size={{ getenv "VARNISHD_PARAM_HTTP_RESP_SIZE" "32k" }} \ 38 | -p idle_send_timeout={{ getenv "VARNISHD_PARAM_IDLE_SEND_TIMEOUT" "60.000" }} \ 39 | -p max_esi_depth={{ getenv "VARNISHD_PARAM_MAX_ESI_DEPTH" "5" }} \ 40 | -p max_restarts={{ getenv "VARNISHD_PARAM_MAX_RESTARTS" "4" }} \ 41 | -p max_retries={{ getenv "VARNISHD_PARAM_MAX_RETRIES" "4" }} \ 42 | -p nuke_limit={{ getenv "VARNISHD_PARAM_NUKE_LIMIT" "50" }} \ 43 | -p ping_interval={{ getenv "VARNISHD_PARAM_PING_INTERVAL" "3" }} \ 44 | -p pipe_timeout={{ getenv "VARNISHD_PARAM_PIPE_TIMEOUT" "60.000" }} \ 45 | -p pool_req={{ getenv "VARNISHD_PARAM_POOL_REQ" "10,100,10" }} \ 46 | -p pool_sess={{ getenv "VARNISHD_PARAM_POOL_SESS" "10,100,10" }} \ 47 | -p prefer_ipv6={{ getenv "VARNISHD_PARAM_PREFER_IPV6" "off" }} \ 48 | -p rush_exponent={{ getenv "VARNISHD_PARAM_RUSH_EXPONENT" "3" }} \ 49 | -p send_timeout={{ getenv "VARNISHD_PARAM_SEND_TIMEOUT" "600" }} \ 50 | -p shortlived={{ getenv "VARNISHD_PARAM_SHORTLIVED" "10.000" }} \ 51 | -p tcp_keepalive_intvl={{ getenv "VARNISHD_PARAM_TCP_KEEPALIVE_INTVL" "75.000" }} \ 52 | -p tcp_keepalive_probes={{ getenv "VARNISHD_PARAM_TCP_KEEPALIVE_PROBES" "8" }} \ 53 | -p tcp_keepalive_time={{ getenv "VARNISHD_PARAM_TCP_KEEPALIVE_TIME" "7200.000" }} \ 54 | -p thread_pool_add_delay={{ getenv "VARNISHD_PARAM_THREAD_POOL_ADD_DELAY" "0.000" }} \ 55 | -p thread_pool_destroy_delay={{ getenv "VARNISHD_PARAM_THREAD_POOL_DESTROY_DELAY" "1.000" }} \ 56 | -p thread_pool_fail_delay={{ getenv "VARNISHD_PARAM_THREAD_POOL_FAIL_DELAY" "0.200" }} \ 57 | -p thread_pool_max={{ getenv "VARNISHD_PARAM_THREAD_POOL_MAX" "5000" }} \ 58 | -p thread_pool_min={{ getenv "VARNISHD_PARAM_THREAD_POOL_MIN" "100" }} \ 59 | -p thread_pool_stack={{ getenv "VARNISHD_PARAM_THREAD_POOL_STACK" "48k" }} \ 60 | -p thread_pool_timeout={{ getenv "VARNISHD_PARAM_THREAD_POOL_TIMEOUT" "300.000" }} \ 61 | -p thread_pools={{ getenv "VARNISHD_PARAM_THREAD_POOLS" "2" }} \ 62 | -p thread_queue_limit={{ getenv "VARNISHD_PARAM_THREAD_QUEUE_LIMIT" "20" }} \ 63 | -p timeout_idle={{ getenv "VARNISHD_PARAM_TIMEOUT_IDLE" "5.000" }} \ 64 | -p timeout_linger={{ getenv "VARNISHD_PARAM_TIMEOUT_LINGER" "0.050" }} \ 65 | -p vsl_buffer={{ getenv "VARNISHD_PARAM_VSL_BUFFER" "4k" }} \ 66 | -p vsl_reclen={{ getenv "VARNISHD_PARAM_VSL_RECLEN" "255b" }} \ 67 | -p vsl_space={{ getenv "VARNISHD_PARAM_VSL_SPACE" "80M" }} \ 68 | -p vsm_space={{ getenv "VARNISHD_PARAM_VSM_SPACE" "1M" }} \ 69 | -p workspace_backend={{ getenv "VARNISHD_PARAM_WORKSPACE_BACKEND" "64k" }} \ 70 | -p workspace_client={{ getenv "VARNISHD_PARAM_WORKSPACE_CLIENT" "64k" }} \ 71 | -p workspace_session={{ getenv "VARNISHD_PARAM_WORKSPACE_SESSION" "0.50k" }} \ 72 | -p workspace_thread={{ getenv "VARNISHD_PARAM_WORKSPACE_THREAD" "2k" }} 73 | -------------------------------------------------------------------------------- /tests/basic/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | image: wodby/nginx 4 | environment: 5 | NGINX_BACKEND_HOST: php 6 | NGINX_VHOST_PRESET: php 7 | depends_on: 8 | - php 9 | 10 | php: 11 | image: wodby/php 12 | 13 | varnish: 14 | image: $IMAGE 15 | depends_on: 16 | - nginx 17 | environment: 18 | VARNISH_IMPORT_MODULES: cookie,header,digest 19 | VARNISH_BACKEND_HOST: nginx 20 | -------------------------------------------------------------------------------- /tests/basic/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | varnish() { 10 | docker compose exec -T varnish "${@}" 11 | } 12 | 13 | docker compose up -d 14 | 15 | echo -n "Running check-ready action... " 16 | varnish make check-ready max_try=10 -f /usr/local/bin/actions.mk 17 | echo "OK" 18 | 19 | echo -n "Running flush action... " 20 | varnish make flush -f /usr/local/bin/actions.mk 21 | echo "OK" 22 | 23 | us_ip="185.229.59.42" 24 | 25 | echo -n "Checking healthz endpoint... " 26 | varnish curl -s -o /dev/null -w '%{http_code}' "localhost:6081/.vchealthz" | grep -q 204 27 | echo "OK" 28 | 29 | echo -n "Checking varnish backend response containing a country code header detected via geoip module... " 30 | docker compose exec -T php sh -c 'echo " /var/www/html/index.php' 31 | varnish curl --header "X-Real-IP: ${us_ip}" -s "localhost:6081" | grep -q "US" 32 | varnish make flush -f /usr/local/bin/actions.mk 33 | echo "OK" 34 | 35 | echo -n "Checking varnish backend response containing the currency... " 36 | docker compose exec -T php sh -c 'echo " /var/www/html/index.php' 37 | varnish curl --header "X-Real-IP: ${us_ip}" -s "localhost:6081" | grep -q "USD" 38 | varnish make flush -f /usr/local/bin/actions.mk 39 | echo "OK" 40 | 41 | echo -n "Checking varnish backend response containing the currency (from Cloudflare \"CF-IPCountry\" header)... " 42 | docker compose exec -T php sh -c 'echo " /var/www/html/index.php' 43 | varnish curl --header "CF-IPCountry: US" -s "localhost:6081" | grep -q "USD" 44 | varnish make flush -f /usr/local/bin/actions.mk 45 | echo "OK" 46 | 47 | echo -n "Checking varnish VCKEY cookies... " 48 | docker compose exec -T php sh -c 'echo " /var/www/html/index.php' 49 | varnish sh -c 'curl -sI -b "VCKEYinvalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 50 | varnish sh -c 'curl -sI -b "VCKEYinvalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 51 | varnish sh -c 'curl -sI -b "VCKEY-.invalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 52 | varnish sh -c 'curl -sI -b "VCKEY-.invalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 53 | varnish sh -c 'curl -sI -b "vckey-invalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 54 | varnish sh -c 'curl -sI -b "vckey-invalid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 55 | varnish sh -c 'curl -sI -b "VCKEY-valid=123" localhost:6081 | grep -q "X-VC-Cache: MISS"' 56 | varnish sh -c 'curl -sI -b "VCKEY-valid=123" localhost:6081 | grep -q "X-VC-Cache: HIT"' 57 | varnish sh -c 'curl -sI -b "VCKEY-valid=123; VCKEY-multiple-cookies_1=123;" localhost:6081 | grep -q "X-VC-Cache: MISS"' 58 | varnish sh -c 'curl -sI -b "VCKEY-valid=123; VCKEY-multiple-cookies_1=123;" localhost:6081 | grep -q "X-VC-Cache: HIT"' 59 | varnish sh -c 'curl -sI -b "VCKEY-valid=123; VCKEY-multiple-cookies_1=321;" localhost:6081 | grep -q "X-VC-Cache: MISS"' 60 | echo "OK" 61 | 62 | docker compose down 63 | -------------------------------------------------------------------------------- /tests/drupal/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | image: nginx:stable-alpine 4 | 5 | varnish: 6 | image: $IMAGE 7 | depends_on: 8 | - nginx 9 | environment: 10 | VARNISH_BACKEND_HOST: nginx 11 | VARNISH_CONFIG_PRESET: drupal 12 | -------------------------------------------------------------------------------- /tests/drupal/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | docker compose up -d 10 | 11 | echo "Running check-ready action... " 12 | docker compose exec -T varnish make check-ready max_try=10 wait_seconds=1 delay_seconds=1 -f /usr/local/bin/actions.mk 13 | 14 | echo "Running flush action... " 15 | docker compose exec -T varnish make flush -f /usr/local/bin/actions.mk 16 | 17 | echo -n "Checking varnish backend response... " 18 | docker compose exec -T varnish curl -s "localhost:6081" | grep -q 'Welcome to nginx!' 19 | echo "OK" 20 | 21 | docker compose down 22 | -------------------------------------------------------------------------------- /tests/wordpress/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | image: nginx:stable-alpine 4 | 5 | varnish: 6 | image: $IMAGE 7 | depends_on: 8 | - nginx 9 | environment: 10 | VARNISH_BACKEND_HOST: nginx 11 | VARNISH_CONFIG_PRESET: wordpress 12 | -------------------------------------------------------------------------------- /tests/wordpress/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ -n "${DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | docker compose up -d 10 | 11 | echo "Running check-ready action... " 12 | docker compose exec -T varnish make check-ready max_try=10 -f /usr/local/bin/actions.mk 13 | 14 | echo "Running flush action... " 15 | docker compose exec -T varnish make flush -f /usr/local/bin/actions.mk 16 | 17 | echo -n "Checking varnish backend response... " 18 | docker compose exec -T varnish curl -s "localhost:6081" | grep -q 'Welcome to nginx!' 19 | echo "OK" 20 | 21 | docker compose down 22 | --------------------------------------------------------------------------------