Skip to main content

Adding a caching proxy

Introduction

A caching reverse proxy like Varnish Cache sits between your web application and the internet, storing frequently accessed content in memory. This significantly reduces load on your application server and dramatically improves response times for cached content.

Benefits of using Varnish:

  • Performance: Serve cached content directly from memory, reducing response times from hundreds of milliseconds to just a few
  • Scalability: Handle significantly more concurrent users by reducing backend load
  • Flexibility: Fine-grained control over what gets cached and for how long using VCL (Varnish Configuration Language)
  • Cost efficiency: Reduce resource requirements for your backend application

This guide demonstrates how to deploy Varnish as a caching layer in front of web applications (TYPO3, Shopware 5/6) on the mittwald platform, with specific configuration examples for each platform.

Prerequisites

Before you begin, ensure you have:

  • A mittwald project with container support enabled
  • Basic familiarity with containerized applications
  • For Terraform deployments: The mittwald Terraform provider configured

Architecture overview

The setup consists of three main components:

  1. Backend application: Your web application (TYPO3, Shopware, etc.) connected to a virtual host
  2. Varnish container: Runs the Varnish caching server
  3. VCL configuration: Defines caching behavior and routes requests to the backend
Internet → Varnish Container (Port 80) → Backend Application

VCL Configuration
(Host header rewrite)

Deployment

This section covers the general deployment process for Varnish with a backend application. For application-specific VCL configurations, see the sections for TYPO3, Shopware 5, or Shopware 6 after completing the deployment.

Using Terraform

Step 1: Define your project and backend application

First, create your mittwald project and deploy your managed application (TYPO3, in this example):

terraform {
required_providers {
mittwald = {
source = "mittwald/mittwald"
version = "~> 1.4"
}
}
}

provider "mittwald" {
# Provider configuration
}

locals {
server_id = "your-server-id" # Replace with your server ID
public_domain = "www.example.com" # Replace with your actual domain
}

# Variable for TYPO3 admin password
variable "typo3_admin_password" {
type = string
sensitive = true
}

# Create the project
resource "mittwald_project" "varnish_demo" {
description = "TYPO3 with Varnish Cache"
server_id = local.server_id
}

# Look up the latest TYPO3 version
data "mittwald_app" "typo3" {
name = "typo3"
selector = "^13"
}

# Create backend application
resource "mittwald_app" "example" {
project_id = mittwald_project.varnish_demo.id

app = data.mittwald_app.typo3.name
version = data.mittwald_app.typo3.version
description = "TYPO3"
update_policy = "patchLevel"

user_inputs = {
"host" = "https://${local.public_domain}"
"installmode" = "composer"
"site_title" = "TYPO3 with Varnish"
"admin_user" = "admin"
"admin_pass" = var.typo3_admin_password
}
}

# Connect the TYPO3 app to an internal virtual host
# This is required so Varnish can route requests to it
resource "mittwald_virtualhost" "example_backend" {
hostname = "backend.${mittwald_project.varnish_demo.short_id}.project.space"
project_id = mittwald_project.varnish_demo.id

paths = {
"/" = {
app = mittwald_app.example.id
}
}
}

Step 2: Create the VCL configuration file

Create a file named default.vcl in your Terraform working directory. Use the official VCL configuration for your application (see Application-Specific VCL Configurations below) with one critical addition for the mittwald platform:

vcl 4.1;

backend default {
.host = "${backend.host}";
.port = "${backend.port}";
}

# Copy the complete VCL configuration for your application:
# - TYPO3: https://github.com/mittwald/typo3-varnishcache/blob/main/Resources/Private/Example/default-4.vcl
# - Shopware 5: https://developers.shopware.com/sysadmins-guide/varnish-setup/
# - Shopware 6: https://developer.shopware.com/docs/guides/hosting/infrastructure/reverse-http-cache.html

sub vcl_recv {
# Preserve the original Host header
set req.http.X-Forwarded-Host = req.http.Host;

# ... other vcl_recv rules from your application's VCL ...
}

sub vcl_backend_fetch {
# IMPORTANT: This Host header rewrite is required for the mittwald platform
# The backend app must be reachable via its internal virtual host
set bereq.http.Host = "${backend.external_host}";
}

