Securing Next.js + Supabase, in depth
The specific mistakes that get apps on this stack hacked — and the exact fix for each. No generic checklists.
- ·6 min read
eval() in JavaScript: the one line that turns user input into remote code execution
eval() runs whatever string you hand it. The moment any part is user-controlled, it's remote code execution. Here's why, and the exact fix.
JavaScriptevalRCENode.js - ·7 min read
Hardcoded API keys in a Next.js app: why deleting the line isn't enough
A hardcoded API key stays in git history after you delete the line. Here's why, and how to actually rotate it and purge it from your Next.js repo.
SecretsGitNext.jsStripe - ·6 min read
NEXT_PUBLIC_ leaked my API key — how it happens and how to catch it
NEXT_PUBLIC_ inlines any value into your client bundle. Here's how it ships your API key to every visitor — and why anon keys are safe but secrets aren't.
Next.jsSecretsAPI KeysEnv Vars - ·6 min read
How to validate the request body in a Next.js API route (and why request.json() is a hole)
request.json() returns any, so attackers can send any shape. Here's the mass-assignment risk and the exact zod safeParse fix for Next.js routes.
Next.jsValidationZodSecurity - ·6 min read
Access-Control-Allow-Origin: * — the CORS wildcard that quietly leaks your Next.js API
Access-Control-Allow-Origin: * in a Next.js route hands your API responses to any website. Why it leaks data, and the exact allowlist fix.
Next.jsCORSAPISecurity - ·7 min read
Next.js middleware runs on every request — add a matcher
Next.js middleware with no config.matcher runs on every request — static assets, images, prefetches included. Why that's a bug, and the exact fix.
Next.jsMiddlewareAuthPerformance - ·7 min read
Are Next.js Server Actions secure? The auth check everyone forgets
Server Actions are public POST endpoints anyone can call. Here's why a 'use server' mutation with no auth check is exploitable — and the fix to add.
Next.jsServer ActionsAuthApp Router - ·7 min read
Supabase Edge Functions are public by default: verify JWTs
Supabase Edge Functions are publicly invocable the second you deploy them. Here is why that is dangerous and how to verify the caller's JWT.
SupabaseEdge FunctionsAuthJWT - ·7 min read
Stop building SQL strings: injection in Next.js + Supabase apps
Interpolating a user value into a SQL string is how SQL injection reaches Supabase apps — even behind RLS. Here's the risk and the exact fix.
SupabaseSQL InjectionNext.jsRPC - ·7 min read
Public Supabase storage buckets: the silent data leak
A public Supabase storage bucket makes every object readable by anyone with the URL — no auth, no RLS. Here's why public: true leaks user data, and the fix.
SupabaseStorageSigned URLsSecurity - ·14 min read
Disabling RLS in Supabase: what it exposes and the fix
Disabling Row Level Security on a Supabase table makes it fully public via the anon key. What it exposes, how to re-enable RLS, and the exact fix.
SupabaseRLSPostgresMigrations - ·7 min read
Your Supabase RLS policy isn't scoped to the user: the missing auth.uid()
An RLS policy with a real WHERE-style condition can still leak every user's rows. The cause: the predicate never references auth.uid().
SupabaseRLSPostgresSecurity - ·7 min read
Your Supabase RLS is enabled and still wide open: the USING (true) trap
RLS being enabled doesn't mean it protects anything. A policy with USING (true) returns every row to every caller.
SupabaseRLSPostgresSecurity - ·6 min read
Hardcoding your Supabase service_role JWT is a permanent breach, even after you delete it
Pasting the service_role JWT (eyJ...) straight into source bypasses every RLS policy and lives in git history forever. Here's why, and the exact fix.
SupabaseSecretsJWTGit - ·8 min read
Every Supabase table needs RLS — the one you forgot is public
New Supabase tables ship with RLS off. The one table you forgot to enable it on is readable by anyone holding your anon key. How to catch it.
SupabaseRLSPostgresMigrations