Most performance advice stops at the browser: compress the images, defer the JavaScript, trim the unused CSS. That advice is correct, and it has a ceiling, because every one of those fixes only begins to work after the server has answered. If the first byte arrives late, or arrives badly compressed, or arrives from a process that was asleep, no amount of front-end work gets that time back. This is what we found when we stopped tuning the pages and started measuring the machine underneath them — on our own three production domains, not on a test rig.
— Guide
What your server does to your PageSpeed score.
Front-end work has a ceiling and the server sets it. Here is what we found measuring our own three domains, with the byte counts.
The server checklist: what to check, and what a pass looks like
| Check | How to verify it | What a pass looks like |
|---|---|---|
| Compression is genuinely working | Fetch the same URL three times, accepting brotli, then gzip, then neither. Compare byte counts. | Brotli is comfortably the smallest of the three. A header alone proves nothing. |
| Time to First Byte | Measure from the start of navigation, not from your own server’s access log. | 0.8 s or less. Over 1.8 s is poor by Google’s threshold. |
| Response-time distribution | Request one URL a few hundred times and record every response, not an average. | p95 close to the median. Ten times the median means something underneath is restarting. |
| Cold starts and restarts | Check how long application processes have actually been alive, over a full day. | Process lifetime measured in hours, not minutes. |
| Process and memory ceiling | Read the plan’s account-wide limits, then count the processes actually running. | Real headroom after a deploy, not a number that creeps up each release. |
| Deploy hygiene | Count application processes immediately before and after a deploy. | The count returns to where it started. Anything else is a leak. |
| Cache headers at the edge | Request twice and read the response headers, not the configuration file. | The second response reports a cache hit and an age. |
| Static asset caching | Check what is served for fingerprinted, content-hashed files. | A long max-age with immutable, since the filename changes when the bytes do. |
| Redirect chains | Follow every entry form: http, https, www, non-www, trailing slash. | One hop at most to the canonical URL, and no port number leaking into it. |
| Crawler access | Fetch robots.txt and sitemap.xml with a crawler user agent, not a browser. | 200, correct content type, and no challenge page. |
| CDN or WAF security level | Test as a bot as well as a browser. Bots do not solve JavaScript challenges. | No challenge on any crawlable URL. |
| Protocol and TLS | Check which protocol is actually negotiated, per domain. | HTTP/2 or HTTP/3 with a modern TLS handshake, adding no extra round trips. |
Run these in order. The first four decide whether any page-level work can be measured reliably at all; the rest decide whether the result reaches real visitors and crawlers.
The same bytes, three ways
| Payload | Served by the platform as “brotli” | The same platform’s gzip | Brotli q11, done properly |
|---|---|---|---|
| Homepage HTML | 75,377 B | 54,284 B | 28,121 B |
| Main stylesheet | 29,799 B | 17,689 B | 15,300 B |
| Largest JavaScript chunk | 86,142 B | 61,088 B | 49,870 B |
Byte-identical payloads, SHA-256 verified, same server, same request. Measured on our own hosting in August 2026.
The server has no audit of its own
PageSpeed Insights has no “your hosting is slow” score. There is no category for it and no number that isolates it. The server appears indirectly, inside metrics that read like front-end problems: Time to First Byte feeds First Contentful Paint, which in turn gates Largest Contentful Paint, and Lighthouse only names it — as Reduce initial server response time — once it is already bad enough to be obvious.
Google treats a TTFB of 0.8 seconds or less as good and anything over 1.8 seconds as poor, measured from the moment navigation starts to the moment the first byte arrives. That window includes redirects, DNS, connection setup and TLS negotiation, not just your application’s thinking time — so a redirect chain and a slow certificate handshake are charged to you exactly like slow code is.
This is why the server layer gets skipped. It never files a complaint in its own name. It just quietly caps what every other fix can achieve.
Compression: we measured our platform’s “brotli” and it lost to its own gzip
Compression is the single highest-leverage server setting, and it is the one most often assumed to be handled. Ours was enabled. The response header said so. Everything looked correct.
Then we compared byte counts on identical payloads, and the platform’s brotli was producing larger files than the platform’s own gzip — 75,377 bytes against 54,284 for the same homepage HTML, as the compression table above shows. The cause was a streaming implementation using a very small compression window, so it never saw enough of the document at once to find the repetition that makes brotli worth using. Against a properly configured brotli it was shipping 2.7x the bytes.
A header that says a thing is on is not evidence that it is working. The test is to fetch the same URL three times — once accepting brotli, once accepting gzip, once accepting neither — and compare the byte counts yourself. If “brotli” is not comfortably the smallest of the three, it is not doing its job, whatever the header claims.
Fixing that one setting is what moved all three of our domains to a mobile score of 99 to 100, and it required no change to a single page.
The slowness that never shows up in a score at all
Here is the failure that convinced us this layer deserves its own article. The same URL, at the same byte size, was served in 20 to 60 ms almost always — and in 700 to 2000 ms occasionally. Run PageSpeed Insights and you would usually see the fast one. Run it again and you might see the slow one, and blame the difference on the tool.
It was not variance. The application process was being killed and restarted every ten minutes by a housekeeping script, and the next visitor after each restart paid for a full cold boot. At its worst, 11.7% of requests were landing on a 1.1 to 2.0 second cold start; after the fix that fell to 1.6%, and process lifetime went from ten minutes to over three and a half hours.
No front-end change addresses this, and no single PageSpeed run reveals it. It is a distribution problem, and a score is one sample. The only way to see it is to request the same URL a few hundred times, record every response time, and look at the slowest few percent rather than the average. If your p95 is ten times your median on a page that is served identically every time, the problem is underneath the application, not inside it.
On shared hosting, you are sharing more than disk
Shared and entry-level managed plans cap the number of processes an account may run, and that cap is usually account-wide rather than per-site. We hit it: every deploy left an old application process behind, each one holding memory and a process slot indefinitely, and they accumulated until the account sat near its ceiling. Sites nobody had touched in weeks got slower because of deploys to a different domain.
Two things follow from that. Read the process and memory limits of a plan before you read its price, because they decide whether a site degrades under its own maintenance. And treat a slowdown that started “out of nowhere” as a question about what else lives on the account, not only about what changed in the code.
The layer in front of the server can block Google outright
A CDN or web application firewall sits ahead of the application and answers some requests without ever consulting it. That is the point of it, and it is also a place where one dropdown does real damage.
On one of our properties, a CDN security level set to “medium” began answering the sitemap with a 403 and a browser-check challenge page. A browser passed that check without the visitor noticing anything. Googlebot does not run a JavaScript challenge in order to fetch a sitemap, so Search Console simply reported that it could not fetch the file, with zero pages discovered — and nothing in the site’s own code was wrong.
None of this appears in PageSpeed Insights, because PageSpeed asks for a page and gets one. It appears in Search Console, weeks later, as an indexing problem. The check worth running is to fetch your robots.txt and sitemap.xml with a crawler user agent and confirm both return 200 with the right content type.
Cache headers are a server decision, not a framework one
Your framework can propose caching behaviour; the web server, the CDN and the hosting panel can each override it. We have watched an application-level policy replaced wholesale by a rule further up the stack, with the application none the wiser. RFC 9111 defines what the directives mean, but not who gets the last word on your particular stack — that is a property of your configuration, and the only way to know it is to read the response headers at the edge rather than the code that intended to set them.
The rule we use is simple: for anything that claims to be cached, request it twice and compare. If the second response does not show a cache hit and an age, it is not cached, whatever the configuration file says.
And not only PageSpeed
Treating the score as the goal is what makes server work look optional, because a well-built page can score highly on a badly configured server most of the time. The costs land somewhere else.
Crawlers absorb a disproportionate share of the slow responses, because they request continuously rather than in the short bursts a human generates — so a fault that hits one request in nine is one that Google meets constantly, across thousands of URLs. Compression savings are collected on every request by every visitor and every bot, not once at build time. And the occasional two-second page is the one that decides whether somebody waits, which no median in any report will ever show you.
The score is a useful proxy and a poor target. What you are really buying with server configuration is the worst experience on your site, not the typical one.
How we scope this in practice
When we take on hosting and deployment work, the server pass comes before any page work, because page work is measured against whatever the server is doing and that measurement is worthless while the server moves underneath it. The checklist at the top of this article is the running order we use, and it is deliberately ordered: the first four items decide whether anything you measure afterwards can be trusted, and the rest decide whether the result actually reaches visitors and crawlers.
None of it requires access to the application. Every item is verifiable from outside with a handful of requests, which also makes it the fastest way to find out whether a slow site is a hosting problem or a code problem before anyone commits to a rebuild.
If you want that as a one-off rather than as an engagement, it is the first half of our Technical Audit. And if the page layer turns out to be what is holding you back, How to get a 100 PageSpeed score covers the other side of the line.
Sources
Thresholds and definitions below are Google’s and the IETF’s. The measurements in this article are our own, taken on our three production domains in August and September 2026.
- web.dev — Time to First Byte (TTFB) ↗
Defines the metric and its thresholds — 0.8 s or less is good, over 1.8 s is poor — and lists what the window includes: redirects, service worker startup, DNS, connection and TLS negotiation, and the request itself.
- Chrome for Developers — Reduce initial server response time ↗
The Lighthouse audit that surfaces server latency, and the threshold at which it starts reporting it.
- Chrome for Developers — Lighthouse performance scoring ↗
The metric weights behind the single number, which is what makes paint timing worth more than the long audit list PageSpeed prints underneath it.
- RFC 9111 — HTTP Caching ↗
What the cache directives mean. It defines the semantics, not which layer of your stack gets to set them last.
- RFC 7932 — Brotli Compressed Data Format ↗
The format specification, including the sliding window whose size decides how much repetition the compressor can find — the parameter at the root of the measurements in the table above.
— FAQ
Frequently asked questions
Fast page, slow server?
We will measure the layer underneath your site — compression, response-time distribution, headers and crawler access — and tell you what is capping it.