# ... other subroutines from your application's VCL (vcl_backend_response, vcl_deliver, etc.) ...

Step 3: Upload the VCL configuration and deploy Varnish

Complete your Terraform configuration with the VCL file upload and Varnish container:

# Upload the VCL configuration file
resource "mittwald_remote_file" "varnish_vcl" {
app_id = mittwald_app.example.id

path = "/files/varnish/default.vcl"
contents = templatefile("${path.module}/default.vcl", {
backend = {
host = mittwald_app.example.short_id
port = 8080
external_host = mittwald_virtualhost.example_backend.hostname
}
})
}

# Look up the Varnish container image
data "mittwald_container_image" "varnish" {
image = "varnish:8"
}

# Deploy the Varnish container
resource "mittwald_container_stack" "varnish" {
project_id = mittwald_project.varnish_demo.id
default_stack = true

# Ensure VCL file is uploaded before starting container
depends_on = [mittwald_remote_file.varnish_vcl]

containers = {
varnish = {
description = "Varnish caching server"
image = data.mittwald_container_image.varnish.image
entrypoint = data.mittwald_container_image.varnish.entrypoint
command = data.mittwald_container_image.varnish.command

ports = [
{
container_port = 80
public_port = 80
protocol = "tcp"
}
]

volumes = [
{
mount_path = "/etc/varnish"
project_path = "/files/varnish"
}
]
}
}
}

# Connect Varnish to your public domain
resource "mittwald_virtualhost" "public" {
hostname = local.public_domain
project_id = mittwald_project.varnish_demo.id

paths = {
"/" = {
container = {
container_id = mittwald_container_stack.varnish.containers["varnish"].id
port = "80/tcp"
}
}
}
}

Step 4: Apply the configuration

$ terraform init
$ terraform plan
$ terraform apply -var="typo3_admin_password=YOUR_SECURE_PASSWORD"

You can also store the password in a .tfvars file or use environment variables:

$ export TF_VAR_typo3_admin_password="YOUR_SECURE_PASSWORD"
$ terraform apply

After applying, your TYPO3 application will be accessible through Varnish at your configured domain.

Using the CLI

Step 1: Create the project and TYPO3 application

# Create a new project
$ mw project create --description "Managed application with Varnish" --server <server-id>

# Create TYPO3 application
$ mw app install typo3 \
--version 13.4.3 \
--install-mode composer \
--admin-user admin \
--admin-pass "#supersecr3t!" \
--site-title "TYPO3 example"
--host "https://www.example.com" \
--description "TYPO3 example"

