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:
- Backend application: Your web application (TYPO3, Shopware, etc.) connected to a virtual host
- Varnish container: Runs the Varnish caching server
- 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
- 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.
- 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. - After installation, note the application's short ID (visible in the app details)
Step 2: Create an internal virtual host
- In your project, click "Domains" in the sidebar
- Click "Add vHost"
- Use hostname:
backend.<project-short-id>.project.space - 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
- In your project, click "Containers" in the sidebar
- Click "Create Container"
- Select "varnish:8" as the image
- Configure:
- Name:
varnish - Port mapping:
80:80/tcp - Volumes: Mount
/files/varnishto/etc/varnish
- Name:
- Click "Create"
Step 5: Connect to your domain
- Click "Domains" in the sidebar
- Click "Add Virtual Host"
- Enter your public domain (e.g.,
www.example.com) - 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_usercookie) - 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-Invalidatesheader - 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-CapabilityandSurrogate-Controlheaders - Custom headers:
X-Shopware-Cache-Idfor 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:
-
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 -
Set environment variable:
SHOPWARE_HTTP_CACHE_ENABLED=1
Key VCL requirements from Shopware 6 documentation include:
- XKey module: Use
xkey.purge()for hard purge orxkey.softpurge()for soft purge - Surrogate keys: Shopware sends
Xkeyheaders for targeted invalidation - Cache-Control headers: Respect
s-maxagefrom 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 cachex-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
passmode for non-static content) - Verify your VCL logic isn't forcing
passmode for the specific URLs - Check backend response headers (e.g.,
Cache-Control: no-cachewill 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
Hostheader 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:
- Monitor memory usage
- Adjust container resources based on your traffic patterns
- 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
- TYPO3: mittwald TYPO3 Varnish Extension
- Shopware 5: Varnish Setup Guide
- Shopware 6: Reverse HTTP Cache Guide