The first time you ask Rork to "build me an app with login," you'll likely see a line like process.env.EXPO_PUBLIC_RORK_AUTH_URL show up in the generated code, and your hand will pause on the keyboard wondering what value belongs there. I bumped into this on my first few Rork apps too — it's one of those quiet snags that doesn't look dangerous until it is.
The real trap isn't that you don't know the value. It's that the build will succeed even when the variable is empty. Anything in Expo's EXPO_PUBLIC_* namespace gets baked into the client bundle as undefined if you forget to set it, so the Rork preview keeps working as if everything were fine. You don't notice until you've shipped to TestFlight and your testers say "the login button does nothing."
This article walks through how I configure auth-related EXPO_PUBLIC_ variables in Rork-generated apps across local dev, preview, and production. It's the routine that finally stuck after I had shipped about thirty Rork apps and gotten tired of the same mistake.
Why Rork generates the auth URL as an environment variable
When Rork's AI writes an authentication flow, it doesn't hardcode the API endpoint. Instead it leaves it as EXPO_PUBLIC_* so you can swap it out from the outside. That's the right call: every developer's auth backend is different. Some people run Supabase, some Firebase, some a self-hosted Hono server on Cloudflare Workers. The URL is yours to provide.
So EXPO_PUBLIC_RORK_AUTH_URL is essentially a placeholder that says "fill this in before you ship." Skip it and the generated client just keeps POSTing to a non-existent domain, returning network errors with no clue why.
The EXPO_PUBLIC_ prefix itself is standard Expo behavior — anything with that prefix gets embedded in the client bundle and is publicly readable. Auth endpoint URLs are fine to expose, but never put API secret keys behind EXPO_PUBLIC_. That's the one rule worth tattooing somewhere visible.
What value should you actually put there
Across the apps I've shipped, the auth URL falls into one of three patterns. Pick the one that matches your setup.
Pattern A: Using Supabase Auth
This is the lowest-friction path and the one I usually recommend to indie developers. Use the project URL directly.
# .env
EXPO_PUBLIC_RORK_AUTH_URL=https://your-project-id.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEYIf Rork's generated code is already constructing paths like /auth/v1/token internally, the base project URL is the right value. Open the generated file and look for something like fetch(\${process.env.EXPO_PUBLIC_RORK_AUTH_URL}/auth/v1/token...`)`. If you see that pattern, the base URL is what you want.
Pattern B: Using Firebase Auth
Firebase usually expects you to use its SDK directly rather than hitting an endpoint URL. If the generated auth code uses the Firebase SDK, you probably won't need EXPO_PUBLIC_RORK_AUTH_URL at all — instead you'll be filling in EXPO_PUBLIC_FIREBASE_API_KEY, EXPO_PUBLIC_FIREBASE_PROJECT_ID, etc. Read the generated code first before you start setting variables blindly.
Pattern C: A self-hosted backend
If you're running your own auth endpoints with Hono / Express / tRPC, point the variable at your deployed domain.
# .env (development)
EXPO_PUBLIC_RORK_AUTH_URL=http://localhost:8787
# .env.production
EXPO_PUBLIC_RORK_AUTH_URL=https://api.your-app.comIf localhost doesn't work from a real device on the same Wi-Fi, swap it for your dev machine's LAN IP, or expose it temporarily with ngrok. Both are common workarounds.
Where local .env setup tends to break
The most frequent question I get about Rork environment variables is "I added it to .env but the app still shows undefined." The cause is almost always one of three things.
First, Metro needs a fresh restart. Editing .env while npx expo start is running won't pick up the change. Stop with Ctrl+C and run npx expo start -c to clear the cache. Without -c, additions sometimes propagate but edits don't.
Second, the file must be exactly named .env — not .env.txt, not env. macOS Finder occasionally appends .txt invisibly. Confirm with ls -la | grep env from the terminal.
Third, typos in the variable name. Rork generates process.env.EXPO_PUBLIC_RORK_AUTH_URL in screaming snake case. If you write a lowercase or otherwise mismatched name in your .env, JavaScript silently returns undefined and you spend an hour wondering why the request never fires.
If you're hitting other env-related symptoms, Why Rork apps fail to read environment variables covers the broader diagnostic pattern.
Setting it up for EAS Build and production
Your .env only works for the Rork preview and npx expo start. For EAS Build, you need to register the value with EAS Secrets.
# Register a production secret
eas secret:create --scope project \
--name EXPO_PUBLIC_RORK_AUTH_URL \
--value "https://api.your-app.com" \
--type string
# Verify
eas secret:listAs long as your eas.json production profile is configured to read from secrets, the value gets injected at build time. One important caveat: anything EXPO_PUBLIC_* gets baked into the client bundle, which means changing the value later won't update apps that are already installed. To rotate the URL, you have to ship a new build.
For staging vs. production, I keep them separated by varying env per profile in eas.json. If you're trying to wire this into CI, The Rork EAS Build CI/CD pipeline guide walks through the full GitHub Actions flow.
How to spot when the URL is wrong
The errors you'll see are predictable, so working backward from the message saves time.
Network request failed on login almost always means the URL is empty or unreachable. Drop a temporary console.log(process.env.EXPO_PUBLIC_RORK_AUTH_URL) near the auth call to confirm what's actually getting through.
CORS error (you'll see this on web targets) means the URL is reaching your backend but it's rejecting the origin. Supabase mostly handles this for you, so this almost always points to a self-hosted backend that needs CORS headers configured.
401 Unauthorized means the URL is fine but the credential is off. The companion variable — EXPO_PUBLIC_SUPABASE_ANON_KEY, your Firebase config, whatever — is what to check next. Common Rork × Firebase / Supabase auth errors lists the typical patterns.
When it works on simulator but not on a real device, that's almost always localhost not resolving from the device's perspective. Replace it with your machine's LAN IP (something like 192.168.1.10) and the request will go through.
A pattern I keep coming back to: per-environment overrides
Once you have more than one environment (dev, staging, prod), keeping the auth URL in sync becomes its own small chore. The cleanest setup I have found is to use eas.json profiles to inject different values, rather than swapping .env files manually.
{
"build": {
"development": {
"env": {
"EXPO_PUBLIC_RORK_AUTH_URL": "http://192.168.1.10:8787"
}
},
"preview": {
"env": {
"EXPO_PUBLIC_RORK_AUTH_URL": "https://staging-api.your-app.com"
}
},
"production": {
"env": {
"EXPO_PUBLIC_RORK_AUTH_URL": "https://api.your-app.com"
}
}
}
}For values you do not want committed to the repository (most secrets, but some teams treat backend hostnames as sensitive too), reference EAS Secrets instead of inlining the URL. The combination — non-sensitive URLs in eas.json, secrets in EAS Secrets — has been the most maintainable for me across small teams of one or two.
A small but important detail: if you change the value in eas.json and run an EAS Update rather than a full build, the JavaScript bundle picks it up, but variables that were resolved at native build time will not. Auth URLs are JavaScript-side, so EAS Update is safe for rotating them, but I still double-check by adding a tiny Settings screen in dev that displays process.env.EXPO_PUBLIC_RORK_AUTH_URL. It is the fastest way to confirm what your installed binary is actually pointing at.
Confirming the placeholder is gone before you submit
Apple and Google reviewers will not flag a placeholder URL by name — your binary just submits, hits a 404 on first login attempt, and gets rejected for "the app does not function as advertised." That feedback can take days to come back, which is why I added a small pre-submit check to my workflow.
Before every eas submit, I run:
# Confirm the production secret is set and present in the project
eas secret:list | grep EXPO_PUBLIC_RORK_AUTH_URL
# In a release build run on a simulator, log the value once on startup
# console.log("AUTH_URL:", process.env.EXPO_PUBLIC_RORK_AUTH_URL);If the logged value is undefined or the literal string EXPO_PUBLIC_RORK_AUTH_URL, something in the build chain has not picked up the secret. The thirty seconds it takes to run this check has saved me at least one App Store rejection.
What to do next
If you take one thing away from this, let it be this: open your .env right now and read the EXPO_PUBLIC_RORK_AUTH_URL line out loud. Most of the bugs I've seen with this variable get caught by a 30-second visual check — placeholder text still in place, missing https://, dev URL accidentally shipped to prod. Eyes catch it faster than debuggers.
If you're about to wire up Supabase auth in a Rork-generated app, the Rork × Supabase auth + realtime guide is a natural next read. It connects Rork's generated auth template to a working Supabase setup step by step.
The environment variables Rork hands you are basically a "fill me in" sticky note. Leave them and the preview keeps lying to you. Spend five minutes reviewing .env and EAS Secrets before each TestFlight build and you'll cut out one of the most embarrassing categories of indie-dev bugs.