A roofing client called us in March because their Vercel bill had jumped to $412 in February. We pulled the analytics. The site had served roughly 18,000 visits in the month. Most of those were the same six pages — services, about, contact, three location pages. None of them had changed since the build in 2024. We were paying enterprise-tier infrastructure prices to serve HTML that was, in any meaningful sense, frozen.
We moved the site to Hugo on Cloudflare Pages over the next weekend. Total monthly cost now: zero. The site is also faster.
This is becoming a pattern.
What we kept telling ourselves
For about three years our default for any new client site was Next.js, server-rendered, hosted on Vercel or a similar Node host. The justifications sounded reasonable in each individual case. We wanted incremental static regeneration so the client could push a blog post and see it live in a minute. We wanted server components so we could query a small CMS at request time. We wanted preview deployments. We wanted the ecosystem.
What we actually got, for a typical small-business site:
- A blog the client touched twice a year.
- A CMS query that returned the same data on every request.
- Preview deployments nobody ever opened.
- A $40-to-$400 monthly hosting bill scaling with traffic, plus a build pipeline that broke once or twice a year when an upstream package decided to break.
For a marketing site that changes once a quarter, this was the wrong shape of infrastructure. We were buying a Mercedes for trips to the mailbox.
The stack we're using now
Most of our new small-business builds run on Hugo and deploy to Cloudflare Pages. The pipeline is:
- Hugo generates the site to static HTML at build time. Build times for typical sites are well under five seconds.
- Pushes to the main branch on GitHub trigger a Cloudflare Pages build.
- The output is served from Cloudflare's edge, free, with a generous bandwidth allowance.
- The contact form posts to a Cloudflare Worker that hands off to Resend for email and writes a copy to a private form-submissions repo.
- Search, if needed, is Pagefind — a static search index generated at build time, no server required.
Total monthly cost for a typical site: zero to a few dollars. We charge the client a flat managed-hosting fee anyway, because they want one bill from us, but the infrastructure underneath is free.
The objections, and what we actually found
"Clients want to edit the site themselves." Most don't. The ones who do edit twice a year and email us about anything that doesn't go right. For the few who genuinely want self-serve editing, we run Decap CMS or pair Hugo with a small Notion-backed pipeline. Neither requires SSR.
"You'll lose SEO without server-side rendering." No. Static HTML is server-side rendered HTML. Googlebot doesn't care whether your HTML was generated three milliseconds ago by Node or three days ago by Hugo. It cares whether the HTML in the response contains the content. Static wins on Core Web Vitals because the response is faster and lighter.
"What about personalization?" A small-business roofing site does not need to greet a returning visitor by name. If it did, we'd add a small island of client-side JavaScript that hits a worker for the personalized fragment, and the rest of the page would still be static. Treating the whole page as dynamic to support one personalized element was always overkill.
"Hugo's templating is painful." It's fine. It's not React. The first time we used it after a long stretch of JSX we cursed at it for an hour. By day three it was faster to work in than the JSX project sitting in the other tab. Templating is templating.
Where SSR still earns its keep
We're not rebuilding everything static. Sites with logged-in user areas, real-time dashboards, dynamic product catalogs of any size, e-commerce with stock-aware pricing — those still need a server. So do clients on a CMS like Sanity or Contentful where the editorial cadence is daily and the team genuinely uses preview workflows.
What we stopped doing is reaching for SSR by default. The default should be the simplest thing that meets the brief. For a five-page marketing site, that's a static generator and an edge host. We had to relearn this the expensive way, with several clients paying for infrastructure they didn't need.
What this looks like on the back end of the agency
Quieter on-call. Sites that were static almost never page us. We've had two outage tickets on static-hosted client sites in the last year, both because Cloudflare itself had a regional incident. SSR sites paged us a dozen times in the same period — usually a build failure on a deploy, occasionally a runtime memory issue, once a Node version upgrade we hadn't tested.
Backups are easy. The site is a git repository. git clone is the backup.
Handoffs are easy. A new developer joining the team can read a Hugo project end to end in an afternoon. The Next.js project with three abstraction layers and a custom data loader took the same developer a week.
If you build sites for small businesses and your default is still a Node-rendered framework on a paid host, the question worth asking is whether the brief actually requires it, or whether it requires the simplest version of itself. The roofing client's bill answered that question for us pretty clearly.