# Speed Optimizations

> The operator's guide to making a WordPress site fast on CorePanel: WebP, Early Hints, minification and the dynamic page cache — how to turn each one on, how to verify it, and what to do when something looks wrong.

Source: https://www.corepanel.net/docs/wordpress/speed/
Last updated: 2026-08-13
Part of the CorePanel documentation — https://www.corepanel.net/docs

---

A WordPress site on CorePanel can be sped up at the web-server level, without a caching
plugin, a build step or a single change to the site's code. Four of the switches are
transformations of what the site returns; the fifth — the **dynamic page cache** — skips
building the page at all, and is the one that turns a server that dies at 20 visitors into
one that serves thousands.

This page is the operating manual: what to enable, in what order, how to prove it is
working, how to turn it off, and what each failure looks like from the panel. The
reference for each individual switch — and the benchmarks — lives in
[Web Optimizations](https://www.corepanel.net/docs/web/optimizations); how the plugin decides what may be cached is
in [the cache module](https://www.corepanel.net/docs/wordpress/security#the-cache-module).

| Switch | What it does | Editions |
|---|---|---|
| **Automatic WebP** | Serves images as WebP to browsers that accept it | All, including Personal |
| **Early Hints** | Announces a page's key assets while PHP is still building it | All, including Personal |
| **Minify JS / CSS** | Strips whitespace and comments on the way out | Speed Optimizer — Pro and above |
| **Dynamic page cache** | Answers a repeat visitor without running PHP at all | Speed Optimizer — Pro and above |

Everything here is **per site**. A hosting account with a primary domain, an addon domain
and two subdomains has four independent sets of switches, and nothing is inherited between
them: turning the cache on for `example.com` leaves `blog.example.com` exactly as it was.

## Before you start

Five things decide whether this will work at all. Check them once, at the beginning, and
most of the troubleshooting section below never applies to you.

1. **The edition.** WebP and Early Hints are in every edition. Minification and the page
   cache are the **Speed Optimizer**, which needs Pro or Business. On Personal those rows
   are shown locked in your panel, and are not rendered at all in a customer's.
2. **CorePanel must know the site is WordPress.** The page cache installs its companion
   plugin into the WordPress instances CorePanel manages. A site that was copied in by hand
   and never scanned is, as far as the cache is concerned, not WordPress — see
   [Adopting sites that are already there](https://www.corepanel.net/docs/wordpress#adopting-sites-that-are-already-there),
   or run `corepanel wp scan <account>`.
3. **Not on an alias.** An alias (parked) domain serves a copy of another domain's site,
   and WordPress invalidates by path — it never learns which hostname the visitor came
   through. CorePanel refuses to cache one. Enable it on the domain the alias points to;
   the other optimizations work on an alias normally.
4. **One page cache at a time.** If WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super
   Cache, WP Fastest Cache or Cache Enabler is active, CorePanel's companion stands down
   and declares nothing — two page caches fight over the same output buffer. Remove the
   plugin cache first.
5. **A site that is being edited is a site to leave alone.** The cache is safe during a
   redesign, but comparing before-and-after numbers while a theme changes underneath you
   proves nothing.

## Where the switches are

In the panel, open the account and go to **Optimizations**. The selector at the top right
picks which of the account's sites you are configuring — every domain, addon, alias and
subdomain is listed, each with its own settings.

![Mockup of CorePanel's Optimizations section for example.com: a site selector in the header, Automatic WebP forced On and Early Hints left on Default with Default/On/Off segmented controls, then a Speed Optimizer group with Minify JavaScript and Minify CSS switched on, and a Dynamic page cache control set to Evaluate showing the evaluation report — 83% of cacheable requests would be served from cache, 12% of all traffic could never be cached, and a breakdown listing state-cookie 402, no-contract 190 and set-cookie 82.](https://www.corepanel.net/_astro/wp-speed-optimizations-panel.WLhqAXAf.svg)

Customers with access to the [client panel](https://www.corepanel.net/docs/client-panel) get the same section for
their own sites, under **Websites**. Everything in this guide applies to them too.

Each switch takes effect on the next request — the site file is rewritten and the web
server reloads, with no downtime and no restart of anything the visitor can notice.

## Turning them on, in an order that stays boring

Enable them one at a time, in this order, and check the site after each. The order is not
arbitrary: it goes from the change least likely to be visible to the one that changes what
a visitor is actually served.

### 1. Automatic WebP

Set it to **On**. It is the largest single win on an image-heavy site, it costs nothing,
and it cannot break a page: browsers that do not accept WebP keep receiving the original
file, and the files in the document root are never touched.

```bash
corepanel domain optimizations set example.com --webp on
```

How much it saves is decided by what was uploaded. Flat graphics — screenshots, logos,
exports — are what PNG handles worst and WebP best; photographs that were already exported
sensibly give up far less. [The measured range is here](https://www.corepanel.net/docs/web/optimizations#webp).

### 2. Early Hints

Leave it on **Default** (which is on) unless you have a reason not to. It only affects
responses that take time to build — PHP-FPM and proxied backends — and lets the browser
start fetching the stylesheet while WordPress is still working.

### 3. Minify CSS, then JavaScript

Turn on **Minify CSS**, load the site, then turn on **Minify JavaScript** and load it
again. Two separate steps, because if something breaks you want to know which one did it.

```bash
corepanel domain optimizations set example.com --minify-css on
corepanel domain optimizations set example.com --minify-js on
```

Minification is generally safe, but it is a transformation of what your application
returns. Sites that depend on the exact bytes of a script — an integrity hash, an inline
source map — are the ones that notice.

### 4. Page cache: **Evaluate** first

Do not go straight to *On*. Set the cache to **Evaluate** and leave it there for a few
days of real traffic.

```bash
corepanel domain optimizations set example.com --page-cache shadow
```

In Evaluate the cache decides and stores **exactly** as it would in production, and serves
none of it. Visitors get the same site they got yesterday. What you gain is the only
honest answer to "is this worth it for this site", which cannot be guessed from the
outside: a news site and a membership site with identical traffic land in completely
different places.

This is also the step that installs the companion plugin — see
[what switching it on actually does](#what-switching-the-cache-on-actually-does) below.

### 5. Read the report, then switch it **On**

While a site is evaluating, the Optimizations section shows what the cache is finding, and
the CLI reports the same numbers for every site being measured:

```bash
corepanel cache stats                 # every site under evaluation, busiest first
corepanel cache stats example.com     # just this one
```

```
HOSTNAME       REQUESTS  HIT%   BYPASS%  TOP BYPASS REASON
example.com    5614      83.4   12.0     state-cookie (402)
```

Two numbers, kept deliberately apart:

- **HIT%** — of the traffic the cache *could* serve, how much it would have served from
  cache. This is the number that says whether switching it on is worth it.
- **BYPASS%** — how much of all traffic could never be cached at all. A shop where every
  visitor carries a cart is not a site with a bad cache; it is a site the cache cannot
  help.

A site above roughly 60% HIT% with a low bypass share will feel dramatically faster. A
site whose bypass share is most of its traffic will not, and no setting will change that —
[what the reasons mean](#reading-the-bypass-reasons) is below.

When the numbers say yes:

```bash
corepanel domain optimizations set example.com --page-cache on
```

Leaving Evaluate is clean: everything stored while evaluating is discarded rather than
served, so nothing built under rules you were still testing ever reaches a visitor. The
only cost is that those pages are generated once more.

> **Switching the cache on stops the measurement**
>
> The counters only run while a site is evaluating — the report exists to make this one
> decision, not to monitor the cache afterwards. They also reset when the web server
> restarts, so a figure never mixes two configurations.
## What switching the cache on actually does

Four things happen, and knowing them is most of the troubleshooting:

1. **The site file gains a cache configuration** and the web server reloads. The only
   caching policy CorePanel ever writes is *the application decides*: the engine stores a
   page when, and only when, WordPress declares that page shareable.
2. **The companion plugin is installed or refreshed** at
   `wp-content/mu-plugins/corepanel.php`, in every WordPress instance that belongs to
   *this* site. It is the same must-use plugin that already provides one-time login links
   and hardening; if the site had it, it is brought up to date.
3. **The site is told the cache is running**, per request, through two FastCGI parameters.
   Without them the plugin registers no hooks at all — which is why having it installed on
   every managed site costs nothing, and why enabling the cache later needs no visit to the
   site.
4. **Invalidation starts flowing back.** Publishing, editing or deleting a post drops its
   permalink, the front page, its archives, the feed and the sitemaps; a comment drops the
   page it lives on; a theme switch, a plugin activation or a permalink change drops
   everything. The signal travels on the very response that made the change, including the
   admin `POST` that is never itself cached.
5. **Updates drop everything too.** Updating a plugin or a theme — from the panel's
   WordPress section, or from the plugins list in wp-admin — empties that site's cache,
   and so does rolling the site back to a restore point. Updates run from the panel have
   no page load to carry the signal, so the panel empties the cache itself once the update
   succeeds.

   Updating **WordPress itself** is the one case where the signal cannot ride the update's
   own page — by then it has already started printing — so it is carried by the next screen
   you open in wp-admin, normally the very next click. An automatic update from cron has no
   page at all, so the site fetches one of its own and empties the cache within the run.

Pages are stored with a lifetime of up to a year, which is only safe because freshness
comes from that invalidation rather than from a timer. The plugin also caps a page's
lifetime whenever it mints a WordPress nonce, so a cached page can never hand out a
security token that has already expired.

> **The companion is a managed file**
>
> The panel rewrites it from scratch on every cache, hardening or login operation. Edits made
> by hand are lost at the next one; the supported extension points are
> [the filters](https://www.corepanel.net/docs/wordpress/security#the-cache-module), not the file.
## Verifying it is working

**While the site is evaluating**, the engine reports its verdict in a response header. This
is the only mode that emits it:

```bash
curl -sI 'https://example.com/' | grep -i x-px-cache
```

```
X-Px-Cache: would-hit
```

The values are `would-hit`, `would-miss` and `bypass:<reason>` — the same vocabulary the
report counts. Fetch a page twice: the first request is a `would-miss`, the second should
be a `would-hit`. A `bypass:` on a plain anonymous page view is the interesting case, and
[the reasons are below](#reading-the-bypass-reasons).

**With the cache On** there is no such header — a live site does not advertise its cache
state to visitors — so measure the thing you actually care about instead:

```bash
curl -s -o /dev/null -w 'ttfb %{time_starttransfer}s\n' 'https://example.com/'
curl -s -o /dev/null -w 'ttfb %{time_starttransfer}s\n' 'https://example.com/'
```

The second call should come back in single-digit milliseconds plus your network latency.
If both calls are equally slow, the page is not being served from cache; start at
[the cache is on and nothing got faster](#the-cache-is-on-and-nothing-got-faster).

**The safety check worth doing once**, on any site with logins: sign in, load a page you
know is cached, and confirm you see your admin bar and your own content. A logged-in
visitor is never served a cached page — this is enforced in the plugin *and* independently
in the engine, which refuses to store any response that sets a cookie.

## Day to day

Nothing. WordPress refreshes its own cached pages as content changes, so a site publishing
posts, taking comments and updating plugins keeps itself fresh without anyone touching the
panel.

What WordPress cannot signal is a change it never saw:

- a theme or template edited over SFTP or in the file manager,
- a database restored underneath the site,
- a page that was cached while something was already wrong.

For those, **Empty cache** in the Optimizations section drops every stored page for the
selected site. The button only appears while the cache is *On*. From the CLI:

```bash
corepanel cache purge example.com                # the whole site
corepanel cache purge blog.example.com / /feed   # just those pages
corepanel cache purge example.com "/2026/*"      # everything under a prefix
```

Two things worth knowing. **Every hostname of the site is emptied**, not just the one you
named — a page is stored once per hostname a visitor arrived through, so `example.com` and
`www.example.com` hold separate copies; the panel and the CLI both report the hostnames
they reached. And **emptying the cache is never blocked**, even on a server whose licence
has lapsed: being unable to clear a wrong page is not an acceptable way to be reminded of a
renewal.

Clearing the cache costs the next visitor to each page one normal, uncached page load.
There is no other side effect.

## Turning it off

| What you want | How |
|---|---|
| Stop serving cached pages for a site | Set the page cache to **Off** (`--page-cache off`). Effective on the next request; stored copies stop being reachable. |
| Stop caching **one** WordPress site from inside WordPress | `define( 'COREPANEL_CACHE_OFF', true );` in `wp-config.php`. Hardening and login links keep working. |
| Rule the companion plugin out entirely while diagnosing | `define( 'COREPANEL_MU_OFF', true );` in `wp-config.php`. This also disables hardening and one-time login links. |
| Keep a specific page out of the cache | `add_filter( 'corepanel_cache_public', '__return_false' );` on that page — see [the filters](https://www.corepanel.net/docs/wordpress/security#the-cache-module) |
| Undo minification | Switch **Minify CSS** / **Minify JavaScript** off |

If you turn the cache off and on again around a change, empty the cache afterwards. It
costs one uncached load per page and removes any doubt about what is stored.

> **A licence that lapses does not lose your settings**
>
> On a server that falls back to Personal, the stored modes stay in the database and simply
> stop being applied — the cache stops serving and minification stops running. Nothing you
> configured is thrown away, and it comes back when the licence does.
## Troubleshooting

| Symptom | Most likely cause | What to do |
|---|---|---|
| Cache is On, nothing got faster | The companion plugin is not answering | [See below](#the-cache-is-on-and-nothing-got-faster) |
| `no-contract` dominates the report | Same, or the traffic is admin/REST | [See below](#reading-the-bypass-reasons) |
| `state-cookie` dominates | Visitors are logged in or carrying a cart | Nothing to fix — the cache cannot help that traffic |
| `set-cookie` dominates | A plugin issues a cookie on every response | Identify it; the engine will never store those responses |
| `vary` dominates | A plugin emits `Vary: Cookie` indiscriminately | The report names the field; a per-visitor `Vary` cannot be cached |
| Old content after an SFTP or database change | WordPress never saw the change | **Empty cache** for that site |
| `www` still shows the old page | Each hostname stores its own copy | Purge reports the hostnames it reached; if one is missing it is a separate site |
| A form stopped submitting | A stale security token | [See below](#a-form-or-a-comment-stopped-working) |
| Site looks wrong after enabling minify | An asset that must not be rewritten | Turn Minify JS off, then CSS, to find which |
| The page cache row is missing | Personal edition, or the site is an alias | Pro or Business; enable it on the domain the alias points to |
| "The Speed Optimizer requires CorePanel Pro or Business" | The licence does not include it | Turning something **off** is always allowed, even unlicensed |
| The report is empty | No traffic yet, the server restarted, or the site is already On | Counters only run under **Evaluate** |

### The cache is on and nothing got faster

This is the failure this feature can produce that leaves no trace in any log, and it has
one cause with four flavours: **the companion plugin is not speaking**, so every page is
bypassed as `no-contract` and the cache stores nothing at all.

Work down the list:

1. **Is the site a managed WordPress instance?** Open **WordPress** in the panel and look
   for it. If it is not listed, the plugin was never installed there — run a scan
   (`corepanel wp scan <account>`) and enable the cache again.
2. **Is the instance attributed to this hostname?** The plugin goes into the WordPress
   under *this* site's document root. A WordPress living under a subdomain's docroot
   belongs to the subdomain, and must have the cache enabled on the subdomain.
3. **Is the file there?** Check `wp-content/mu-plugins/corepanel.php`. Reinstalling
   WordPress or a migration that skipped `mu-plugins` removes it. Any of these puts it
   back: re-applying the cache mode from the CLI, toggling the mode in the panel, applying
   hardening, or clicking **Log in**.
4. **Is something disabling it?** `COREPANEL_CACHE_OFF` or `COREPANEL_MU_OFF` in
   `wp-config.php`, or another page-cache plugin still active — the module stands down for
   both.

The quickest confirmation is Evaluate mode: set the cache to Evaluate, fetch a page twice
and read `X-Px-Cache`. A `bypass:no-contract` on an ordinary anonymous page view means the
plugin is not running; a `would-hit` means it is.

### Reading the bypass reasons

The report names why requests could not be cached, busiest first. The ones that come up:

| Reason | Meaning |
|---|---|
| `state-cookie` | The visitor is logged in, has a cart, or carries another state cookie. Normal, and the honest ceiling on a shop or a membership site. |
| `no-contract` | Nothing declared the response cacheable. On a WordPress site with the plugin running this is normal for wp-admin, REST, AJAX and XML-RPC traffic, which never reach the page path at all — so a *share* of `no-contract` is expected. A site where it dominates the front-end is [the case above](#the-cache-is-on-and-nothing-got-faster). |
| `set-cookie` | The response issued a cookie of its own. The engine never stores those, whatever the application says. |
| `vary` | The response declared it varies on something the cache cannot distinguish. The report breaks it down by field: a language or encoding header can be handled; `Cookie` cannot. |

The report lists these names on their own; the `X-Px-Cache` header prefixes them, so the
same finding reads as `bypass:no-contract` on the wire.

Reasons the plugin declares on purpose — searches, previews, password-protected posts,
visitors who have left a comment, 404s, cart, checkout and account pages — are working as
intended, not problems to solve.

### A form or a comment stopped working

WordPress security tokens expire, and a page cached for a long time could hand out a dead
one. CorePanel already caps a cached page's lifetime a minute short of the token inside it,
so this should not happen — but a plugin that mints its own token with its own lifetime is
outside that rule.

Empty the cache to confirm the diagnosis: if the form works right after a purge and fails
again later, that is what it is. Fix it by shortening the page's lifetime, or by keeping it
out of the cache:

```php
// This page: cache for an hour instead of the default.
add_filter( 'corepanel_cache_max_age', function () { return HOUR_IN_SECONDS; } );
```

### A visitor sees another visitor's page

Prices in the wrong currency, a greeting with somebody else's name, a members-only block on
an anonymous page. A plugin is personalising the page through a cookie, and CorePanel does
not know that cookie means "this visitor is different".

The session, comment, WooCommerce and Aelia currency cookies are already handled. For
anything else, name the cookie and the site stops sharing those pages at once:

```bash
corepanel domain optimizations set shop.example.com --state-cookies "my_currency"
```

Full detail — including why the list survives your other setting changes, and what it costs
— is in [Cookies that mean "this visitor is
different"](https://www.corepanel.net/docs/web/optimizations/#cookies-that-mean-this-visitor-is-different).

### A cookie consent banner

A banner that decides **in PHP** what the page contains turns one URL into two different
pages, and only one gets stored. The companion recognises **Cookie Notice & Compliance**
and **CookieYes in its legacy mode** and stops caching those sites outright, because the
alternative is serving tracking tags to visitors who refused them. **Complianz**, **Real
Cookie Banner** and **CookieYes** on its web app block in the browser and cache normally.

For any other banner, [the one-minute
test](https://www.corepanel.net/docs/wordpress/security#the-cache-module) fetches the page with and without the
consent cookie and diffs the two.

## What it will not do

- **It does not cache non-WordPress sites.** Without an application that can say what is
  shareable and which URLs went stale, the honest answer is not to cache. Enabling the
  mode on such a site is harmless — nothing is stored, nothing is served from cache — but
  it changes nothing either.
- **It does not cache alias domains**, for the reason in
  [Before you start](#before-you-start).
- **It does not shorten how long your content is wrong.** It shortens how long each page
  takes to serve. When you publish, the affected URLs are dropped immediately.
- **It is not a CDN.** Pages are served from your server, close to your server. What
  changes is that serving one costs about a millisecond of CPU instead of a quarter of a
  second.
- **A store or a membership site will see a smaller win** than a blog, because much more of
  its traffic is personal to each visitor. That is the cache working correctly.

## Next

- [Web Optimizations](https://www.corepanel.net/docs/web/optimizations) — the reference for each switch, and the
  measured numbers on a 2-vCPU server
- [Security and Hardening](https://www.corepanel.net/docs/wordpress/security#the-cache-module) — what the companion
  plugin declares, its filters and its escape hatches
- [Web Traffic](https://www.corepanel.net/docs/web-traffic) — what your server is actually serving, before and after
- [CLI reference](https://www.corepanel.net/docs/cli#domains) — `domain optimizations`, `subdomain optimizations`
  and `cache`
