Appearance
Production Varnish bfcache change
This procedure is prepared but has not been executed in production. Run it on the production Varnish edge, not on mzo-webserver. The backend is confirmed as 10.0.6.215:8080; mzo-webserver owns that private address and Nginx listens on port 8080 there.
The production VCL must continue to use this backend:
vcl
backend default {
.host = "10.0.6.215";
.port = "8080";
.first_byte_timeout = 600s;Do not copy the staging or dev backend into production. Do not change the probe or PURGE ACL as part of this deployment.
Exact VCL change
In vcl_deliver, replace this complete block:
vcl
# Not letting browser to cache non-static files.
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
}with:
vcl
# Allow public HTML in the browser back/forward cache. Private HTML keeps no-store.
if (req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
if (resp.http.Cache-Control ~ "public") {
set resp.http.Cache-Control = "no-cache, must-revalidate, max-age=0";
} else {
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
}
}This removes no-store only from public HTML. Cart, account, checkout and every other response not marked public retain no-store.
Backup and prepare
The commands below discover the -f file from the running varnishd process. They stop if no readable VCL is found or if the production backend does not match.
bash
LIVE_VCL="$(sudo ps -ww -eo args= | awk '
/[v]arnishd/ {
for (i = 1; i <= NF; i++) {
if ($i == "-f") { print $(i + 1); exit }
if ($i ~ /^-f\//) { sub(/^-f/, "", $i); print $i; exit }
}
}
')"
test -n "${LIVE_VCL}" && sudo test -r "${LIVE_VCL}" || {
echo "STOP: the live VCL path was not detected"
exit 1
}
sudo grep -q '10\.0\.6\.215' "${LIVE_VCL}" && \
sudo grep -q '\.port = "8080"' "${LIVE_VCL}" || {
echo "STOP: this is not the confirmed production VCL"
exit 1
}
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
BACKUP_DIR="/var/backups/varnish/${STAMP}"
CANDIDATE="/tmp/default-bfcache-${STAMP}.vcl"
OLD_ACTIVE="$(sudo varnishadm vcl.list | awk '$1 == "active" { print $NF; exit }')"
sudo install -d -m 0750 "${BACKUP_DIR}"
sudo cp -a "${LIVE_VCL}" "${BACKUP_DIR}/default.vcl"
sudo sha256sum "${BACKUP_DIR}/default.vcl" | sudo tee "${BACKUP_DIR}/default.vcl.sha256"
sudo cp -a "${LIVE_VCL}" "${CANDIDATE}"
printf 'live=%s\nbackup=%s\nold_active=%s\n' \
"${LIVE_VCL}" "${BACKUP_DIR}/default.vcl" "${OLD_ACTIVE}" | \
sudo tee "${BACKUP_DIR}/deployment.txt"Apply the exact replacement to the candidate. The command aborts unless the old block occurs exactly once.
bash
sudo python3 - "${CANDIDATE}" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text()
old = ''' # Not letting browser to cache non-static files.
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
}
'''
new = ''' # Allow public HTML in the browser back/forward cache. Private HTML keeps no-store.
if (req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
if (resp.http.Cache-Control ~ "public") {
set resp.http.Cache-Control = "no-cache, must-revalidate, max-age=0";
} else {
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
}
}
'''
if text.count(old) != 1:
raise SystemExit("STOP: expected production block was not found exactly once")
path.write_text(text.replace(old, new))
PY
sudo diff -u "${LIVE_VCL}" "${CANDIDATE}"The diff must contain only the cache-policy block above.
Verify and activate
Compile with the production Varnish binary before changing the active VCL. A non-zero exit code stops the deployment.
bash
sudo varnishd -C -f "${CANDIDATE}" >/dev/null
VCL_NAME="bfcache_${STAMP}"
sudo varnishadm vcl.load "${VCL_NAME}" "${CANDIDATE}"
sudo varnishadm vcl.use "${VCL_NAME}"
sudo varnishadm vcl.list
sudo varnishadm backend.listCheck the public homepage and a private route:
bash
curl -sS -D - -o /dev/null https://www.menzzo.fr/ | \
grep -Ei '^(HTTP/|cache-control:|age:|x-cache-hits:|x-magento-cache-debug:)'
curl -sS -D - -o /dev/null https://www.menzzo.fr/checkout/cart/ | \
grep -Ei '^(HTTP/|location:|cache-control:)'Acceptance requires all of the following:
backend.listreports the backend as healthy.- The homepage remains successful and its public response no longer contains
no-store. - The cart response still contains
no-store. - No new Varnish or Magento error appears during the check.
Persist the validated candidate only after those checks pass:
bash
sudo cp -a "${CANDIDATE}" "${LIVE_VCL}"
sudo varnishd -C -f "${LIVE_VCL}" >/dev/nullNo Varnish restart is required: vcl.load and vcl.use activate the change in memory, while the final copy makes it survive the next service restart.
Rollback
Use the backup directory printed during deployment. This compiles and activates the saved VCL, then restores the file used at the next restart.
bash
BACKUP_DIR=/var/backups/varnish/YYYYMMDDTHHMMSSZ
LIVE_VCL="$(sudo awk -F= '$1 == "live" { print $2 }' "${BACKUP_DIR}/deployment.txt")"
ROLLBACK_NAME="rollback_$(date -u +%Y%m%dT%H%M%SZ)"
sudo varnishd -C -f "${BACKUP_DIR}/default.vcl" >/dev/null
sudo varnishadm vcl.load "${ROLLBACK_NAME}" "${BACKUP_DIR}/default.vcl"
sudo varnishadm vcl.use "${ROLLBACK_NAME}"
sudo cp -a "${BACKUP_DIR}/default.vcl" "${LIVE_VCL}"
sudo varnishadm vcl.list
sudo varnishadm backend.listRepeat the homepage and cart header checks. The homepage should return to its previous no-store policy, and the backend must remain healthy.