"We went headless for performance." I hear this constantly. And half the time, when I actually look at the implementation, it's slower than the WordPress site it replaced.
Headless architecture can be faster. It often is. But the assumption that decoupling automatically improves performance is one of the more persistent myths in modern web development.
The TTFB Problem
Time to First Byte is where many headless implementations fail immediately.
A well-cached WordPress site can serve pages in under 100ms. The database query is cached, the HTML is cached, the CDN delivers it from the edge. Done.
A poorly implemented headless site? The browser makes a request. The frontend server receives it. The server makes an API call to the CMS. The CMS queries the database. The response travels back through the middleware. The frontend renders. The HTML is finally sent.
Each hop adds latency. Without aggressive caching at every layer, you've built a slower system while calling it modern.
Over-Fetching and Middleware Abuse
GraphQL was supposed to solve over-fetching. In practice, I've seen GraphQL implementations that fetch entire content trees to display a single page. The query is flexible, yes. It's also enormous.
Middleware layers compound this. A common pattern: frontend calls a BFF (Backend for Frontend), which calls an API gateway, which calls the CMS, which queries the database. Four network hops for what could be a single cached response.
Each layer was added for a good reason — separation of concerns, authentication, transformation. But the cumulative effect is a system that's architecturally clean and practically slow.
Cache Misuse
Caching is the answer to most headless performance problems. It's also the thing most teams get wrong.
Common mistakes:
- Caching at the CDN level but not invalidating on content changes
- Caching API responses but not the rendered HTML
- Cache keys that are too specific, resulting in low hit rates
- No cache warming, so the first visitor after deployment always gets a slow response
- Stale-while-revalidate not configured, forcing synchronous cache rebuilds
The irony is that WordPress with a simple page cache plugin often outperforms a headless site with a sophisticated caching strategy that wasn't properly implemented.
When Traditional Outperforms Modern
There are scenarios where a traditional CMS genuinely performs better:
Simple content sites. If you're building a marketing site with ten pages and a blog, WordPress with good hosting will be faster to build, easier to maintain, and perform well enough that nobody notices the difference.
Database-heavy operations. Traditional CMSs have spent years optimising database queries. That work doesn't transfer when you move to a headless setup with a new data layer.
Server-side rendering without optimisation. SSR in frameworks like Nuxt or Next is powerful, but it requires configuration. Out of the box, you might be rendering every page on every request. That's slower than serving cached HTML.
When Headless Actually Wins
Headless performance advantages are real when:
- Static generation is possible (ISR, SSG) and content doesn't change every minute
- Edge deployment means the frontend runs close to users globally
- API responses are cached aggressively with proper invalidation
- The frontend is optimised — code splitting, lazy loading, minimal JavaScript
- The architecture was designed for performance, not just for separation
Notice the pattern: none of this is automatic. Each point requires intentional engineering.
The Honest Benchmark
Before claiming headless is faster, measure it. Against the real alternative, not a strawman.
Compare your headless build to a properly optimised traditional CMS. Same content. Same hosting budget. Same caching strategy applied to both.
Often, headless wins. Sometimes, it doesn't. And knowing which scenario you're in matters more than following the trend.