How to connect authentication, database, and payments on Webflow Cloud (Supabase, Auth0, and Stripe)

Learn how to deploy authenticated, payment-ready Next.js apps entirely within Webflow's infrastructure.

How to connect authentication, database, and payments on Webflow Cloud (Supabase, Auth0, and Stripe)

Colin Lateano
Developer Evangelist
View author profile
Colin Lateano
Developer Evangelist
View author profile
Table of contents

Webflow Cloud handles Supabase cleanly out of the box. Auth0 and Stripe each need one non-obvious fix. Learn the complete setup for all three: what each service requires, where the default SDK behavior breaks on Cloudflare Workers, and how to configure them before your first deploy.

For years, the most common pattern in Webflow was a split-stack compromise: Webflow handled the design, and a separate Next.js app sat on Vercel handling the logic, with the two communicating via an API or an iframe.

It worked, but it meant two deployment pipelines, two sets of environment secrets, and a constant negotiation at the API boundary between what Webflow rendered and what the backend served.

Webflow Cloud changes that calculus by running your Astro application directly on Webflow's infrastructure (backed by Cloudflare Workers), so the frontend and the full-stack app live under the same roof.

The catch? Cloudflare Workers runs on the V8 isolate runtime, not Node.js. That means the default HTTP clients used by most SDKs (Stripe especially) break silently when you try to deploy them without the right configuration.

This guide covers the complete setup: scaffolding a Webflow Cloud project; wiring in Supabase for your database layer, Auth0 for authentication, and Stripe for payments; and getting all three running cleanly on the edge runtime before you deploy.

What do you need to build a full-stack app on Webflow Cloud?

Five things need to be in place before you write any integration code: a Webflow workspace with Cloud access, Node.js 18+ with npm, the Webflow CLI, and active accounts with Supabase, Auth0, and Stripe. Plan for 45–60 minutes if you're setting up fresh accounts across all three services.

Here's what each requirement actually entails.

A Webflow workspace with Cloud access

Webflow Cloud is available on paid workspace plans. You'll need access to the Cloud section in your Webflow dashboard. If you don't see "Cloud" in the left sidebar when you're in your workspace, check your plan tier.

Node.js 18+ and npm

Webflow Cloud only supports npm; not Yarn, pnpm or bun. This matters because the wrangler.jsonc config and the build pipeline assume npm. If your project was initialized with another package manager, convert it before you init a Cloud project. Run node -v and npm -v to confirm both are available.

Also, you need Next.js 15+. Webflow Cloud only supports Next.js 15 or Astro. If your project is on Next.js 14 or earlier, upgrade before running webflow cloud init. Run next --version to confirm.

Webflow CLI global installation

The CLI handles authentication with Webflow's platform and the deploy commands.

Install it with:

npm install -g @webflow/webflow-cli

Confirm it's installed with webflow --version.

A Supabase project with a database

Create a project at supabase.com.

You'll need the project URL and the publishable (anon) key from the API settings. Supabase's JavaScript client uses the Fetch API under the hood, which means it works in Cloudflare Workers without any special configuration. This is one of the few third-party integrations that just works.

An Auth0 application configured as a Regular Web Application

In your Auth0 dashboard, create an application and set the type to "Regular Web Application."

Note your domain, client ID, and client secret. You'll also generate a 32-byte hex secret used to sign sessions: openssl rand -hex 32 produces the right format. Auth0's v4 Next.js SDK (@auth0/nextjs-auth0) is edge-compatible by default; the v3 /edge import path no longer exists in v4.

A Stripe account with a webhook endpoint

You'll need a publishable key and secret key from your Stripe dashboard.

For local testing, the Stripe CLI generates a webhook signing secret that you'll add to your environment. For production, you'll configure the webhook endpoint in the Stripe dashboard pointing to your deployed domain.

Once these are in place, the full integration takes about six steps. Here's how.

6 steps to build a full-stack app on Webflow Cloud with Supabase, Auth0, and Stripe

The six steps below cover the complete integration: scaffolding the project and config files, declaring the edge runtime for each API route, connecting Supabase, Auth0, and Stripe, and deploying.

