[{"data":1,"prerenderedAt":587},["ShallowReactive",2],{"blog-blueprints/cursor-supabase-vercel":3},{"id":4,"title":5,"body":6,"category":561,"date":562,"dateModified":563,"description":564,"draft":565,"extension":566,"faq":567,"featured":565,"headerVariant":571,"image":572,"keywords":572,"meta":573,"navigation":574,"ogDescription":575,"ogTitle":572,"path":576,"readTime":577,"schemaOrg":578,"schemaType":579,"seo":580,"sitemap":581,"stem":582,"tags":583,"twitterCard":585,"__hash__":586},"blog/blog/blueprints/cursor-supabase-vercel.md","Cursor + Supabase + Vercel Security Blueprint",{"type":7,"value":8,"toc":537},"minimark",[9,20,24,30,35,46,51,54,110,114,118,121,131,144,148,151,190,199,203,206,234,238,242,245,254,258,261,279,288,292,296,299,308,312,315,341,350,354,401,405,466,484,506,525],[10,11,12],"blueprint-summary",{},[13,14,15,19],"p",{},[16,17,18],"strong",{},"To secure a Cursor + Supabase + Vercel stack,"," you need to: (1) enable Row Level Security on all Supabase tables and write policies for each operation, (2) store your Supabase URL and anon key as NEXT_PUBLIC_ environment variables in Vercel while keeping the service_role key server-side only, (3) add security headers (X-Frame-Options, CSP) in vercel.json, (4) create a .cursorignore file to prevent AI from accessing your .env files, and (5) review all AI-generated code for missing authentication checks. This blueprint covers all three tools with color-coded guidance showing which security tasks belong to each platform.",[21,22],"blueprint-meta",{"time":23},"2-3 hours",[25,26,27],"tldr",{},[13,28,29],{},"This is the most popular vibe coding stack. Key security priorities: enable Row Level Security (RLS) on all Supabase tables, store your Supabase keys in Vercel environment variables, add security headers in vercel.json, and review AI-generated code for authentication gaps. About 73% of security issues in this stack come from missing RLS policies or exposed API keys.",[31,32,34],"h3",{"id":33},"platform-guides-checklists","Platform Guides & Checklists",[36,37,42],"pre",{"className":38,"code":40,"language":41},[39],"language-text","      Cursor Security Guide\n\n\n\n      Supabase Security Guide\n\n\n\n      Vercel Security Guide\n\n\n\n      Pre-Launch Checklist\n","text",[43,44,40],"code",{"__ignoreMap":45},"",[47,48,50],"h2",{"id":49},"stack-overview","Stack Overview",[13,52,53],{},"The Cursor + Supabase + Vercel stack has become the default choice for vibe coders building full-stack applications. Here's how the pieces fit together:",[55,56,57,73],"table",{},[58,59,60],"thead",{},[61,62,63,67,70],"tr",{},[64,65,66],"th",{},"Component",[64,68,69],{},"Role",[64,71,72],{},"Security Responsibility",[74,75,76,88,99],"tbody",{},[61,77,78,82,85],{},[79,80,81],"td",{},"Cursor",[79,83,84],{},"AI code editor",[79,86,87],{},"Code quality, secret detection in code",[61,89,90,93,96],{},[79,91,92],{},"Supabase",[79,94,95],{},"Database + Auth + Storage",[79,97,98],{},"RLS policies, auth config, API key protection",[61,100,101,104,107],{},[79,102,103],{},"Vercel",[79,105,106],{},"Hosting + Deployment",[79,108,109],{},"Environment variables, headers, edge functions",[47,111,113],{"id":112},"part-1-supabase-security-configuration","Part 1: Supabase Security Configuration",[31,115,117],{"id":116},"enable-row-level-security-supabase","Enable Row Level Security Supabase",[13,119,120],{},"RLS is the most critical security feature in Supabase. Without it, anyone with your anon key can read and modify all data.",[122,123,125],"code-block",{"label":124},"Enable RLS on every table",[36,126,129],{"className":127,"code":128,"language":41},[39],"-- Enable RLS\nALTER TABLE users ENABLE ROW LEVEL SECURITY;\nALTER TABLE posts ENABLE ROW LEVEL SECURITY;\nALTER TABLE comments ENABLE ROW LEVEL SECURITY;\n\n-- Example policy: users can only read their own data\nCREATE POLICY \"Users can view own data\"\n  ON users FOR SELECT\n  USING (auth.uid() = id);\n\n-- Example policy: users can only update their own data\nCREATE POLICY \"Users can update own data\"\n  ON users FOR UPDATE\n  USING (auth.uid() = id);\n",[43,130,128],{"__ignoreMap":45},[132,133,134],"warning-box",{},[13,135,136,139,140,143],{},[16,137,138],{},"Common mistake:"," Creating tables without RLS enabled. Cursor might generate table creation SQL without RLS. Always add ",[43,141,142],{},"ENABLE ROW LEVEL SECURITY"," immediately after creating any table.",[31,145,147],{"id":146},"protect-your-supabase-keys-supabase-vercel","Protect Your Supabase Keys Supabase Vercel",[13,149,150],{},"Supabase provides two types of keys:",[55,152,153,166],{},[58,154,155],{},[61,156,157,160,163],{},[64,158,159],{},"Key Type",[64,161,162],{},"Where to Use",[64,164,165],{},"Exposure Risk",[74,167,168,179],{},[61,169,170,173,176],{},[79,171,172],{},"anon/public key",[79,174,175],{},"Frontend (browser)",[79,177,178],{},"Safe if RLS is configured",[61,180,181,184,187],{},[79,182,183],{},"service_role key",[79,185,186],{},"Server only",[79,188,189],{},"Never expose (bypasses RLS)",[122,191,193],{"label":192},"Vercel environment variables",[36,194,197],{"className":195,"code":196,"language":41},[39],"# Public (can be exposed to browser)\nNEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co\nNEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...\n\n# Private (server-side only)\nSUPABASE_SERVICE_ROLE_KEY=eyJ...\n",[43,198,196],{"__ignoreMap":45},[31,200,202],{"id":201},"configure-auth-settings-supabase","Configure Auth Settings Supabase",[13,204,205],{},"In the Supabase dashboard, configure these auth settings:",[207,208,209,216,222,228],"ul",{},[210,211,212,215],"li",{},[16,213,214],{},"Site URL:"," Set to your production domain",[210,217,218,221],{},[16,219,220],{},"Redirect URLs:"," Add only your app's domains",[210,223,224,227],{},[16,225,226],{},"Email confirmations:"," Enable for production",[210,229,230,233],{},[16,231,232],{},"Password requirements:"," Set minimum length (8+ characters)",[47,235,237],{"id":236},"part-2-vercel-security-configuration","Part 2: Vercel Security Configuration",[31,239,241],{"id":240},"set-security-headers-vercel","Set Security Headers Vercel",[13,243,244],{},"Add security headers to protect against common attacks:",[122,246,248],{"label":247},"vercel.json",[36,249,252],{"className":250,"code":251,"language":41},[39],"{\n  \"headers\": [\n    {\n      \"source\": \"/(.*)\",\n      \"headers\": [\n        {\n          \"key\": \"X-Frame-Options\",\n          \"value\": \"DENY\"\n        },\n        {\n          \"key\": \"X-Content-Type-Options\",\n          \"value\": \"nosniff\"\n        },\n        {\n          \"key\": \"Referrer-Policy\",\n          \"value\": \"strict-origin-when-cross-origin\"\n        },\n        {\n          \"key\": \"Permissions-Policy\",\n          \"value\": \"camera=(), microphone=(), geolocation=()\"\n        }\n      ]\n    }\n  ]\n}\n",[43,253,251],{"__ignoreMap":45},[31,255,257],{"id":256},"configure-environment-variables-vercel","Configure Environment Variables Vercel",[13,259,260],{},"In Vercel's dashboard, add your environment variables:",[262,263,264,267,270,276],"ol",{},[210,265,266],{},"Go to Project Settings > Environment Variables",[210,268,269],{},"Add variables for each environment (Production, Preview, Development)",[210,271,272,273],{},"Prefix client-side variables with ",[43,274,275],{},"NEXT_PUBLIC_",[210,277,278],{},"Keep service keys without the prefix (server-only)",[280,281,282],"tip-box",{},[13,283,284,287],{},[16,285,286],{},"Tip:"," Use different Supabase projects for development and production. This prevents accidental data exposure and makes it easier to test RLS policies.",[47,289,291],{"id":290},"part-3-cursor-security-practices","Part 3: Cursor Security Practices",[31,293,295],{"id":294},"configure-cursorignore-cursor","Configure .cursorignore Cursor",[13,297,298],{},"Prevent sensitive files from being sent to AI:",[122,300,302],{"label":301},".cursorignore",[36,303,306],{"className":304,"code":305,"language":41},[39],"# Environment files\n.env\n.env.local\n.env.production\n\n# Supabase local config\nsupabase/.env\n\n# Any credentials\n**/credentials.json\n**/service-account.json\n",[43,307,305],{"__ignoreMap":45},[31,309,311],{"id":310},"review-ai-generated-code-cursor","Review AI-Generated Code Cursor",[13,313,314],{},"When Cursor generates code that interacts with Supabase, check for:",[207,316,317,323,329,335],{},[210,318,319,322],{},[16,320,321],{},"Missing auth checks:"," Ensure protected routes verify the user session",[210,324,325,328],{},[16,326,327],{},"Direct service_role usage:"," Use anon key on client, service_role only on server",[210,330,331,334],{},[16,332,333],{},"Hardcoded keys:"," Replace any hardcoded values with environment variables",[210,336,337,340],{},[16,338,339],{},"Missing error handling:"," Don't expose error details to users",[122,342,344],{"label":343},"Secure Supabase client setup",[36,345,348],{"className":346,"code":347,"language":41},[39],"// Client-side (browser)\nimport { createClient } from '@supabase/supabase-js'\n\nconst supabase = createClient(\n  process.env.NEXT_PUBLIC_SUPABASE_URL!,\n  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!\n)\n\n// Server-side (API routes, server components)\nimport { createClient } from '@supabase/supabase-js'\n\nconst supabaseAdmin = createClient(\n  process.env.NEXT_PUBLIC_SUPABASE_URL!,\n  process.env.SUPABASE_SERVICE_ROLE_KEY!\n)\n",[43,349,347],{"__ignoreMap":45},[47,351,353],{"id":352},"security-checklist-with-tool-attribution","Security Checklist with Tool Attribution",[355,356,358,362,365,368,371,374,377,380,383,386,389,392,395,398],"checklist-section",{"title":357},"Pre-Launch Checklist for Cursor + Supabase + Vercel",[359,360],"checklist-item",{"label":361},"RLS enabled on all Supabase tables",[359,363],{"label":364},"RLS policies created for SELECT, INSERT, UPDATE, DELETE on each table",[359,366],{"label":367},"service_role key only used in server-side code",[359,369],{"label":370},"Environment variables configured in Vercel dashboard",[359,372],{"label":373},"Security headers configured in vercel.json",[359,375],{"label":376},".cursorignore excludes .env and credential files",[359,378],{"label":379},".gitignore includes .env files",[359,381],{"label":382},"Auth redirect URLs configured in Supabase dashboard",[359,384],{"label":385},"Email confirmations enabled for production",[359,387],{"label":388},"AI-generated API routes have authentication checks",[359,390],{"label":391},"No hardcoded secrets in codebase",[359,393],{"label":394},"Supabase Storage buckets have appropriate RLS policies",[359,396],{"label":397},"Different Supabase projects for dev and production",[359,399],{"label":400},"Vercel preview deployments use separate environment variables",[47,402,404],{"id":403},"common-vulnerabilities-in-this-stack","Common Vulnerabilities in This Stack",[55,406,407,420],{},[58,408,409],{},[61,410,411,414,417],{},[64,412,413],{},"Vulnerability",[64,415,416],{},"Cause",[64,418,419],{},"Fix",[74,421,422,433,444,455],{},[61,423,424,427,430],{},[79,425,426],{},"Data exposed to all users",[79,428,429],{},"RLS not enabled or no policies",[79,431,432],{},"Enable RLS, create policies",[61,434,435,438,441],{},[79,436,437],{},"API key in git history",[79,439,440],{},"Committed .env file",[79,442,443],{},"Rotate key, add to .gitignore",[61,445,446,449,452],{},[79,447,448],{},"Unauthorized data modification",[79,450,451],{},"Missing UPDATE/DELETE policies",[79,453,454],{},"Add policies for all operations",[61,456,457,460,463],{},[79,458,459],{},"Service key exposed",[79,461,462],{},"Used service_role on client",[79,464,465],{},"Move to server-side only",[467,468,469,473],"stack-comparison",{},[31,470,472],{"id":471},"alternative-stack-configurations","Alternative Stack Configurations",[467,474,475,478],{},[13,476,477],{},"Cursor + Firebase + Vercel\nSwap Supabase for Firebase. Different security model with Firestore rules instead of RLS.",[36,479,482],{"className":480,"code":481,"language":41},[39],"      Cursor + Supabase + Netlify\n      Same database, different host. Netlify uses _headers file instead of vercel.json.\n\n\n      Bolt.new + Supabase + Vercel\n      Swap Cursor for Bolt.new. Same Supabase/Vercel config, different AI code review approach.\n",[43,483,481],{"__ignoreMap":45},[485,486,487,494,500],"faq-section",{},[488,489,491],"faq-item",{"question":490},"Is the Supabase anon key safe to expose?",[13,492,493],{},"Yes, if RLS is properly configured. The anon key is designed for client-side use and only allows access that your RLS policies permit. Think of it as a \"public API key\" that still requires authentication for protected data.",[488,495,497],{"question":496},"Do I need different Supabase projects for dev and production?",[13,498,499],{},"It's strongly recommended. Using separate projects prevents accidental data leaks, lets you test RLS changes safely, and gives you isolated environments for development.",[488,501,503],{"question":502},"How do I test if my RLS policies work?",[13,504,505],{},"Use the Supabase SQL editor with \"Run as authenticated user\" to test policies. Also test your app while logged out and as different users to verify data isolation.",[507,508,509,515,520],"related-articles",{},[510,511],"related-card",{"description":512,"href":513,"title":514},"Firebase alternative configuration","/blog/blueprints/cursor-firebase-vercel","Cursor + Firebase + Vercel",[510,516],{"description":517,"href":518,"title":519},"Framework-focused security","/blog/blueprints/nextjs-supabase-vercel","Next.js + Supabase + Vercel",[510,521],{"description":522,"href":523,"title":524},"Full Next.js App Router security","/blog/blueprints/cursor-nextjs-supabase","Cursor + Next.js + Supabase",[526,527,530,534],"cta-box",{"href":528,"label":529},"/","Start Free Scan",[47,531,533],{"id":532},"using-this-stack","Using This Stack?",[13,535,536],{},"Scan your Cursor + Supabase + Vercel app for security issues.",{"title":45,"searchDepth":538,"depth":538,"links":539},2,[540,542,543,548,552,556,557,560],{"id":33,"depth":541,"text":34},3,{"id":49,"depth":538,"text":50},{"id":112,"depth":538,"text":113,"children":544},[545,546,547],{"id":116,"depth":541,"text":117},{"id":146,"depth":541,"text":147},{"id":201,"depth":541,"text":202},{"id":236,"depth":538,"text":237,"children":549},[550,551],{"id":240,"depth":541,"text":241},{"id":256,"depth":541,"text":257},{"id":290,"depth":538,"text":291,"children":553},[554,555],{"id":294,"depth":541,"text":295},{"id":310,"depth":541,"text":311},{"id":352,"depth":538,"text":353},{"id":403,"depth":538,"text":404,"children":558},[559],{"id":471,"depth":541,"text":472},{"id":532,"depth":538,"text":533},"blueprints","2026-02-02","2026-02-23","Complete security blueprint for the Cursor + Supabase + Vercel stack. Learn to configure RLS, protect API keys, set security headers, and deploy securely.",false,"md",[568,570],{"question":490,"answer":569},"Yes, if RLS is properly configured. The anon key is designed for client-side use and only allows access that your RLS policies permit. Think of it as a public API key that still requires authentication for protected data.",{"question":496,"answer":499},"purple",null,{},true,"Complete security configuration for the most popular vibe coding stack.","/blog/blueprints/cursor-supabase-vercel","12 min read","[object Object]","Article",{"title":5,"description":564},{"loc":576},"blog/blueprints/cursor-supabase-vercel",[584],"Most Popular","summary_large_image","obJrYv1LKeGptPgYCxm1dmIZXscMphQ2lyJ3xCHYDR4",1775843932585]