87  87. Step 13: Deploy to Production

As of Oct 22, 2026 · Official docs: Vercel · Vercel environment variables · Supabase: Redirect URLs

Once everything works on your computer, save your work to GitHub, then put the front end online with Vercel. In Classes 10–12, these are steps (J) and (K) Put the app online with Vercel.

87.1 Save Your Work to GitHub

Vercel builds the app from your fork on GitHub, so first push your changes there. Ask Claude to commit your changes and push them to your fork, or do it yourself: type each of these commands in VS Code’s terminal, in the task-app folder, pressing Enter after each one. First:

git status

git status lists the files you changed or added since your last commit, and changes nothing. Make sure .env.local is not in the list: it holds your keys, and the project’s .gitignore file tells Git to ignore it (see .gitignore and Keeping Secrets Out of Git). Then:

git add -A
git commit -m "Set up the task app"
git push
  • git add -A chooses which changes go into your next commit: add stages changes, and -A (all) includes every changed, new and deleted file.
  • git commit -m "Set up the task app" saves those changes as a commit, a snapshot in your project’s history. -m gives the commit its message, in quotes; write one that says what you did.
  • git push uploads your new commits to your fork on GitHub.

87.2 Deploying the Frontend

Recommended: Vercel

Vercel is tightly integrated with Next.js and offers smooth deployments. Its free Hobby plan is enough for this project.

  1. At vercel.com, sign up with Continue with GitHub.
  2. Click Add New > Project, and import your task-app repository.
  3. During setup:
    • Select the correct framework: Next.js.
    • Before you deploy, open Environment Variables and add, with the values from your worksheet (the same ones as in .env.local):
      • NEXT_PUBLIC_SUPABASE_URL
      • NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY (your Supabase publishable key, sb_publishable_...)
      These two are all the app needs. Without the URL, the build fails. Don’t add any secret keys: the app doesn’t use them, and a secret key must never get a NEXT_PUBLIC_ name.
  4. Click Deploy. When it finishes, copy your app’s address (https://....vercel.app) into the worksheet.
  5. In the Supabase dashboard, Authentication > URL Configuration: change Site URL to your Vercel address, and add the redirect URL https://YOUR-APP.vercel.app/**. Keep the http://localhost:3000/** one, so that signing in still works on your computer.
  6. In Google Cloud, add your Vercel address to the OAuth client’s Authorized JavaScript origins. The Authorized redirect URI stays the Supabase callback URL (https://YOUR-PROJECT-REF.supabase.co/auth/v1/callback), and Stripe needs no changes.

If you add or change an environment variable later, redeploy: changes apply only to new deployments.

After deployment:

  • Visit the production URL to confirm it loads.
  • Test login, task creation, and subscription flows in production.

From now on, every git push to your fork makes Vercel deploy the new version automatically. More background in Hosting and Deployment.

Alternative Options

If you prefer other providers:

  • Netlify – good Next.js support (with appropriate adapters).
  • AWS Amplify, Render, or any provider that supports Node/Next.js.

Configuration will be similar: point to your repo and set env vars.

87.3 Update Environment Variables for Production

For a real product (beyond this course), you would also:

  • Use a separate production Supabase project (with its own URL and publishable key in Vercel), so that testing never touches real users’ data.
  • Switch Stripe from test mode (the sandbox) to live mode. Everything Stripe-related has a separate live version, and each one goes where its test version went:
    • the live secret key (sk_live_...): in Supabase secrets (npx supabase secrets set STRIPE_SECRET_KEY=sk_live_...), not in Vercel, and in Supabase Vault under the name stripe (the Vault page is in Project Settings, under Integrations), because the database creates Stripe customers itself;
    • a live product and price: their new price ID goes in the STRIPE_PRICE_ID secret;
    • a live webhook destination, set up again in live mode, and its new signing secret in STRIPE_WEBHOOK_SECRET.
    The app’s web pages never use a Stripe key, so nothing about Stripe goes into Vercel. (The publishable key, pk_live_..., isn’t used by this app.)
  • Use your production domain (e.g., https://yourapp.com) wherever the steps above use the Vercel address: Supabase’s Site URL and redirect URLs, and Google’s Authorized JavaScript origins. Google’s authorized redirect URI doesn’t change: it stays your Supabase callback URL.

There is no CORS setting to change in Supabase: the Edge Functions already send the header Access-Control-Allow-Origin: *, which lets any web address call them. (CORS is a browser rule about which sites may call a server from a web page.)

Practice Questions

Easy

Question 1 · Short answer

What does it mean to deploy to production? Why can’t other people use your app at http://localhost:3000?

Deploying to production means putting the app on servers on the internet so that real users can reach it at a public web address (Vercel gives you one). Production is the live version that real users use. localhost means “this computer”, so http://localhost:3000 only works on your own machine, and only while your development server is running.

Question 2 · Multiple choice

Why does the page recommend Vercel for deploying this app?

  1. Vercel is the only host that can run a Next.js app
  2. Vercel is tightly integrated with Next.js, so deployments are smooth
  3. Vercel also hosts the app’s database and edge functions
  4. Vercel replaces Stripe for handling payments

b. Vercel is the company that makes Next.js, and its hosting is built around it. Option a is false: the page lists Netlify, AWS Amplify and Render as alternatives. Option c is false: the database and edge functions stay on Supabase - this page deploys only the front end. Option d is false: Stripe still handles payments.

Question 3 · True or false

With the method on this page, your code must be in a Git repository (for example on GitHub) before Vercel can deploy it.

True. First you push your code to your fork on GitHub. Vercel then imports the project from that repository and builds the app from it. After that, every git push to the fork makes Vercel deploy the new version automatically.

Question 4 · Short answer

Stripe test keys start with pk_test_ and sk_test_. What do the matching live keys start with, and what do pk and sk stand for?

Live keys start with pk_live_ and sk_live_. pk stands for publishable key (safe to use in the browser) and sk for secret key (must be kept secret, on a server).

Question 5 · Short answer

Before you commit and push, you run git status. Which file must not appear in its list, and why?

.env.local. It holds your keys, and anything you push to GitHub can be seen by others. The project’s .gitignore file tells Git to ignore it, so it normally doesn’t appear. If it does, stop and ask Claude for help before you commit.

Medium

Question 6 · Short answer

Your .env.local file works perfectly on your laptop. Why doesn’t Vercel get the values in it automatically, and what do you need to do instead?

.env.local is listed in .gitignore, so it is never committed or pushed to GitHub - that is exactly what keeps your keys out of the repository. Vercel builds the app from the GitHub repository, so it never sees the file. You have to enter each variable (NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY and so on) in the environment variable settings of your Vercel project. If you add or change a variable after a deployment, redeploy so that the new value is used.

Question 7 · Multiple choice

Variables whose names start with NEXT_PUBLIC_ are built into the JavaScript that is sent to every visitor’s browser. Which of these values must never go into a NEXT_PUBLIC_ variable?

  1. The Supabase project URL
  2. The Supabase publishable key (sb_publishable_...)
  3. The Stripe publishable key (pk_...)
  4. The Stripe secret key (sk_...)

d. Anyone who visits your site can read a NEXT_PUBLIC_ value. The Supabase URL (a), the Supabase publishable key (b) and the Stripe publishable key (c) are designed to be safe in the browser. The secret key gives full access to your Stripe account (for example, issuing refunds or reading customer data), so it must stay on a server. In this project it is stored as a Supabase secret that the edge functions use.

Question 8 · Matching

When you go live, different settings live in different places. Match each item with where you set it.

Item Where
1. NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY for the live site A. Supabase secrets, used by the edge functions
2. The live Stripe secret key (sk_live_...) B. Supabase’s Auth settings (Site URL and allowed redirect URLs)
3. Your production web address, so that sign-in can return users to it C. The live-mode webhook settings in the Stripe Dashboard
4. The endpoint that sends Stripe’s events to your stripe-webhook function D. The environment variables of your Vercel project

1 → D, 2 → A, 3 → B, 4 → C.

The Next.js app on Vercel reads its NEXT_PUBLIC_... variables from Vercel. The edge functions read the Stripe secret key from Supabase secrets (that’s where you stored it with npx supabase secrets set when you set up Stripe). Sign-in redirects are controlled by Supabase Auth. Webhook endpoints are set up in Stripe, and live mode has its own endpoints, separate from test mode.

Question 9 · Short answer

Vercel reports that the deployment succeeded and your home page loads. According to the page, what should you test next, and why isn’t “the page loads” enough?

Visit the production URL and test login, task creation and the subscription flow there. A page that loads only proves the front end is online. Each of those flows depends on other services - Supabase, Google sign-in, OpenAI (through an edge function) and Stripe - and on settings that are different in production (web addresses, environment variables, keys). Any of them can be wrong even when the page looks fine.

Question 10 · Ordering

Put these commands in the order you use them to save your work to GitHub, and say what each one does.

  • A. git push
  • B. git commit -m "Set up the task app"
  • C. git status
  • D. git add -A

C → D → B → A.

  • git status lists what you changed, and changes nothing.
  • git add -A stages every changed, new and deleted file for the next commit.
  • git commit -m "..." saves the staged changes as a commit, with that message.
  • git push uploads the new commits to your fork on GitHub.

Question 11 · Multiple choice

Your app is now at https://maria-task-app.vercel.app. What do you change in Google Cloud for “Login with Google” to work there?

  1. Replace the Authorized redirect URI with https://maria-task-app.vercel.app
  2. Add https://maria-task-app.vercel.app to the client’s Authorized JavaScript origins
  3. Create a new Google Cloud project for the live site
  4. Nothing in Google Cloud, but change the Stripe webhook URL

b. The new address goes into Authorized JavaScript origins. The redirect URI (a) stays the Supabase callback URL, https://YOUR-PROJECT-REF.supabase.co/auth/v1/callback: Google always sends users back to Supabase, and Supabase then sends them on to your app. You don’t need a new project (c), and Stripe needs no changes (d), because its webhook calls Supabase, not Vercel.

Hard

Question 12 · Scenario

On your live Vercel site you click “Sign in with Google” and choose your account - and then you land on http://localhost:3000 instead of your live site. What’s wrong, and how do you fix it?

Supabase’s Auth settings still only know about your local address. After Google signs you in, Supabase sends you back only to addresses it has been told are allowed; when the requested address isn’t allowed, it falls back to the Site URL, which is still http://localhost:3000.

Fix: in Supabase’s Auth URL settings, set the Site URL to your production address and add the production address to the allowed redirect URLs (for example https://your-app.vercel.app/**, where ** means “any page on this site”). Also make sure http://localhost:3000/** is on the redirect list, so that sign-in still works when you develop locally.

Question 13 · Long answer

In this tutorial you created only one Supabase project, and you use it both for developing on your laptop and for the live site. The page talks about using “your production Supabase project URL” and keys. What does that suggest bigger teams do, and what is one risk of using a single project for everything?

Bigger teams usually keep separate Supabase projects: one (or more) for development and testing, and one for production that holds real users’ data. Each project has its own URL and keys, so .env.local points at the development project while Vercel points at the production project. With a single project, your Vercel variables simply use the same URL and publishable key as .env.local.

The risk: everything you do while developing - creating junk test tasks, running automated tests, trying out a new migration - happens in the same database that real users rely on. A broken experiment can break or clutter the live app.

Question 14 · Scenario

You switch Stripe to live mode and replace the test keys with live keys, but you skip “update webhook endpoints/secrets”. Real customers pay successfully, yet their accounts never become premium. Explain why, and what you should do.

Live mode is separate from test mode. The webhook endpoint you created, and its whsec_... signing secret, belong to test mode. In live mode, either no endpoint sends events to your stripe-webhook function at all, or - once you create one - it has a different signing secret from the test one stored in Supabase, so the signature check fails. Either way, your database never hears about the payments.

Fix: create a live-mode webhook endpoint that points to https://[your-project-ref].supabase.co/functions/v1/stripe-webhook (with the same events as before), then store its signing secret in Supabase:

npx supabase secrets set STRIPE_WEBHOOK_SECRET=whsec_...

Question 15 · Scenario

Sam imports his task-app repository into Vercel and clicks Deploy right away, without adding any environment variables. The build fails. He then adds NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY in the project’s settings. Is he done? What should he do, and what else must he set up after the first successful deploy?

He is not done. Variables apply only to new deployments, so he must redeploy. The build failed because the app needs the Supabase URL when it is built. After a successful deploy, he copies the app’s address and:

  • in Supabase (Authentication > URL Configuration) sets the Site URL to the Vercel address and adds the redirect URL https://HIS-APP.vercel.app/**, keeping http://localhost:3000/**;
  • in Google Cloud, adds the Vercel address to Authorized JavaScript origins.

Then he tests sign-in, tasks and the upgrade on the live site.

Question 16 · Scenario

Sam switches his app to Stripe live mode. He adds STRIPE_SECRET_KEY=sk_live_... to his Vercel project’s environment variables and redeploys, but upgrades still go to the test checkout page. What did he do wrong, and what three live values does he need, and where?

The Stripe calls happen in the Edge Functions (and in the database), not in the Next.js app on Vercel, so Vercel’s variables don’t matter for Stripe. He needs:

  1. the live secret key in Supabase secrets (npx supabase secrets set STRIPE_SECRET_KEY=sk_live_...) and in Supabase Vault under the name stripe;
  2. the live price’s ID in the STRIPE_PRICE_ID secret;
  3. a live-mode webhook destination, with its new signing secret in the STRIPE_WEBHOOK_SECRET secret.

He should also remove the key from Vercel: a secret key doesn’t belong in any place it isn’t needed.

Question 17 · True or false

Before the live site can call the Edge Functions, you must add your Vercel address to a CORS setting in Supabase.

False. There is no such setting to change. The functions themselves send the header Access-Control-Allow-Origin: *, which tells browsers that a web page from any address may call them. What you do change for the live site is Supabase’s Auth settings (Site URL and redirect URLs) and Google’s Authorized JavaScript origins.

© 2025-2026 Y. Rosenthal. All rights reserved.