[{"data":1,"prerenderedAt":891},["ShallowReactive",2],{"blog-how-to/deploy-lovable-securely":3},{"id":4,"title":5,"body":6,"category":865,"date":866,"dateModified":866,"description":867,"draft":868,"extension":869,"faq":870,"featured":868,"headerVariant":876,"image":877,"keywords":878,"meta":879,"navigation":319,"ogDescription":880,"ogTitle":877,"path":881,"readTime":882,"schemaOrg":883,"schemaType":884,"seo":885,"sitemap":886,"stem":887,"tags":888,"twitterCard":889,"__hash__":890},"blog/blog/how-to/deploy-lovable-securely.md","How to Deploy Lovable Securely (2026)",{"type":7,"value":8,"toc":852},"minimark",[9,18,21,27,32,35,38,68,80,84,165,275,604,681,726,746,750,773,783,817,836,848],[10,11,12,13,17],"p",{},"Any variable prefixed with ",[14,15,16],"code",{},"VITE_"," in a Lovable app ends up compiled into your JavaScript bundle. Literally. Open the network tab on a deployed Lovable app, fetch the main JS file, search for your env var name, and you'll find the value sitting there in plain text. That's not a bug in Lovable; it's how Vite works. But it's a critical thing to understand before you go live.",[10,19,20],{},"Deploying Lovable securely means catching this before users land on your site.",[22,23,24],"tldr",{},[10,25,26],{},"Lovable generates functional apps fast but ships them without the security hardening you need for production. The six steps below cover the actual risks: leaked secrets in your JS bundle, Supabase tables open to any authenticated user, missing auth enforcement, and no security headers. Walk through these before you click Publish.",[28,29,31],"h2",{"id":30},"why-lovable-apps-need-a-security-pass-before-launch","Why Lovable apps need a security pass before launch",[10,33,34],{},"Lovable prioritizes getting a working app in front of you quickly. Security configuration that would slow down prototyping gets left out. That's a reasonable product decision for a prototype tool. It becomes a problem when founders ship that prototype directly to paying users.",[10,36,37],{},"The three risks we see most often in CheckYourVibe scans of Lovable apps:",[39,40,41,56,62],"ol",{},[42,43,44,51,52,55],"li",{},[45,46,47,48,50],"strong",{},"Secret keys as ",[14,49,16],{}," variables:"," Stripe secret keys, OpenAI keys, and Supabase ",[14,53,54],{},"service_role"," keys exposed in the client bundle",[42,57,58,61],{},[45,59,60],{},"Supabase RLS off:"," tables created with no Row Level Security policies, so any logged-in user can query any row",[42,63,64,67],{},[45,65,66],{},"Auth checks on the frontend only:"," a Lovable app might hide a UI element for non-admins while leaving the underlying Supabase query completely unprotected",[69,70,71],"warning-box",{},[10,72,73,74,76,77,79],{},"The ",[14,75,54],{}," key bypasses all Supabase Row Level Security. If it ends up as a ",[14,78,16],{}," variable, any user can extract it from the bundle and call your database as an admin. Rotate it immediately if you find it exposed.",[28,81,83],{"id":82},"the-six-steps","The six steps",[85,86,88,93,100,108,123,131,156],"step",{"number":87},"1",[89,90,92],"h3",{"id":91},"audit-your-environment-variables","Audit your environment variables",[10,94,95,96,99],{},"In Lovable, go to ",[45,97,98],{},"Settings > Environment Variables",". Look at every entry.",[10,101,102],{},[45,103,104,105,107],{},"Safe as client-side (",[14,106,16],{},"):",[109,110,111,117],"ul",{},[42,112,113,116],{},[14,114,115],{},"VITE_SUPABASE_URL",": public, needed by the Supabase client",[42,118,119,122],{},[14,120,121],{},"VITE_SUPABASE_ANON_KEY",": designed to be public, but only safe if RLS is enabled",[10,124,125],{},[45,126,127,128,130],{},"Must never be ",[14,129,16],{},":",[109,132,133,144,147,153],{},[42,134,135,136,139,140,143],{},"Stripe secret key (",[14,137,138],{},"sk_live_..."," or ",[14,141,142],{},"sk_test_...",")",[42,145,146],{},"OpenAI API key",[42,148,149,150,152],{},"Supabase ",[14,151,54],{}," key",[42,154,155],{},"Any third-party key that doesn't have its own row-level or user-level scoping",[10,157,158,159,161,162,164],{},"If any secret key is currently prefixed with ",[14,160,16],{},", rotate it now and move it before redeploying. A rotated key that's still in your bundle does nothing. You have to remove the ",[14,163,16],{}," prefix and move the logic server-side.",[85,166,168,172,183,190,193,238,241,269],{"number":167},"2",[89,169,171],{"id":170},"enable-supabase-rls-on-every-table","Enable Supabase RLS on every table",[10,173,174,175,178,179,182],{},"Open your ",[45,176,177],{},"Supabase dashboard",", navigate to ",[45,180,181],{},"Authentication > Policies",", and check each table.",[10,184,185,186,189],{},"A table with RLS disabled has a red warning: \"Row Level Security is disabled.\" That means any authenticated user can run ",[14,187,188],{},"SELECT * FROM that_table"," through the Supabase JS client and get every row back.",[10,191,192],{},"For each table, enable RLS and add at least one policy. The most common pattern for user-owned data:",[194,195,197],"code-block",{"label":196},"Supabase RLS policy: user owns their rows",[198,199,204],"pre",{"className":200,"code":201,"language":202,"meta":203,"style":203},"language-sql shiki shiki-themes github-light github-dark","-- Users can only see their own rows\nCREATE POLICY \"Users see own rows\"\nON public.your_table\nFOR ALL\nUSING (auth.uid() = user_id);\n","sql","",[14,205,206,214,220,226,232],{"__ignoreMap":203},[207,208,211],"span",{"class":209,"line":210},"line",1,[207,212,213],{},"-- Users can only see their own rows\n",[207,215,217],{"class":209,"line":216},2,[207,218,219],{},"CREATE POLICY \"Users see own rows\"\n",[207,221,223],{"class":209,"line":222},3,[207,224,225],{},"ON public.your_table\n",[207,227,229],{"class":209,"line":228},4,[207,230,231],{},"FOR ALL\n",[207,233,235],{"class":209,"line":234},5,[207,236,237],{},"USING (auth.uid() = user_id);\n",[10,239,240],{},"If you're not sure which tables exist, run this in the Supabase SQL editor to get a list of all tables with RLS status:",[194,242,244],{"label":243},"Check RLS status across tables",[198,245,247],{"className":200,"code":246,"language":202,"meta":203,"style":203},"SELECT schemaname, tablename, rowsecurity\nFROM pg_tables\nWHERE schemaname = 'public'\nORDER BY tablename;\n",[14,248,249,254,259,264],{"__ignoreMap":203},[207,250,251],{"class":209,"line":210},[207,252,253],{},"SELECT schemaname, tablename, rowsecurity\n",[207,255,256],{"class":209,"line":216},[207,257,258],{},"FROM pg_tables\n",[207,260,261],{"class":209,"line":222},[207,262,263],{},"WHERE schemaname = 'public'\n",[207,265,266],{"class":209,"line":228},[207,267,268],{},"ORDER BY tablename;\n",[10,270,271,274],{},[14,272,273],{},"rowsecurity = false"," means that table is wide open.",[85,276,278,282,285,288,593],{"number":277},"3",[89,279,281],{"id":280},"move-secrets-to-supabase-edge-functions","Move secrets to Supabase Edge Functions",[10,283,284],{},"Any API call that requires a secret key needs to happen server-side. Supabase Edge Functions are the easiest option for Lovable apps because you're already on Supabase.",[10,286,287],{},"Create a new Edge Function via the Supabase dashboard or CLI, and set the secret key as an environment variable there (not in your Lovable app). Your frontend calls the Edge Function via a normal fetch; the function does the work with the secret key.",[194,289,291],{"label":290},"Edge Function example: OpenAI call",[198,292,296],{"className":293,"code":294,"language":295,"meta":203,"style":203},"language-typescript shiki shiki-themes github-light github-dark","import { serve } from \"https://deno.land/std@0.168.0/http/server.ts\"\n\nserve(async (req) => {\n  const { prompt } = await req.json()\n  \n  const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n    method: \"POST\",\n    headers: {\n      \"Authorization\": `Bearer ${Deno.env.get(\"OPENAI_API_KEY\")}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      model: \"gpt-4o\",\n      messages: [{ role: \"user\", content: prompt }],\n    }),\n  })\n  \n  return new Response(await response.text(), {\n    headers: { \"Content-Type\": \"application/json\" },\n  })\n})\n","typescript",[14,297,298,315,321,349,379,384,408,420,426,464,477,483,500,511,523,529,535,540,566,582,587],{"__ignoreMap":203},[207,299,300,304,308,311],{"class":209,"line":210},[207,301,303],{"class":302},"szBVR","import",[207,305,307],{"class":306},"sVt8B"," { serve } ",[207,309,310],{"class":302},"from",[207,312,314],{"class":313},"sZZnC"," \"https://deno.land/std@0.168.0/http/server.ts\"\n",[207,316,317],{"class":209,"line":216},[207,318,320],{"emptyLinePlaceholder":319},true,"\n",[207,322,323,327,330,333,336,340,343,346],{"class":209,"line":222},[207,324,326],{"class":325},"sScJk","serve",[207,328,329],{"class":306},"(",[207,331,332],{"class":302},"async",[207,334,335],{"class":306}," (",[207,337,339],{"class":338},"s4XuR","req",[207,341,342],{"class":306},") ",[207,344,345],{"class":302},"=>",[207,347,348],{"class":306}," {\n",[207,350,351,354,357,361,364,367,370,373,376],{"class":209,"line":228},[207,352,353],{"class":302},"  const",[207,355,356],{"class":306}," { ",[207,358,360],{"class":359},"sj4cs","prompt",[207,362,363],{"class":306}," } ",[207,365,366],{"class":302},"=",[207,368,369],{"class":302}," await",[207,371,372],{"class":306}," req.",[207,374,375],{"class":325},"json",[207,377,378],{"class":306},"()\n",[207,380,381],{"class":209,"line":234},[207,382,383],{"class":306},"  \n",[207,385,387,389,392,395,397,400,402,405],{"class":209,"line":386},6,[207,388,353],{"class":302},[207,390,391],{"class":359}," response",[207,393,394],{"class":302}," =",[207,396,369],{"class":302},[207,398,399],{"class":325}," fetch",[207,401,329],{"class":306},[207,403,404],{"class":313},"\"https://api.openai.com/v1/chat/completions\"",[207,406,407],{"class":306},", {\n",[207,409,411,414,417],{"class":209,"line":410},7,[207,412,413],{"class":306},"    method: ",[207,415,416],{"class":313},"\"POST\"",[207,418,419],{"class":306},",\n",[207,421,423],{"class":209,"line":422},8,[207,424,425],{"class":306},"    headers: {\n",[207,427,429,432,435,438,441,444,447,449,452,454,457,459,462],{"class":209,"line":428},9,[207,430,431],{"class":313},"      \"Authorization\"",[207,433,434],{"class":306},": ",[207,436,437],{"class":313},"`Bearer ${",[207,439,440],{"class":306},"Deno",[207,442,443],{"class":313},".",[207,445,446],{"class":306},"env",[207,448,443],{"class":313},[207,450,451],{"class":325},"get",[207,453,329],{"class":313},[207,455,456],{"class":313},"\"OPENAI_API_KEY\"",[207,458,143],{"class":313},[207,460,461],{"class":313},"}`",[207,463,419],{"class":306},[207,465,467,470,472,475],{"class":209,"line":466},10,[207,468,469],{"class":313},"      \"Content-Type\"",[207,471,434],{"class":306},[207,473,474],{"class":313},"\"application/json\"",[207,476,419],{"class":306},[207,478,480],{"class":209,"line":479},11,[207,481,482],{"class":306},"    },\n",[207,484,486,489,492,494,497],{"class":209,"line":485},12,[207,487,488],{"class":306},"    body: ",[207,490,491],{"class":359},"JSON",[207,493,443],{"class":306},[207,495,496],{"class":325},"stringify",[207,498,499],{"class":306},"({\n",[207,501,503,506,509],{"class":209,"line":502},13,[207,504,505],{"class":306},"      model: ",[207,507,508],{"class":313},"\"gpt-4o\"",[207,510,419],{"class":306},[207,512,514,517,520],{"class":209,"line":513},14,[207,515,516],{"class":306},"      messages: [{ role: ",[207,518,519],{"class":313},"\"user\"",[207,521,522],{"class":306},", content: prompt }],\n",[207,524,526],{"class":209,"line":525},15,[207,527,528],{"class":306},"    }),\n",[207,530,532],{"class":209,"line":531},16,[207,533,534],{"class":306},"  })\n",[207,536,538],{"class":209,"line":537},17,[207,539,383],{"class":306},[207,541,543,546,549,552,554,557,560,563],{"class":209,"line":542},18,[207,544,545],{"class":302},"  return",[207,547,548],{"class":302}," new",[207,550,551],{"class":325}," Response",[207,553,329],{"class":306},[207,555,556],{"class":302},"await",[207,558,559],{"class":306}," response.",[207,561,562],{"class":325},"text",[207,564,565],{"class":306},"(), {\n",[207,567,569,572,575,577,579],{"class":209,"line":568},19,[207,570,571],{"class":306},"    headers: { ",[207,573,574],{"class":313},"\"Content-Type\"",[207,576,434],{"class":306},[207,578,474],{"class":313},[207,580,581],{"class":306}," },\n",[207,583,585],{"class":209,"line":584},20,[207,586,534],{"class":306},[207,588,590],{"class":209,"line":589},21,[207,591,592],{"class":306},"})\n",[10,594,595,596,599,600,603],{},"Set ",[14,597,598],{},"OPENAI_API_KEY"," in ",[45,601,602],{},"Supabase > Edge Functions > Secrets",". It never touches your Lovable environment variables.",[85,605,607,611,614,617,624,674],{"number":606},"4",[89,608,610],{"id":609},"add-security-headers","Add security headers",[10,612,613],{},"Lovable's built-in hosting doesn't give you fine-grained control over HTTP response headers. If you're using it, the options are limited to what Lovable exposes in their dashboard.",[10,615,616],{},"For full control, export your app to GitHub (Lovable's Publish menu) and deploy to Netlify or Vercel. Both are free tiers for small apps.",[10,618,619,620,623],{},"On Netlify, add a ",[14,621,622],{},"netlify.toml"," to your repo root:",[194,625,627],{"label":626},"netlify.toml: security headers",[198,628,632],{"className":629,"code":630,"language":631,"meta":203,"style":203},"language-toml shiki shiki-themes github-light github-dark","[[headers]]\n  for = \"/*\"\n  [headers.values]\n    X-Frame-Options = \"DENY\"\n    X-Content-Type-Options = \"nosniff\"\n    Referrer-Policy = \"strict-origin-when-cross-origin\"\n    Strict-Transport-Security = \"max-age=31536000; includeSubDomains\"\n    Content-Security-Policy = \"default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self' https://*.supabase.co\"\n","toml",[14,633,634,639,644,649,654,659,664,669],{"__ignoreMap":203},[207,635,636],{"class":209,"line":210},[207,637,638],{},"[[headers]]\n",[207,640,641],{"class":209,"line":216},[207,642,643],{},"  for = \"/*\"\n",[207,645,646],{"class":209,"line":222},[207,647,648],{},"  [headers.values]\n",[207,650,651],{"class":209,"line":228},[207,652,653],{},"    X-Frame-Options = \"DENY\"\n",[207,655,656],{"class":209,"line":234},[207,657,658],{},"    X-Content-Type-Options = \"nosniff\"\n",[207,660,661],{"class":209,"line":386},[207,662,663],{},"    Referrer-Policy = \"strict-origin-when-cross-origin\"\n",[207,665,666],{"class":209,"line":410},[207,667,668],{},"    Strict-Transport-Security = \"max-age=31536000; includeSubDomains\"\n",[207,670,671],{"class":209,"line":422},[207,672,673],{},"    Content-Security-Policy = \"default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self' https://*.supabase.co\"\n",[10,675,676,677,680],{},"Adjust the CSP ",[14,678,679],{},"connect-src"," to include any third-party APIs your app calls.",[85,682,684,688,691,694,700,717,720],{"number":683},"5",[89,685,687],{"id":686},"test-your-auth-flows-end-to-end","Test your auth flows end-to-end",[10,689,690],{},"Lovable's auth UI is typically correct; protected routes redirect unauthenticated users. The gap is usually the data layer: the frontend hides admin features, but the Supabase queries behind them have no server-side access check.",[10,692,693],{},"Two tests to run manually:",[10,695,696,699],{},[45,697,698],{},"Test 1: Unauthenticated data access","\nLog out. Open DevTools > Console. Run:",[198,701,705],{"className":702,"code":703,"language":704,"meta":203,"style":203},"language-javascript shiki shiki-themes github-light github-dark","const { data } = await window.supabase.from('your_table').select('*')\nconsole.log(data)\n","javascript",[14,706,707,712],{"__ignoreMap":203},[207,708,709],{"class":209,"line":210},[207,710,711],{},"const { data } = await window.supabase.from('your_table').select('*')\n",[207,713,714],{"class":209,"line":216},[207,715,716],{},"console.log(data)\n",[10,718,719],{},"If you get rows back, RLS isn't configured correctly.",[10,721,722,725],{},[45,723,724],{},"Test 2: Cross-user data access","\nCreate two test accounts. Log in as user A, note the ID of a record you own. Log in as user B. Try fetching that record by ID directly. If user B can access user A's data, your RLS policies have an IDOR gap.",[85,727,729,733,736,739],{"number":728},"6",[89,730,732],{"id":731},"scan-before-you-launch","Scan before you launch",[10,734,735],{},"A manual review catches obvious issues. A scanner catches the things you didn't know to look for: missing headers, exposed patterns in your JS bundle, DNS misconfigurations, open CORS policies.",[10,737,738],{},"Run a CheckYourVibe scan on your staging URL before pointing your domain at production. The scan takes under 2 minutes and produces a prioritized list of issues with fix instructions for each one.",[10,740,741,742,745],{},"If your app is still on a Lovable subdomain (something like ",[14,743,744],{},"yourapp.lovable.app","), scan that. If you've set up a custom domain, scan the custom domain.",[28,747,749],{"id":748},"before-you-go-live-the-short-checklist","Before you go live: the short checklist",[109,751,752,758,761,764,767,770],{},[42,753,754,755,757],{},"All secret keys are removed from ",[14,756,16],{}," variables and rotated",[42,759,760],{},"RLS is enabled on every Supabase table with at least one policy",[42,762,763],{},"Secret API calls go through Edge Functions or a backend",[42,765,766],{},"Security headers are in place (X-Frame-Options, HSTS, CSP at minimum)",[42,768,769],{},"Auth flows tested: unauthenticated users can't access data, users can't access other users' data",[42,771,772],{},"Security scan passed on your staging URL",[774,775,776],"tip-box",{},[10,777,778,779,782],{},"If you're not sure which tables exist in your Supabase project, look at the ",[45,780,781],{},"Table Editor"," in the dashboard. Every table listed there should have a green \"RLS enabled\" indicator before you go live.",[784,785,786,793,799,805,811],"faq-section",{},[787,788,790],"faq-item",{"question":789},"Is Lovable safe to deploy to production?",[10,791,792],{},"Lovable itself is a legitimate platform, but the apps it generates are not production-ready by default. You need to enable Supabase RLS, audit environment variables, and harden auth before launching with real users or real data.",[787,794,796],{"question":795},"What environment variables does Lovable expose?",[10,797,798],{},"Any variable prefixed with VITE_ gets compiled into your JavaScript bundle and is visible to anyone who opens DevTools. Supabase anon keys are fine as VITE_ variables because they're designed to be public with proper RLS. Service_role keys, Stripe secret keys, and OpenAI keys must never be VITE_-prefixed.",[787,800,802],{"question":801},"Do I need to enable RLS manually in Lovable?",[10,803,804],{},"Yes. Lovable creates Supabase tables but does not enable Row Level Security by default. Without RLS enabled and at least one policy in place, any authenticated user can read or write every row in those tables via the Supabase client.",[787,806,808],{"question":807},"How do I add security headers to a Lovable app?",[10,809,810],{},"If you're using Lovable's built-in hosting, headers are currently limited. For full control, deploy to Netlify or Vercel and add headers via netlify.toml or vercel.json. Both platforms let you set Content-Security-Policy, X-Frame-Options, and Strict-Transport-Security without touching code.",[787,812,814],{"question":813},"Can I deploy a Lovable app to Vercel or Netlify instead of Lovable hosting?",[10,815,816],{},"Yes. In Lovable, click Publish > Export to GitHub, then connect that repo to Vercel or Netlify. You get the same app with full control over environment variables and deployment configuration.",[818,819,820,826,831],"related-articles",{},[821,822],"related-card",{"description":823,"href":824,"title":825},"15-item checklist to run through before deploying any Lovable app to production.","/blog/checklists/lovable-security-checklist","Lovable Security Checklist",[821,827],{"description":828,"href":829,"title":830},"Step-by-step fix for Lovable apps with leaked API keys: find, rotate, and move secrets to Edge Functions.","/blog/how-to/fix-lovable-api-key-exposure","How to Fix Lovable API Key Exposure",[821,832],{"description":833,"href":834,"title":835},"The seven security risks that ship with most Lovable apps, each with a 5-minute fix.","/blog/vulnerabilities/lovable-common-issues","7 Common Lovable Security Risks",[837,838,841,845],"cta-box",{"href":839,"label":840},"/","Start Free Scan",[28,842,844],{"id":843},"about-to-deploy-a-lovable-app","About to deploy a Lovable app?",[10,846,847],{},"Scan it first. CheckYourVibe finds exposed API keys, open Supabase tables, and missing security headers in under 2 minutes.",[849,850,851],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":203,"searchDepth":216,"depth":216,"links":853},[854,855,863,864],{"id":30,"depth":216,"text":31},{"id":82,"depth":216,"text":83,"children":856},[857,858,859,860,861,862],{"id":91,"depth":222,"text":92},{"id":170,"depth":222,"text":171},{"id":280,"depth":222,"text":281},{"id":609,"depth":222,"text":610},{"id":686,"depth":222,"text":687},{"id":731,"depth":222,"text":732},{"id":748,"depth":216,"text":749},{"id":843,"depth":216,"text":844},"how-to","2026-06-05","Six steps to deploy your Lovable app without exposing API keys, leaving Supabase tables open, or shipping auth bypasses. Covers env vars, RLS, Edge Functions, and security headers.",false,"md",[871,872,873,874,875],{"question":789,"answer":792},{"question":795,"answer":798},{"question":801,"answer":804},{"question":807,"answer":810},{"question":813,"answer":816},"yellow",null,"lovable deploy securely, lovable production deployment, lovable security, lovable supabase rls, lovable environment variables, lovable api key exposure",{},"Deploy your Lovable app safely: audit env vars, enable Supabase RLS, move secrets to Edge Functions, and set security headers before going live.","/blog/how-to/deploy-lovable-securely","9 min read","[object Object]","HowTo",{"title":5,"description":867},{"loc":881},"blog/how-to/deploy-lovable-securely",[],"summary_large_image","Aq5dyXAXEZqCpDJuQAedJ15P_gpHZW3oK_7iHlya3CE",1784736388841]