Invisible Pages
When designing a high-traffic site, the temptation of SPAs is strong.
No server request on every page transition. JavaScript rewrites the DOM, and the HTML rendering cost disappears. Just return data from a REST API. At tens of millions of page views a month, that difference matters.
But SPAs have a shadow.
Googlebot has a Chromium-based renderer. But there's a delay between crawling and rendering. If async data fetching doesn't finish in time, the page gets recorded as empty. Pages that users can see are invisible to the search engine. The larger the site, the more it depends on search traffic. What SPAs are good at and what SEO demands collide head-on.
There's another concern: the REST API is visible from the outside. SPAs fetch data by hitting APIs. Open DevTools and every endpoint is exposed. Authentication and rate limiting help, but the structure of the endpoints and their parameters can't be hidden. The risk of data being extracted in unintended ways is always there. With SSR, you embed the data in HTML and return it. No need to expose an API externally. SSR gets scraped too, of course. But parsing HTML and hitting structured JSON are different levels of effort for an attacker.
There's a middle ground. SSR for the initial load, SPA behavior after that. The Next.js and Nuxt approach. But hybrid has its own complexity. Syncing state between server and client, and dealing with hydration mismatches — a whole different category of bugs.
And the moment you decide to expose a REST API, the next swamp opens up. How do you handle authentication? OAuth, OIDC, token management. As APIs multiply, you want to split services. Split services and you need microservice architecture. Inter-service auth, data consistency, failure propagation. Choosing SPA looks like a frontend architecture decision. It's an invitation that drags in the entire backend.
In the end, you're just choosing what to give up. Pick SPA and your SEO and security surface area grows, with a microservices swamp waiting beyond. Pick SSR and you fight scalability. Caching strategies help, but the architecture stays simpler. Pick hybrid and you take on the complexity of both.
No matter what you pick, you get hit somewhere. At least pick where.