Skip to content
    Back to Blog

    Headless Drupal and Next.js: What We Learned Running One at 13,000 Requests a Second

    Dave Odey
    August 31, 2026
    7 min read
    Share:
    Headless Drupal and Next.js: What We Learned Running One at 13,000 Requests a Second

    We run a fan site for a waterpark that only exists inside a video game. Nobody is paying for it, it started as a joke, and it has taught us more about how a headless CMS behaves under real traffic than any client project we have shipped.

    The site is visitstankyshores.com. Drupal holds the content, a FastAPI service sits in the middle, Next.js renders the pages, and the whole thing had to survive the moment its creator reads the visitor reviews out loud to a live audience. Here is what we learned, including the part we got wrong in production.

    Why Drupal, in 2026

    Choosing Drupal for a new build invites a question, usually asked in the tone of someone being polite about your car. We did not choose it for the front end. We chose it for two things that are tedious to build and easy to underestimate.

    The first is moderation. Anyone who visits can file a review of the park, and every review waits for a human to approve it before it appears anywhere. Real workflow states, revisions, and an audit trail of who approved what is something Drupal has shipped for twenty years. Most modern stacks treat moderation as a feature you assemble yourself out of a boolean column and good intentions, and it fails quietly, usually the week you stop watching it.

    The second is structured content. An episode on this site is a number, an air date, a runtime, a source URL, and a set of relationships to the clips and stories that reference it. Modelling that as real fields means one record feeds the episode archive, the interactive park map, and the search index without anybody rewriting it three times. Store the same thing as a blob of HTML in a rich text field and every new surface turns into a parsing job.

    Neither reason has anything to do with how Drupal renders a page, which is exactly why going headless made sense.

    Where Next.js fits

    Next.js owns everything the visitor touches. Pages are server rendered, so a crawler and somebody on a phone both receive finished HTML instead of an empty div waiting on JavaScript to decide what the page says. For a site whose audience arrives almost entirely from Google, that is not a stylistic preference.

    It also gives us a proper component model for the pieces that genuinely are interactive: a park map you can drag and zoom, a staff ballot that updates without a reload, and a review form that posts and re-renders in place. Everything else on the site is effectively static, and it gets treated that way.

    Why we put a service layer in the middle

    The tempting shape is Next.js calling Drupal's JSON:API directly. We started there and moved off it.

    JSON:API is faithful to Drupal's data model, and that is the problem. Rendering one page meant several round trips with includes and filters strung together, and each one was a query against Drupal. A FastAPI service now sits between them. It gathers a page's worth of data in one call, shapes it into what the front end actually needs, and holds a short cache in front of the whole thing.

    That layer also became the natural home for logic that belongs in neither a CMS nor a rendering framework: rate limiting on the review form, spam checks, and the vote tally for the ballot. Keeping it separate means either side can change without negotiating with the other.

    The caching work that actually mattered

    The requirement we could not schedule was about a minute long. The park's creator reads the visitor reviews on stream sometimes, and his audience arrives all at once when he does.

    Load testing the honest way, against the origin rather than through a CDN, showed exactly where it would break. The home page managed 312 requests a second, and the slowest response in the run took 377 milliseconds. Worse, the staff ballot ran a per-visitor lookup against Drupal on every single page load, so page views and database queries scaled together. That is the shape of an outage.

    Two changes fixed it. An nginx page cache went in front of the application, configured to keep serving the cached copy while it refreshes in the background, so a slow origin never becomes a slow page for anyone waiting. And the per-visitor vote lookup moved out of Drupal into a small local store, so a page view stopped costing a database round trip.

    One detail here is worth stealing. Next.js sends a Vary header covering its own router hints, and if you let that reach the cache it fragments per browser and quietly destroys the hit rate you think you have. Normalising that header is what makes the cache work at all.

    The numbers

    After the change, the home page served 13,330 requests a second with the slowest response at 7 milliseconds. Before it, 312 a second with a 377 millisecond tail.

    The cached API endpoints tell a similar story. nginx answers roughly 9,300 requests a second from cache, while the application itself tops out somewhere between 500 and 700. That gap is the useful thing to know, because it says plainly what the cache is doing: standing between the audience and an application that was never going to absorb them.

    The stale asset problem nobody warns you about

    We broke production once, and it is worth writing down because the failure was invisible from the origin.

    We deployed twice within a few minutes. The second build replaced the first mid-flight, and for a short window the HTML being served referenced CSS files that were no longer on disk. Cloudflare cached those 404 responses. The origin returned 200s the entire time and looked perfectly healthy in every log we had, while actual visitors got an unstyled page.

    The fix is to give every build its own asset URLs. Next.js has a deploymentId setting, so the deploy script now feeds it the commit hash and every static URL carries that hash. A new build can never be handed an old build's cached answer. The rule we adopted alongside it is duller and did more good: one deploy at a time, and never a second while the first is still running.

    Letting the content file itself

    The archive updates without anyone touching it. Watchers poll YouTube for new episodes and Twitch for fan clips, then create Drupal entities directly, with the same fields and the same moderation states a human editor would produce.

    This is where the structured content decision pays off a second time. Because an episode is a set of fields rather than a page, a script can file one correctly and nobody has to check its formatting afterwards.

    When this stack is worth it, and when it is not

    Headless Drupal with Next.js is a real commitment. You deploy three systems instead of one, and a bug can live in any of them or in the gaps between.

    It earns that cost when your content is genuinely structured and feeds more than one surface, when you need editorial workflow and moderation you can actually trust, and when the pages have to be fast for anonymous visitors arriving from search.

    It is the wrong answer for a brochure site. If your content is a dozen pages that change a few times a year and one person edits them, this architecture is a tax that returns nothing. We would not sell it to a business that needs a good website with a phone number on it, and we say so when it comes up.

    We built this one as a joke for somebody else's audience. It now pulls around 1,600 clicks a month from Google at an average position of 5. The architecture is not why it found an audience. It is why the audience got a page that loaded.

    If you want the full build, screenshots and all, it is written up as a case study in our portfolio. If you are weighing a headless build for something real, our custom web development page covers how we approach it, and you are welcome to book a free site checkup and get an honest read on whether you need one.

    Share:

    Comments

    Want a second opinion on your website?

    We are a North Phoenix web studio building honest, fast websites for local businesses since 2007. Tell us what is not working and we will give you a free, no pressure site checkup.

    Get a Free Site Checkup