In the mw app install command, use your public domain (e.g., https://www.example.com) as the base URL, not the backend hostname. Note the application ID from the output, you'll need it in the following steps.

Step 2: Connect TYPO3 to an internal virtual host

# Get your project's short ID
$ mw project get <project-id>

# Create internal virtual host for the backend
$ mw domain virtualhost create \
--hostname backend.<project-short-id>.project.space \
--path-to-app /:<app-id>

Step 3: Create and upload the VCL configuration

Create a file named default.vcl locally. Start with the official VCL configuration for your application (see Application-Specific VCL Configurations below) and add the Host header rewriting required for the mittwald platform:

vcl 4.1;

backend default {
.host = "a-XXXXX"; # Replace with your app's short ID
.port = "8080";
}

# Copy the complete VCL configuration for your application:
# - TYPO3: https://github.com/mittwald/typo3-varnishcache/blob/main/Resources/Private/Example/default-4.vcl
# - Shopware 5: https://developers.shopware.com/sysadmins-guide/varnish-setup/
# - Shopware 6: https://developer.shopware.com/docs/guides/hosting/infrastructure/reverse-http-cache.html

sub vcl_recv {
set req.http.X-Forwarded-Host = req.http.Host;

# ... other vcl_recv rules from your application's VCL ...
}

sub vcl_backend_fetch {
# IMPORTANT: Replace with your backend's internal virtual host
# This Host header rewrite is required for the mittwald platform
set bereq.http.Host = "backend.<project-short-id>.project.space";
}

# ... other subroutines from your application's VCL ...

Upload the VCL file to /files/varnish/default.vcl in your app.

Step 4: Deploy the Varnish container

$ mw container run \
--name varnish \
--publish-all \
--volume /files/varnish:/etc/varnish \
varnish:8

Step 5: Connect Varnish to your public domain

# Get the container ID
$ mw container list

# Create the virtual host
$ mw domain virtualhost create \
--hostname www.example.com \
--path-to-container /:<container-id>:80/tcp

Using the mStudio UI

Step 1: Create the backend application

  1. Install your required backend application (e.g., TYPO3) in your project using the mStudio UI. Alternatively, create a custom PHP application and deploy it yourself.
  2. When installing an application via the mStudio, use your public domain (e.g., https://www.example.com) as the base URL in your application configuration, not the backend hostname.
  3. After installation, note the application's short ID (visible in the app details)

Step 2: Create an internal virtual host

  1. In your project, click "Domains" in the sidebar
  2. Click "Add vHost"
  3. Use hostname: backend.<project-short-id>.project.space
  4. Connect the virtual host with the backend app.

Step 3: Prepare the VCL configuration

Download the exampel VCL configuration for your application (see Application-Specific VCL Configurations below) and modify it to add Host header rewriting in the vcl_backend_fetch subroutine (see the CLI section above for the specific changes needed).

Upload it to /files/varnish/default.vcl in your app using the file manager or SFTP.

Step 4: Create the Varnish container

  1. In your project, click "Containers" in the sidebar
  2. Click "Create Container"
  3. Select "varnish:8" as the image
  4. Configure:
    • Name: varnish
    • Port mapping: 80:80/tcp
    • Volumes: Mount /files/varnish to /etc/varnish
  5. Click "Create"

Step 5: Connect to your domain

  1. Click "Domains" in the sidebar
  2. Click "Add Virtual Host"
  3. Enter your public domain (e.g., www.example.com)
  4. Point it to the Varnish container on port 80

Application-Specific VCL Configurations

The VCL examples in the deployment section above show the basic structure with the critical Host header rewriting required for the mittwald platform. This section provides application-specific guidance for TYPO3, Shopware 5, and Shopware 6.

TYPO3 Configuration

The mittwald TYPO3 Varnish extension provides a complete VCL example that includes:

  • BAN support for cache invalidation (automatically triggered by the TYPO3 extension)
  • Cookie handling that bypasses cache for TYPO3 backend users (be_typo_user cookie)
  • Static file caching with optimized TTLs for images, CSS, JS, etc.
  • ESI (Edge Side Includes) processing support
  • Streaming for media files
  • Retry logic for server errors (5xx responses)

Install the mittwald TYPO3 Varnish extension in your TYPO3 installation. It automatically sends BAN requests to Varnish whenever a TYPO3 page cache is flushed (either manually, or by editing a page), keeping your cache in sync with your content.

Since TYPO3 will be accessed via the Varnish proxy, you will need to configure your TYPO3 instance to be aware of the reverse proxy; for this, set the [SYS][reverseProxyIP] and [SYS][reverseProxyHeaderMultiValue] values in config/system/settings.php or config/system/additional.php:

$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'] = '*';
$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue'] = 'first';

Shopware 5 Configuration

Shopware 5 uses a custom cache invalidation mechanism. Use the official Shopware 5 Varnish configuration as your base VCL.

Key features to include from the Shopware 5 VCL:

  • Cache invalidation: PURGE and BAN methods using X-Shopware-Invalidates header
  • Cookie normalization: Shop, currency, and cache-context-hash cookies for improved cache hit rates
  • URL normalization: Strip UTM and tracking parameters from URLs
  • ESI support: Enable with Surrogate-Capability and Surrogate-Control headers
  • Custom headers: X-Shopware-Cache-Id for identifying cacheable content

When using Varnish with Shopware 5, make sure to follow the official configuration guide closely. Especially, remember to disable the built-in HTTP cache in Shopware 5 to avoid conflicts with Varnish, and set the trustedProxies configuration:

'trustedProxies' => ['100.64.0.0/10'],

mittwald-specific addition: In your vcl_backend_fetch subroutine, add the Host header rewriting:

sub vcl_backend_fetch {
# Standard Shopware 5 backend fetch logic here...
# (from the official Shopware 5 VCL)

# IMPORTANT: Add this for mittwald platform
set bereq.http.Host = "backend.<project-short-id>.project.space";
}

Cache warming: Shopware 5 supports cache warming. Configure your shop to send warm-up requests to the internal backend hostname if needed.

Shopware 6 Configuration

Shopware 6 requires Varnish 6.0 or later with the XKey module for surrogate key-based cache invalidation.

To configure Shopware 6 for reverse proxy caching, follow these steps:

  1. Enable reverse proxy in your Shopware configuration (config/packages/shopware.yaml):

    shopware:
    http_cache:
    reverse_proxy:
    enabled: true
    use_varnish_xkey: true
    hosts: ["http://varnish"]
    max_parallel_invalidations: 3
  2. Set environment variable: SHOPWARE_HTTP_CACHE_ENABLED=1

Key VCL requirements from Shopware 6 documentation include:

  • XKey module: Use xkey.purge() for hard purge or xkey.softpurge() for soft purge
  • Surrogate keys: Shopware sends Xkey headers for targeted invalidation
  • Cache-Control headers: Respect s-maxage from Shopware responses

mittwald-specific VCL structure:

vcl 4.1;

import xkey;

backend default {
.host = "a-XXXXX"; # Your Shopware app's short ID
.port = "8080";
}

# Copy the complete Shopware 6 VCL from the official documentation
# https://developer.shopware.com/docs/guides/hosting/infrastructure/reverse-http-cache.html

sub vcl_backend_fetch {
# Standard Shopware 6 backend fetch logic...

# IMPORTANT: Add this for mittwald platform
set bereq.http.Host = "backend.<project-short-id>.project.space";
}

To invalidate the cache, use Shopware's built-in cache clearing:

  • bin/console cache:clear:http — Clears reverse proxy cache only (recommended)
  • Shopware automatically invalidates cache when content changes in the admin

Monitoring and debugging

Check if caching is working

Most VCL configurations add an x-cache header to responses using a vcl_deliver rule that should look like this:

sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.x-cache = "HIT";
} else {
set resp.http.x-cache = "MISS";
}
}

