Surviving the JS Ecosystem: Auth, Netlify, and the New Rules of Next.js 16

 · 4 min read

A Python backend is predictable and stable. Infrastructure on Google Cloud means strict rules and transparent logs. But as soon as we stepped into the territory of modern frontend to build an API key issuance portal for our SaaS, we found ourselves on the rollercoaster of the JavaScript ecosystem.

Surviving Js Ecosystem Clerk Nextjs

We just needed a single Next.js page where we could embed the <UserProfile /> component from Clerk. On paper, it looked like a two-hour task. In reality, we spent a day fighting hosting providers, outdated documentation, and implicit conventions.

Choosing a Host: Cloudflare, Vercel, or Netlify?

The first task was to find a home for our frontend (app.flude.guide). We were choosing between three options:

  1. Cloudflare Pages: This was our first candidate, as all our static sites (including the blog) were already running there successfully. But a SaaS app requires Server-Side Rendering (SSR). To run Next.js on Cloudflare, you need Workers and adapters like OpenNext. It turned out that the fresh Next.js 16 and the @clerk/nextjs library are strictly tied to the Node-specific async_hooks module, which simply doesn’t exist in the Cloudflare Workers runtime. We couldn’t do a static export (output: 'export') because Clerk requires a live server. This option was out.
  2. Vercel: The native harbor of Next.js. We could have launched on the free Hobby tier, but their Fair Use Guidelines are categorical: commercial use is prohibited. Even if the project isn’t making money yet, having paid developers on staff already violates the rules. The paid Pro tier for $20/month would be safe, but why pay for infrastructure for a page with near-zero traffic at launch?
  3. Netlify: Unlike Vercel, their free tier officially allows commercial use (confirmed on support forums). Next.js SSR is natively supported, and the Clerk + Netlify combo is battle-tested. We chose them.

The War with Netlify Scanners

Having chosen Netlify, we configured the pipeline and pushed the code to release. The build immediately crashed with a security error.

It turned out that Netlify’s built-in secrets scanner saw the string NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY in the configuration and blocked the deploy, deciding that we had committed a private token. We had to spend time digging through the documentation to manually add the SECRETS_SCAN_OMIT_KEYS exception to netlify.toml. We were literally arguing with the host that a public key is, in fact, public.

Surviving Js Ecosystem Clerk Nextjs Part 2

The Silent Break: Next.js 16 and File Renaming

Having solved the deployment issue, we moved on to authorization. According to the official Clerk documentation, route protection is configured in the middleware.ts file. We set everything up, pushed the code, but authorization simply didn’t work—the page let everyone in. No errors in the logs, just silence.

We killed several hours before we realized what was wrong. In Next.js 16, internal conventions had changed: the middleware file is now required to be named proxy.ts. As soon as we renamed the file, authorization worked. Because of one changed word in the framework’s convention, the security of the entire application silently disabled itself.

Surviving Js Ecosystem Clerk Nextjs Part 3

Sudden Deprecation: Chasing Standards

In that very proxy.ts file, we had configured protection for our /account page using a special Clerk function—createRouteMatcher. This was the recommended approach.

But the joy was short-lived. We accidentally discovered that this pattern of route protection at the middleware level was already being marked as deprecated. Clerk was shifting its philosophy towards “Resource-based auth”.

We had to urgently rip out all routing logic from proxy.ts, leaving only a “bare” context passthrough (export default clerkMiddleware();), and move the protection inside the server components. Now the /account/[[...rest]]/page.tsx page itself calls await auth.protect() before rendering. Yesterday’s “Best Practices” standard turned out to be legacy even before we could release the project.

What’s Next?

With much grief, we pushed the frontend through. The SaaS acquired working billing, secure cloud workers, and a portal for getting API keys. The architecture was ready.

But just as we relaxed, we decided to rename the base export format of our core from markdown to hugo_markdown. This harmless decision sent ripples across the entire distributed system, breaking contracts between the engine, workers, and clients. How we eliminated this “butterfly effect”—read about it in the next episode.