Troubleshooting

The pass says requests failed

Heat tallies the reasons and reports them — “What went wrong: cURL error 60: SSL certificate problem (×9)” — because the useful half is always why, and it is nearly always one reason repeated.

ReasonUsually
SSL certificate problemA self-signed certificate in local development. Turn off Verify TLS certificates for that environment only.
HTTP 404Craft’s site URL does not match how the site is actually reachable — check PRIMARY_SITE_URL.
HTTP 403Basic auth, an IP allowlist or a WAF in front of the site. Heat requests its own site over HTTP like any client, so anything that blocks a client blocks it.
Connection refused / timeoutThe site cannot reach itself — common in split-horizon DNS setups where the public hostname does not resolve from inside the network.
HTTP 503Craft is in maintenance mode, or has pending migrations.

“Could not sign the throwaway account in”

A site with two-factor authentication, a required custom user field, or a custom login flow will do this. The logged-in path is the expensive one, so it is worth setting up a way to measure it — but the rest of the pass completes and the projection is still useful, with the logged-in scenario missing rather than wrong.

Everything came back “served ahead of PHP”

Including the cache-busted requests. That means the cache in front is ignoring query strings — good for hit rate, and it means Heat could not measure what a page costs to render. The origin figures are not trustworthy in that state, and Heat says so.

Fix by allowing the heat-probe query parameter through to the origin, or measure from inside the network, behind the cache.

The numbers look far too slow

Check devMode first. With it on, Craft logs every query, recompiles Twig on each request and renders a debug toolbar — the same code can be several times slower. Heat says so at the top of both the pass and the report, and no capacity planning is worth doing until it is off.

After that: a laptop with a browser and twelve other containers open is not a quiet machine. Increase the sample count, or measure somewhere quieter.

“This host does not allow getrusage()”

Some shared hosts disable it. Without it, CPU cannot be measured, so everything that is not database time is reported as “blocked on something else” — which overstates how much of the load is waiting rather than working, and will point the remediation at outbound calls that may not exist. The ceiling arithmetic still holds; treat the CPU wall as unmeasured.

Heat says it had to infer the worker count

It could not read a pool configuration file — usually open_basedir, a non-standard layout, or a host that does not use FPM at all. The estimate is RAM ÷ memory_limit, and it is the number that moves the ceiling further than anything else in the model, so set it explicitly if you know it.

The ceiling changed and nothing else did

Compare two runs. A run stores the machine and the costs it was taken with, so the diff between two of them shows whether the code got heavier, the machine got smaller, or the cache stopped hitting. That is usually the fastest way to answer “what happened?”

Lite and Pro disagree about the ceiling

Expected. Lite models visitor traffic only; Pro adds editor saves and queue work, which are expensive. Lite therefore reports a higher ceiling on the same machine — it is modelling less load, not finding the load cheaper.

The projection says my site handles fewer visitors than it obviously does

Two usual causes. First, think time: if your visitors pause longer than the ten seconds Heat assumes, the same population generates fewer requests and the real ceiling in visitors is higher. Second, the mix: the launch mix deliberately models a cold cache, which is the worst realistic case rather than the typical one. Compare --mix=browsing against --mix=launch to see the span.