A Lovable app can work perfectly in preview and still be unsafe or broken in production because environment variables and secrets solve different problems.
The practical rule is simple: if the browser needs a value to run, assume a visitor can see it. If possession of a value would let somebody spend money, access private data, impersonate your backend or bypass normal permissions, it belongs behind a server boundary.
That distinction matters before you connect payments, email, AI APIs, storage or customer data to an AI-built product.
The decision: public configuration or secret?
Use this test before deciding where a value belongs.
| Value | Browser-safe? | Where it belongs |
|---|---|---|
| Public app URL | Usually | Client configuration |
| Supabase project URL | Yes | Client configuration |
| Supabase publishable/anon key with correct RLS | Yes | Client configuration |
| Feature flag with no security authority | Usually | Client configuration |
| OpenAI or other paid API key | No | Server-side secret |
| Resend/email provider API key | No | Server-side secret |
| Payment secret key or webhook signing secret | No | Server-side secret |
| Supabase secret/service-role credential | No | Server-side secret |
The important question is not whether a value is written in .env. It is where that value is consumed.
A frontend build eventually has to send browser-required values to the browser. Renaming a secret or hiding it behind an environment-variable name does not change that boundary.
How Lovable handles authenticated APIs
Lovable’s current integration documentation says authenticated external APIs should use Lovable Cloud secrets and an Edge Function so credentials are not exposed to the client.
That architecture is worth understanding rather than treating it as platform magic:
browser → your server/Edge Function → third-party API
The browser sends the user’s request to code you control. The server-side function reads the credential, calls the external service and returns only the response the user is allowed to receive.
The dangerous version is:
browser + private API key → third-party API
Anyone who can inspect the browser’s JavaScript or network requests may be able to recover that credential.
Supabase keys are a useful example
Not every visible key is a leaked secret.
Supabase’s current environment-variable documentation distinguishes publishable keys from secret keys. Publishable keys are designed for public clients when Row Level Security and authorization policies are correctly configured. Secret keys are backend credentials and can bypass Row Level Security, so they must not be shipped to the browser.
This is why searching a production bundle for the word key and panicking is not enough. You need to classify what authority each credential carries.
A public Supabase key plus weak RLS can still create a serious data problem. The key being public is expected; the missing authorization boundary is the failure.
If you are unsure about that distinction, read Is the Supabase anon key safe to expose?.
Preview working does not prove production is configured
One of the easiest mistakes in AI-assisted development is treating preview as production evidence.
Your preview may have the right secret, test credential, callback URL or connected backend while the published environment does not. The opposite can happen too.
Before launch, make a small configuration inventory:
| Check | Preview | Production |
|---|---|---|
| Backend/project ID | Verify | Verify |
| Public app URL | Verify | Verify |
| Auth redirect URLs | Verify | Verify |
| Server secrets | Verify names | Verify names |
| Payment mode | Test | Live when ready |
| Webhook destination | Test endpoint | Production endpoint |
| Email sender/domain | Test | Verified production sender |
Do not copy every development value into production blindly. The goal is parity of required configuration, not necessarily identical credentials.
Supabase also documents that secrets can be environment-specific and, in branching workflows, values set for one branch are not automatically available in another. That is the right mental model for production generally: deployment environments should be explicit, not assumed.
Search the code before adding more secrets
When a production feature says a variable is missing, adding values at random can make the system harder to understand.
First search the repository for how configuration is actually referenced. Look for patterns such as import.meta.env, process.env, Deno.env.get, API client initialization and hard-coded provider keys.
Then classify every referenced value:
- Does browser code need it?
- Does it grant privileged authority?
- Which environment supplies it?
- Is the variable name identical in code and configuration?
- Does changing it require a new build or only a runtime secret update?
That inventory usually reveals whether the problem is a missing variable, wrong name, wrong environment or wrong trust boundary.
What if the secret is already in GitHub or the browser?
Treat an exposed real secret as compromised.
Deleting the current line is not enough if the credential has already been published, committed or shared. Rotate or revoke it at the provider, replace it in the server-side secret store and redeploy the application.
Then verify the old credential no longer works.
Also check Git history and build output where appropriate. Moving the current copy out of a React component does not retroactively invalidate an API key that was committed yesterday.
Test the live boundary, not just the interface
A production verification should prove more than “the page loaded.”
For a feature that calls an authenticated API, test the actual live journey:
- Open the production URL in a clean browser session.
- Trigger the feature as a normal user.
- Confirm the browser does not receive the private credential.
- Confirm the server function receives the request.
- Confirm the external provider accepts the server-side credential.
- Confirm failure states do not leak sensitive values.
- Confirm the resulting business state is correct.
For payments, email, authentication and database writes, also verify the downstream result. A success toast is not proof that money was recorded, mail was delivered or data was saved correctly.
This is one reason my AI-built app production checklist treats production readiness as a system problem rather than a UI milestone.
A safer prompt for AI builders
Instead of asking an AI builder to “add this API key,” give it the security boundary explicitly:
Integrate this authenticated API. Store the credential only in the server-side secret store. Do not expose it through client environment variables, frontend source, network responses or Git. Route authenticated calls through a server/Edge Function and tell me which production secret name I must configure.
That prompt does not replace review, but it gives the builder a much better constraint than simply pasting a credential into chat and hoping it chooses the correct architecture.
Before you launch
Your production configuration is ready when you can answer four questions clearly:
- Which values are intentionally public?
- Which credentials are privileged secrets?
- Which runtime consumes each value?
- Where is each production value configured and rotated?
If those answers are fuzzy, adding more features makes the eventual debugging harder.
For a small prototype, fixing the boundary may take minutes. For an app with authentication, payments, webhooks and several integrations, it is worth auditing configuration before real users arrive.
If your AI-built product has reached the point where preview works but production configuration, permissions and integrations are becoming difficult to reason about, see my MVP development service or contact me. I can help separate what needs a small configuration fix from what actually needs architectural work.