When this is in place, you can check the response headers to see if content is being served from cache:

$ curl -I https://www.example.com
HTTP/1.1 200 OK
x-cache: HIT
  • x-cache: HIT = Content served from cache
  • x-cache: MISS = Content fetched from backend

View Varnish logs

This command will print the standard output of Varnish itself:

$ mw container logs <container-id>

To get the actual (per-request) logs, use varnishlog:

$ mw container exec <container-id> -- varnishlog

Varnish statistics

Connect to the container and use varnishstat:

$ mw container exec <container-id> -- varnishstat

Common issues

Problem: All requests show x-cache: MISS

  • Check if requests have cookies (cookies trigger pass mode for non-static content)
  • Verify your VCL logic isn't forcing pass mode for the specific URLs
  • Check backend response headers (e.g., Cache-Control: no-cache will prevent caching)
  • For TYPO3: Ensure you're not accessing backend URLs (/typo3/) or logged in with a backend user cookie

Problem: Backend application returns 404, 403 or doesn't respond

  • Verify the internal virtual host is correctly configured
  • Check the Host header is being rewritten correctly in VCL
  • Ensure the backend hostname in VCL matches your app's virtual host

Problem: Container won't start

  • Check VCL syntax: Run varnishd -C -f /etc/varnish/default.vcl (if the container does not start, run this command in a separate container with an overwritten entrypoint or in a container on your local machine)
  • Verify the VCL file was uploaded to the correct path
  • Check container logs for error messages

Performance considerations

Cache hit ratio

A good cache hit ratio is typically above 80%. Monitor this with:

$ mw container exec <container-id> 'varnishstat -1' | grep 'cache_\(hit\|miss\)\s'

Memory sizing

Varnish stores cached content in memory. For production use, you may want to:

  1. Monitor memory usage
  2. Adjust container resources based on your traffic patterns
  3. Consider implementing size limits in VCL

Grace mode

The example VCL enables "grace mode" which serves stale content if the backend is unavailable. This improves reliability but means users might see outdated content during backend outages.

Further reading

General Varnish Documentation

Application-Specific Documentation

mittwald Platform Documentation