Steps 1 and 2 are foundational. Get them wrong, and the later integrations won't behave as expected. I'll flag the specific gotchas for each service as we go.

1. Initialize your Webflow Cloud project and verify the config files

Before you touch any integration code, the Webflow Cloud scaffolding needs to be correct. Every configuration mistake I've seen in client projects traced back to a malformed webflow.json, a missing open-next.config.ts, or environment variables set in .env.local instead of the Webflow Cloud dashboard.

Getting the scaffold right first means everything downstream deploys correctly on the first attempt.

Start by authenticating with the Webflow CLI and initializing your Cloud project:

webflow auth login
webflow cloud init

Here:

  • webflow auth login opens a browser window to authenticate your CLI session with your CLI session with your Webflow workspace
  • webflow cloud init runs an interactive setup that creates the required configuration files and links your local project to a Webflow Cloud project in your dashboard

If you're working in an existing Next.js project, run webflow cloud init from the project root.

After initialization, confirm these four files exist and match exactly.

webflow.json

The framework selector that tells Webflow Cloud which runtime adapter to use:

{
  "cloud": {
    "framework": "nextjs"
  }
}

If the file doesn't exist after webflow cloud init, create it manually at the project root. This is the only Webflow-specific config file. Everything else is standard Next.js or Cloudflare.

wrangler.jsonc

This is the Cloudflare Workers configuration. The nodejs_compat flag is what enables the subset of Node.js APIs available in the edge runtime.

Without it, even basic operations like Buffer will throw:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "nextjs",
  "main": ".open-next/worker.js",
  "compatibility_date": "2025-03-01",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "binding": "ASSETS",
    "directory": ".open-next/assets"
  },
  "observability": { "enabled": true }
}

This file is for local development only. Wrangler reads it when you run npm run preview, which simulates the Cloudflare Workers environment locally.

open-next.config.ts

This is the OpenNext Cloudflare adapter configuration.

For most projects, this is a single export:

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({});

Most projects don't need to modify this file beyond the default export. It becomes relevant when you add advanced OpenNext features like ISR or custom cache adapters, but for this stack it stays as-is.

cloudflare-env.d.ts

Here are the TypeScript type definitions for Cloudflare bindings.

This starts empty, but you'll add entries here when you add D1, KV, or R2 storage later:

interface CloudflareEnv {}

One critical environment variable behavior to understand before you proceed: on Webflow Cloud, environment variables are runtime-only. They are not available during the build step.

In a standard Next.js deployment, NEXT_PUBLIC_variables are baked into the client bundle at build time, but on Webflow Cloud, environment variables aren't available during the build step, so this inlining cannot happen.

On Webflow Cloud, you configure all variables in your project's Settings in the Webflow dashboard, and they're injected at request time. This means you cannot use environment variables in next.config.ts or in any code that runs during next build. Keep all secrets and config values inside route handlers and API functions.

Once the config files are in place and you've confirmed the structure, install the OpenNext adapter if it wasn't added automatically:

npm install @opennextjs/cloudflare

Then add a preview script to package.json for local testing before deployment:

{
  "scripts": {
    "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview"
  }
}

npm run preview runs a local Cloudflare Workers simulation via Wrangler, which is much closer to the production environment than npm run dev. I run preview before every deploy. It catches edge runtime incompatibilities that next dev silently swallows.

2. Declare the edge runtime on every API route

Every API route that interacts with Supabase, Auth0, or Stripe must explicitly declare the Cloudflare Workers edge runtime. Without this declaration, Next.js defaults to a Node.js runtime assumption, and the build may succeed while the deployed worker silently fails on incompatible module imports.

Add this export to the top of every file in app/api/:

export const runtime = 'edge';

This is a one-liner, but it changes how the Next.js compiler processes the file. With runtime = 'edge' declared, the compiler applies edge bundling. It strips Node.js polyfills and expects only Web API-compatible code.

Any import that would pull in a Node.js-only module (http, https, net, child_process) will now throw a build error immediately rather than failing silently at runtime. This is the behavior you want: fail fast, fail loudly, during build rather than after deploy.

