Migrating from ModPageSpeed 2.0 to mod_pagespeed 2.1
What changes when you move a ModPageSpeed 2.0, mod_pagespeed 1.15, or open-source mod_pagespeed deployment to mod_pagespeed 2.1: your configuration carries over, the worker runs unprivileged, and the management API and browser sandbox get strict defaults.
On this page
mod_pagespeed 2.1 converges the mod_pagespeed lineage and the ModPageSpeed 2.0 engine into one product. If you run 2.0 today, the engine you rely on continues as the 2.1 optimizer worker. The migration upgrades the deployment around it rather than rebuilding it: your nginx directives and worker flags carry over. What to review is who the 2.1 worker runs as, how 2.1 exposes the management API and browser analysis by default, and (for Helm) the 2.1 pod security defaults.
Migration is manual — there is no automated path — but for most deployments it is an image bump plus a review of the defaults below.
Who this guide is for
This guide covers three starting points:
- ModPageSpeed 2.0 Docker Compose and Helm deployments (including the combined evaluation image) — see What changes in 2.1 below.
- mod_pagespeed 1.15 installs (the native Apache/nginx/IIS module) — see Upgrading from mod_pagespeed 1.15.
- The open-source mod_pagespeed project — see Coming from open-source mod_pagespeed.
It is not for users of the ASP.NET Core middleware: WeAmp.PageSpeed.AspNetCore
is the ASP.NET Core form of mod_pagespeed 2.1, and there is no migration step —
see ASP.NET Core performance for that path.
What changes in 2.1
The worker runs unprivileged
The 2.1 worker runs as the dedicated pagespeed user at a fixed UID/GID of
918, with an empty capability set and new privileges disabled. The shared
cache volume is owned by it: /data is group-readable (2750), cache files
and sockets are 0660, and the serving side reaches them through membership
in group pagespeed — the nginx image already joins it.
A volume created by 2.0 is migrated in place on first start: the
entrypoint adopts the cache files into the pagespeed user, logs each file it
takes over, and keeps your warm cache. If it finds content it cannot safely
adopt, it refuses before the worker starts, exits with status 78,
names the path, and prints the one-line remedy. Exit 78 always means the
configuration or the volume rather than a transient fault — worth
distinguishing in your restart alerting. Set PAGESPEED_ADOPT_VOLUME=off to
skip the automatic migration and fix ownership by hand instead.
Three setups need an explicit change:
- You run the worker with an explicit
user:/--user: use918:918, and make sure a bind-mounted data directory is owned by it. - Kubernetes: set
securityContext.fsGroup: 918. - A third container mounts the cache read-only: add
918to that container’s supplementary groups (group_add: ["918"]).
Management API: loopback and a token, by default
In 2.1 the container binds the management API to loopback and requires a
token; a non-loopback bind refuses to start unless you set both
PAGESPEED_API_ALLOW_REMOTE=true and a token. If you deliberately publish the
API, set both — and keep your reverse proxy’s own authentication in front of
it as well.
When the API is enabled and no token is supplied, the entrypoint generates one
per container start and prints it once to the container log, together with a
generated PURGE token so cache invalidation works out of the box. Both are
printed to stdout: if you ship container logs off-host, you are shipping those
credentials with them. Set PAGESPEED_API_TOKEN and PAGESPEED_PURGE_TOKEN
yourself to keep them out of the log and stable across restarts.
Browser analysis: the sandbox is required
Browser analysis in 2.1 requires the Chrome sandbox by default, and the
unprivileged worker satisfies that requirement — provided the container
runtime’s seccomp profile permits user namespaces. Where it does not, browser
analysis refuses to start and /v1/health reports
browser_sandbox: "unavailable" naming the cause; optimization keeps serving.
Run with a Chrome-compatible seccomp profile, or set
PAGESPEED_BROWSER_SANDBOX=off to accept an unsandboxed browser deliberately.
Helm: the pod seccomp profile
The 2.1 chart sets seccompProfile.type: RuntimeDefault on the pod. If a
workload of yours needs a syscall the runtime’s default profile blocks, opt
out with:
podSecurityContext:
seccompProfile: null
Helm deep-merges values, so setting the key to null is what removes it — an
empty podSecurityContext: {} will not override the chart default.
Native packages
2.1 also ships as native web-server packages: the module plus the
pagespeed-optimizer worker, installed and upgraded as a matching pair. On a
package install the worker keeps its cache in a versioned directory
(/var/cache/pagespeed-optimizer/v1), and a package upgrade starts that
cache cold by design. See downloads for packages and
platforms.
Configuration mapping
Your configuration carries over:
- nginx directives (
pagespeed on;,pagespeed_cache_path,pagespeed_cache_mode, the max-age family, and the rest of the configuration reference) are unchanged in 2.1. - Worker flags (
--cache-dir,--cache-size, image quality, proactive variants, browser analysis, and the rest) are unchanged in 2.1.
What to review is operational defaults, not directives:
| Area | 2.1 default | Act if |
|---|---|---|
| Management API | Loopback bind; token required | You reach the API from another host: set PAGESPEED_API_ALLOW_REMOTE=true and PAGESPEED_API_TOKEN |
| Purge endpoint | Token required (generated if unset) | You automate purges: set PAGESPEED_PURGE_TOKEN |
| Browser analysis | Sandbox required | Your runtime’s seccomp profile blocks user namespaces: allow them, or opt out deliberately |
| Helm pod | seccompProfile.type: RuntimeDefault | A workload needs blocked syscalls: set seccompProfile: null |
| Worker identity | pagespeed (UID/GID 918) | You set an explicit user:, bind-mount the cache, or share it with a third container |
| Package paths | /var/cache/pagespeed-optimizer/v1, /run/pagespeed-optimizer/notify.sock | Your web-server config points at explicitly configured worker paths: repoint both |
The reverse-proxy gap
One 2.0 deployment shape has no 2.1 equivalent yet: the any-origin caching reverse proxy — running the stack purely as an optimizing proxy in front of an origin that is otherwise untouched. Closing that gap is a committed part of the converged line’s roadmap. Until it is covered, that shape stays where it is, fully supported on 2.0, and this guide applies when the coverage lands.
Migration steps
- Upgrade to the 2.1 images or chart version — see Install with Docker and the Helm deployment guide for the current tags and values.
- Review the defaults table above and set the environment variables and values your deployment needs; remove license configuration.
- Watch the first start. The entrypoint logs each cache file it adopts. An exit status of 78 names the offending path and prints the remedy — it is a configuration or volume fault, not a transient one.
- Verify. Responses carry the
X-PageSpeedheader, and cached optimizations begin serving warm — the migrated volume keeps its contents. - Roll back if needed by returning to your previous 2.0 image tags or
chart version. On package installs, the module and
pagespeed-optimizermove together as a matching pair — roll both back together.
Upgrading from mod_pagespeed 1.15
The upgrade is drop-in. mod_pagespeed 2.1 keeps the same directives and the same filter names you run today — 1.14 and 1.15 configurations carry over — and installs from the same signed package repository. See downloads for packages, and the getting started guide for a fresh install.
Platform notes:
- IIS / Windows Server. The IIS package ships from the 1.15 packaging channel.
- Debian 11 (bullseye) — the repository’s bullseye suite carries the 1.15.0 module packages. The mod_pagespeed 2.1 packages need Debian 12 or newer.
- cPanel EasyApache 4 (EL8) — the module runs without the optimizer worker.
Coming from open-source mod_pagespeed
Moving from the archived open-source project? Is mod_pagespeed still maintained? covers what changed and what didn’t, and the maintained mod_pagespeed walks through what’s different day to day. Both existing directives and filter names keep working — see Getting started to install. The archived 1.0 docs are available at /1.0/.
Before you start
Check your current version:
# nginx
curl -I http://localhost/ | grep X-Page-Speed
# Apache
curl -I http://localhost/ | grep X-Mod-Pagespeed
Back up your current module in case you want to roll back:
# nginx
sudo cp /usr/lib/nginx/modules/ngx_pagespeed_module.so /usr/lib/nginx/modules/ngx_pagespeed_module.so.bak
# Apache
sudo cp /usr/lib/apache2/modules/mod_pagespeed.so /usr/lib/apache2/modules/mod_pagespeed.so.bak
Upgrade steps
nginx
On Debian 12/13 or Ubuntu 22.04/24.04 (amd64 + arm64), or AlmaLinux/RHEL/Rocky 9 (x86_64 + aarch64) and 10 (x86_64), install from the signed repository — it drops the module into the standard nginx modules directory and tracks upgrades through your package manager:
# 1. Configure the repository and import the signing key (one time)
curl -fsSL https://packages.modpagespeed.com/install.sh | sudo sh
# 2. Install the module
sudo apt install nginx-module-pagespeed # Debian / Ubuntu
sudo dnf install nginx-module-pagespeed # AlmaLinux/RHEL/Rocky 9 or 10
# 3. Restart nginx
sudo systemctl restart nginx
# 4. Verify
curl -I http://localhost/ | grep X-Page-Speed
The header value is the module version — its presence confirms the module is loaded and active.
Running an nginx version we don’t yet package? Each module is exact-version-pinned to its distro’s stock nginx — nginx refuses to load a module built for a different version — and the module source is not public, so there is no build-it-yourself path. Contact us for a matching pinned build.
Apache
# 1. Stop Apache
sudo systemctl stop apache2
# 2. Replace the module
sudo cp mod_pagespeed.so /usr/lib/apache2/modules/
# 3. Start Apache
sudo systemctl start apache2
# 4. Verify
curl -I http://localhost/ | grep X-Mod-Pagespeed
Cache migration
The native module uses Cyclone Cache, a new cache backend. On first start after the upgrade:
- The old file cache is ignored (not deleted)
- Cyclone Cache starts fresh with an empty cache
- Resources are re-optimized on first request — expect a brief warm-up period
The warm-up is a one-time cost: in v1.15.0+r17 and later, cache contents persist across restarts (see cache modes).
You can safely delete the old cache directory after confirming the upgrade works:
# Check your config for the cache path, then:
sudo rm -rf /var/cache/mod_pagespeed/ # or wherever your old cache lived
For cache sizing and storage options, see cache sizing.
Configuration compatibility
All existing directives are supported — the directive index lists the full set. A few notes:
| Directive | Status |
|---|---|
pagespeed on/off | Works as before |
pagespeed RewriteLevel | Works as before |
pagespeed EnableFilters | All 40+ filters available |
pagespeed DisableFilters | Works as before |
pagespeed Domain | Works as before |
pagespeed MapOriginDomain | Works as before |
pagespeed FileCachePath | Accepted — Cyclone Cache uses its own storage |
The FileCachePath directive is still accepted for compatibility but the native module uses Cyclone Cache for storage. You can remove it from your config if you prefer.
.htaccessconfiguration on Apache continues to work.- The
X-Mod-Pagespeed(Apache) /X-Page-Speed(nginx) response header is still emitted.
Rolling back
If you need to revert to the open-source version:
# Restore the backup you made earlier
sudo systemctl stop nginx
sudo cp /usr/lib/nginx/modules/ngx_pagespeed_module.so.bak /usr/lib/nginx/modules/ngx_pagespeed_module.so
sudo systemctl start nginx
Licensing
mod_pagespeed 2.1 is licensed under the Apache License 2.0, free in development and in production. What We-Amp sells on the converged line is support — and hardened, attested builds from the people who build the product; the standard signed packages stay free.
Frequently asked questions
Do I have to recreate my cache volume?
No. On its first start, 2.1 migrates a volume created by 2.0 in place: it adopts the cache files into the unprivileged pagespeed user and keeps the warm cache, deleting nothing. Native-package installs are different: there the cache lives in a versioned directory and a package upgrade starts it cold by design.
Do I have to migrate right away?
No — migrate on your own schedule. Migrating earlier moves you onto the converged line sooner.