In my experience, there is one important nuance about route segment config: the edge runtime declaration applies only to that route file, not to shared utilities or libraries it imports.

If you're using a shared database client defined in lib/supabase.ts, that file doesn't need the declaration, but the API route that imports it does. The rule is to declare runtime = 'edge' at the route level and ensure everything imported from that route is edge-compatible.

The full pattern for an API route in this stack looks like this:

// app/api/user/route.ts
export const runtime = 'edge';

import { createClient } from '@supabase/supabase-js';
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  // Route handler logic here
}

3. Connect Supabase as your database layer

Supabase is the cleanest integration in this stack for Webflow Cloud. The @supabase/supabase-js client uses the Fetch API for all network requests, which means it works in Cloudflare Workers without any special configuration, unlike Stripe, which requires a specific HTTP client swap.

The main gotcha with Supabase is the environment variable naming: the publishable key is now called SUPABASE_PUBLISHABLE_KEY (not SUPABASE_ANON_KEY, which was the older naming convention).

Using the old variable name doesn't immediately break anything; it just silently initializes the client without a key, which produces confusing 401 errors at query time.

So, install the Supabase client:

npm install @supabase/supabase-js

Then, create a shared Supabase client at lib/supabase.ts.

Create a shared Supabase client

Because environment variables are runtime-only on Webflow Cloud, this client must be instantiated inside a function (not at module initialization time) to ensure the environment values are available when the function executes:

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';

export function getSupabaseClient() {
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;

  if (!supabaseUrl || !supabaseKey) {
    throw new Error('Missing Supabase environment variables');
  }

  return createClient(supabaseUrl, supabaseKey);
}

Why wrap in a function? In Next.js on Webflow Cloud, top-level process.env reads at the module scope can return undefined because environment variables are injected at request time, not at module load time. Wrapping the client in a function ensures the values are read at call time, when they're guaranteed to be available.

Add the following variables in your Webflow Cloud project's Settings → Variables:

NEXT_PUBLIC_SUPABASE_URL=https://[your-project-ref].supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=[your-anon-key]

Retrieve both values from your Supabase dashboard under Project Settings → API. The "Project URL" maps to NEXT_PUBLIC_SUPABASE_URL, and the anon / public key maps to NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY.

With the client in place, here's a complete edge-runtime API route that queries a Supabase table:

// app/api/products/route.ts
export const runtime = 'edge';

import { getSupabaseClient } from '@/lib/supabase';
import { NextResponse } from 'next/server';

export async function GET() {
  const supabase = getSupabaseClient();

  const { data, error } = await supabase
    .from('products')
    .select('id, name, price, stripe_price_id')
    .order('created_at', { ascending: false });

  if (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }

  return NextResponse.json(data);
}

The data response is typed automatically if you've generated Supabase TypeScript types using supabase gen types typescript.

I strongly recommend doing this as it surfaces schema mismatches at compile time rather than at runtime, and it's one of the biggest quality-of-life improvements when working on a Webflow Cloud project where you can't easily hot-reload a debugging session.

Row Level Security note: If your query returns no rows and no error, Row Level Security (RLS) is almost certainly the cause. For a public product catalog, you'll need a SELECT policy that allows the anon role to read. For user-specific data, you'll pass a JWT in the Authorization header.

4. Add Auth0 authentication with the v4 Next.js SDK

Auth0's v4 SDK (@auth0/nextjs-auth0) represents a significant architectural change from v3, and the migration catches many developers off guard, especially those following older tutorials.

In v3, you created a catch-all route at app/api/auth/[auth0]/route.ts.

However, in v4, there is no route handler. Authentication is handled entirely through the Next.js middleware layer, and the routes are mounted automatically at /auth/* (not /api/auth/*). If you're upgrading from v3 or following an older tutorial, this is the first thing to get right. The authentication routes silently 404 if you're using the v3 pattern with v4 installed.

Here’s how to install the SDK:

npm install @auth0/nextjs-auth0

Create the Auth0 client at lib/auth0.ts. This is a shared instance used across your middleware and server components:

// lib/auth0.ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client();

The Auth0Client automatically reads all required configuration from environment variables.

Createmiddleware.ts

Create middleware.ts in your project root (or src/middleware.ts if you're using a src/ directory):

// middleware.ts
import type { NextRequest } from "next/server";
import { auth0 } from "./lib/auth0";

export async function middleware(request: NextRequest) {
  return await auth0.middleware(request);
}

export const config = {
  matcher: [
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};

The matcher pattern is not optional. It ensures the Auth0 middleware runs on every request except static assets. Omitting it or narrowing it too aggressively causes session rolling to fail, resulting in users being logged out unexpectedly during long sessions.

Add environment variables

Add all five required environment variables in your Webflow Cloud project's Settings → Variables:

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
AUTH0_SECRET=your-64-char-hex-secret
APP_BASE_URL=https://your-deployed-domain.com

Generate AUTH0_SECRET with openssl rand -hex 32. This must be exactly 64 hexadecimal characters (32 bytes). In your Auth0 dashboard, set the Allowed Callback URL to ${APP_BASE_URL}/auth/callback and the Allowed Logout URL to ${APP_BASE_URL}.

For local development, add these to .env.local with APP_BASE_URL=http://localhost:3000. Do not commit .env.local to source control.

Also, update your Auth0 application settings in the dashboard for local development. Under your application's Settings tab, add http://localhost:3000/auth/callback to Allowed Callback URLs and http://localhost:3000 to Allowed Logout URLs.

Auth0 validates the full callback URL on every login attempt. Without this, local dev throws a callback URL mismatch error that looks like an SDK misconfiguration rather than a dashboard configuration issue.

With middleware in place, accessing user data in a server component uses getSession():

// app/dashboard/page.tsx
import { auth0 } from "@/lib/auth0";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const session = await auth0.getSession();

  if (!session) {
    redirect("/auth/login");
  }

  const { user } = session;

  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <p>Email: {user.email}</p>
      <a href="/auth/logout">Log out</a>
    </div>
  );
}

For protecting API routes, check the session inside the route handler:

// app/api/protected/route.ts
export const runtime = 'edge';

import { auth0 } from "@/lib/auth0";
import { NextResponse } from "next/server";

export async function GET() {
  const session = await auth0.getSession();

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json({ user: session.user });
}

The v4 SDK is edge-compatible by default. There is no longer a separate @auth0/nextjs-auth0/edge import path. If you see that import anywhere in your codebase, remove it; importing from that path in v4 throws a module not found error.

5. Integrate Stripe payments for the edge runtime

Stripe is the trickiest integration in this stack, and the failure mode is subtle: the default Stripe SDK uses Node.js's node:https module for HTTP requests. That module is not available in Cloudflare Workers, even with the nodejs_compat flag.

The SDK initializes without throwing an error, but the first actual API call (creating a checkout session, fetching a product, verifying a webhook) fails with a runtime networking error that doesn't clearly identify Stripe as the source.

The fix is a single configuration line that swaps the HTTP client from Node's https to the Fetch API. This is what makes Stripe work on Webflow Cloud.

Here’s how to install both packages:

npm install stripe @stripe/stripe-js

Then, create a shared Stripe client at lib/stripe.ts:

// lib/stripe.ts
import Stripe from "stripe";

export function getStripeClient() {
  const secretKey = process.env.STRIPE_SECRET_KEY;

  if (!secretKey) {
    throw new Error("Missing STRIPE_SECRET_KEY environment variable");
  }

  return new Stripe(secretKey, {
    // Required for Cloudflare Workers: swap Node.js https for the Fetch API
    httpClient: Stripe.createFetchHttpClient(),
  });
}

The httpClient: Stripe.createFetchHttpClient() option is the critical line. Without it, Stripe will try to use node:https, which is unavailable in the V8 isolate runtime, and your API routes will fail at the first Stripe call with a network error.

With it, Stripe uses the globally available fetch function, which Cloudflare Workers provides natively.

Add these variables to your Webflow Cloud project's Settings → Variables:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...

Use pk_test_ / sk_test_ for your development environment, and pk_live_ / sk_live_ for production. Keep your secret key and webhook secret out of any client-side code and out of version control.

One important distinction: the STRIPE_WEBHOOK_SECRET generated by stripe listen during local development is different from the webhook endpoint secret in your Stripe dashboard.

Both start with whsec_ but they are not interchangeable. Use the CLI-generated secret in .env.local for local testing, and replace it with the dashboard endpoint secret in your Webflow Cloud environment variables before deploying.

Complete Stripe Checkout session

Here's a complete Stripe Checkout session creation route, edge-compatible:

// app/api/checkout/route.ts
export const runtime = 'edge';

import { getStripeClient } from "@/lib/stripe";
import { auth0 } from "@/lib/auth0";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const session = await auth0.getSession();

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { priceId } = await request.json();
  const stripe = getStripeClient();

  const checkoutSession = await stripe.checkout.sessions.create({
    mode: "payment",
    payment_method_types: ["card"],
    line_items: [
      {
        price: priceId,
        quantity: 1,
      },
    ],
    customer_email: session.user.email,
    success_url: `${process.env.APP_BASE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.APP_BASE_URL}/pricing`,
  });

  return NextResponse.json({ url: checkoutSession.url });
}

Webhook verification in the edge runtime requires one additional consideration. Stripe's constructEvent method for webhook signature verification uses the crypto module. The nodejs_compat flag makes crypto available in Webflow Cloud's edge runtime, so webhook verification works, but only if you're passing the raw request body as a string or Uint8Array, not a parsed JSON object.

You should parse the raw body before verifying:

// app/api/webhooks/stripe/route.ts
export const runtime = 'edge';

import { getStripeClient } from "@/lib/stripe";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const body = await request.text();
  const signature = request.headers.get("stripe-signature");

  if (!signature) {
    return NextResponse.json({ error: "Missing signature" }, { status: 400 });
  }

  const stripe = getStripeClient();

  let event;
  try {
    event = await stripe.webhooks.constructEventAsync(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
  }

  // Handle the event
  switch (event.type) {
    case "checkout.session.completed":
      const completedSession = event.data.object;
      // Fulfill the order in Supabase
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  return NextResponse.json({ received: true });
}

Note the use of stripe.webhooks.constructEventAsync rather than constructEvent. The async version uses crypto.subtle (the Web Crypto API) for signature verification, which is available in the edge runtime. The synchronous constructEvent uses Node's native crypto module and will fail on Webflow Cloud.

6. Deploy to Webflow Cloud and configure production environment variables

Once all three integrations are working locally via npm run preview, deploy to production with a single CLI command:

webflow cloud deploy

This triggers a full build and deploys the compiled worker to Webflow Cloud. The CLI outputs a deployment URL when the process completes. The first deploy takes slightly longer because the build artifacts need to be uploaded and distributed. Subsequent deploys are faster because only changed assets are re-uploaded.

Before deploying, verify that every environment variable is set in your Webflow Cloud project's Settings → Variables — not in .env.local, which only applies to local development and is not read by the Webflow Cloud build process.

Here are the variables you need for this full stack:

# Supabase
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY

# Auth0
AUTH0_DOMAIN
AUTH0_CLIENT_ID
AUTH0_CLIENT_SECRET
AUTH0_SECRET
APP_BASE_URL

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET

After deploying, update your Auth0 application settings to add the production domain to Allowed Callback URLs and Allowed Logout URLs. Update your Stripe webhook endpoint to point to https://your-deployed-domain.com/api/webhooks/stripe.

Both of these steps are easy to miss. Missing them causes authentication and payment confirmation to fail in production, but they work perfectly locally.

To monitor your deployed application, Webflow Cloud includes built-in observability (enabled via "observability": { "enabled": true } in wrangler.jsonc). Logs are accessible from the Webflow dashboard.

For structured error tracking, you can addSentry as a Cloudflare Worker integration, but the platform's built-in basic observability is sufficient for initial debugging.

What causes full-stack Webflow Cloud apps to fail?

Most failures in Webflow Cloud full-stack projects fall into four categories: edge runtime incompatibilities caused by Node.js-dependent libraries; environment variable misconfigurations specific to the Webflow Cloud runtime model; Auth0 routing regressions from following v3 patterns with v4 installed; and Stripe webhook verification failures due to incorrect body parsing.

Here's the full diagnostic for each.

Stripe API calls fail with a network error after deploy, even thoughnpm run devworks

Cause: The Stripe SDK is initialized without httpClient: Stripe.createFetchHttpClient(), so it attempts to use Node.js's node:https module. That module is unavailable in Cloudflare Workers (the V8 isolate runtime Webflow Cloud runs on), even with the nodejs_compat flag.

The SDK initializes silently, so you don't see any errors at startup. The failure only surfaces on the first actual API call, which makes it look like a credentials or network issue rather than a configuration issue.

Fix: Initialize Stripe with the fetch-based HTTP client as shown in Step 5. Every Stripe client instance needs this option. If you have multiple places in your codebase that initialize new Stripe(key), they all need the httpClient override. The shared lib/stripe.ts pattern (using a factory function) ensures this is applied consistently.

Auth0 authentication routes return 404 in production

Cause: This is almost always caused by either (a) using the v3 catch-all route handler pattern (app/api/auth/[auth0]/route.ts) with v4 installed, or (b) having the wrong middleware matcher that excludes the /auth/* routes.

In v4, there is no route handler. The middleware mounts auth routes. If the middleware.ts file doesn't exist or its matcher excludes /auth/, the login and callback routes will return a 404.

Fix: Confirm that middleware.ts exists at the project root (or src/middleware.ts) and that the auth0.middleware(request) call is present. Confirm the matcher pattern includes /auth/* paths. The pattern in Step 4 is designed to cover this correctly. Delete any app/api/auth/ route files left over from a v3 setup.

Environment variables returnundefinedin API routes after deploy

Cause: Variables are defined in .env.local but not in the Webflow Cloud project's Settings → Variables. On Webflow Cloud, .env.local is only read during local development. It is not available in the deployed environment. If you set environment variables only in .env.local, they will be undefined in production even if the build succeeds.

Fix: Set every required environment variable in the Webflow Cloud dashboard under Settings → Variables for your project. Then redeploy. The undefined check at the top of each factory function (if (!supabaseUrl || !supabaseKey) throw new Error(...)) makes this failure loud and immediate rather than producing a subtle downstream error.

Stripe webhook verification fails with "No signatures found matching the expected signature for payload"

Cause: The webhook handler is using request.json() to parse the body before passing it to stripe.webhooks.constructEventAsync. Stripe's signature verification computes a hash over the exact raw bytes of the request body.

Once the body is parsed as JSON and re-serialized, the byte sequence changes (even if the data is identical), causing the HMAC comparison to fail.

Fix: Use request.text() to read the raw body string, as shown in the webhook handler in Step 5. Pass the raw string directly to constructEventAsync. Also, confirm you're using constructEventAsync (the async version) rather than constructEvent. The synchronous version uses Node.js native crypto, which is not fully compatible with the edge runtime.

Supabase queries return empty arrays with no error

Cause: Supabase Row Level Security is enabled on the table, and the anon role does not have a SELECT policy. RLS is enabled by default on all Supabase tables. Without an explicit policy, queries from the anon key return no rows and no error; a behavior that looks identical to an empty table.

Fix: In your Supabase dashboard, navigate to the table → Policies → New Policy. For public read access, create a policy that allows SELECT for the anon role with a condition of true. For authenticated user data, create a policy that filters by auth.uid() = user_id.

If you're passing Auth0 JWTs to Supabase to authorize user-specific queries, you'll need to configure Supabase's third-party auth integration to trust Auth0 tokens.

Connect your full-stack Webflow Cloud app to the broader ecosystem

In this guide, we covered the complete setup for a full-stack Next.js application on Webflow Cloud with Supabase handling your database layer, Auth0 managing authentication, and Stripe processing payments.

Explore Webflow + Supabase if you're building with both and see a canonical setup and connection reference.

For Stripe, the Webflow + Stripe integration page covers the payment provider context, and the Webflow + Auth0 integration page covers authentication configuration.

Explore Webflow’s developer documentation for deeper customization, custom Cloudflare bindings, D1 database setup, or API routes that interact directly with the Webflow Data API.

Frequently asked questions

Can I use Yarn or pnpm with Webflow Cloud?

No. Webflow Cloud only supports npm as the package manager. The build pipeline and the wrangler.jsonc configuration assumes npm-style lockfiles and module resolution. If your project uses Yarn or pnpm, you'll need to convert it to npm before initializing a Webflow Cloud project. Run npm install in your project root to generate a package-lock.json, then run webflow cloud init.

How is Webflow Cloud different from deploying Next.js to Vercel?

The core difference is the runtime. Vercel runs Next.js on Node.js servers (or on Vercel's Edge Runtime for explicitly declared edge routes). Webflow Cloud runs all routes on Cloudflare Workers (the V8 isolate runtime), which provides lower cold-start times and global distribution by default, but enforces stricter Node.js API compatibility. On Vercel, most Node.js SDKs work without modification. On Webflow Cloud, any library that uses http, https, net, or child_process needs an edge-compatible alternative or configuration.

Do I need a separate Auth0 application for development and production?

Not necessarily, but it's strongly recommended. Using a single Auth0 application means managing separate Allowed Callback URLs for localhost and your production domain, which works. However, it creates the risk of accidentally deploying with development URLs still in place. A cleaner pattern is to create two Auth0 applications (one for development, one for production) with separate env var files, so the configuration can't cross-contaminate.

Can Webflow Cloud store session data, or does it need a separate session store?

Auth0 v4 handles sessions through encrypted cookies, which are managed entirely on the client and signed with AUTH0_SECRET. No server-side session store is required. The session cookie is read and verified by the Auth0 middleware on each request, so Webflow Cloud's stateless edge runtime handles session management correctly without additional infrastructure.

What's the difference between handling auth in Supabase and using Auth0?

Supabase includes its own authentication system (@supabase/auth-helpers), and for many projects, it's the simpler choice: one service for both the database and auth reduces moving parts. The reason to add Auth0 on top of Supabase is enterprise identity features: social login with fine-grained control, machine-to-machine tokens, SAML/enterprise SSO, and advanced MFA flows. If your app needs Auth0-specific enterprise features or you're inheriting an Auth0 tenant, the combination makes sense. If you're starting fresh and don't need those features, using Supabase Auth exclusively is a valid pattern.


Last Updated
May 8, 2026
Category

Related articles

How to customize Webflow form notification emails for every form on your site
How to customize Webflow form notification emails for every form on your site

How to customize Webflow form notification emails for every form on your site

How to customize Webflow form notification emails for every form on your site

Development
By
Colin Lateano
,
,
Read article
How to add reCAPTCHA spam protection to Webflow forms and block automated bots
How to add reCAPTCHA spam protection to Webflow forms and block automated bots

How to add reCAPTCHA spam protection to Webflow forms and block automated bots

How to add reCAPTCHA spam protection to Webflow forms and block automated bots

Development
By
Colin Lateano
,
,
Read article
How to send Webflow form submissions to HubSpot via Zapier without losing data
How to send Webflow form submissions to HubSpot via Zapier without losing data

How to send Webflow form submissions to HubSpot via Zapier without losing data

How to send Webflow form submissions to HubSpot via Zapier without losing data

Development
By
Colin Lateano
,
,
Read article
How to connect a Webflow form to Mailchimp without losing subscribers or breaking your design
How to connect a Webflow form to Mailchimp without losing subscribers or breaking your design

How to connect a Webflow form to Mailchimp without losing subscribers or breaking your design

How to connect a Webflow form to Mailchimp without losing subscribers or breaking your design

Development
By
Colin Lateano
,
,
Read article

verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logosoundcloud logoreddit logothe new york times logoideo logoupwork logodiscord logo
verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logosoundcloud logoreddit logothe new york times logoideo logoupwork logodiscord logo

Get started for free

Try Webflow for as long as you like with our free Starter plan. Purchase a paid Site plan to publish, host, and unlock additional features.

Get started — it’s free
Watch demo

Try Webflow for as long as you like with our free Starter plan. Purchase a paid Site plan to publish, host, and unlock additional features.