// concept

OAuth callback URL — what it is, why it must be HTTPS, how to test locally

Updated 2026-05-09

What's the callback URL?

In OAuth, after the user authenticates with the provider (Google, Apple, GitHub), the provider redirects them back to your application — to a specific URL you configured ahead of time. This URL receives the authorization code (or token, depending on the flow) and is the bridge between "user logged in over there" and "user logged in over here".

That URL is the callback URL (sometimes "redirect URI" or "return URL").

1. user clicks "Sign in with Google" on https://your-app.com
2. browser → google.com/oauth/authorize?redirect_uri=https://your-app.com/callback&...
3. user signs in at google.com
4. google.com redirects → https://your-app.com/callback?code=...
5. your-app.com exchanges code for tokens

Why it must match exactly

The provider validates that the redirect_uri parameter matches one you pre-registered in their dashboard. Match means exact:

  • scheme (https:// not http://)
  • host (auth.example.com not example.com)
  • port (if non-default)
  • path (/callback not /oauth/callback)
  • trailing slash

If they don't match, the provider refuses to redirect and shows an error. This is a security feature — without it, an attacker could swap in their own URL and steal the auth code.

Why HTTPS is required

The auth code transits via the redirect URL. If the URL is HTTP, the code is in plain text on the wire and any network observer can grab it. Modern OAuth providers (Apple, Google, Auth0, Clerk) refuse to register HTTP URLs; some allow http://localhost as a special case for dev.

The localhost trap

Some providers allow http://localhost:3000/callback. Others don't. Apple Sign-In doesn't. Even ones that do, won't accept it from an iOS device — localhost on the phone is the phone's localhost, not your laptop.

Trying to develop against just http://localhost works for ~half of providers and fails confusingly for the other half.

The clean fix: tunnel + reserved subdomain

Use a reverse-tunnel service to expose your localhost on a real, certificate-valid public URL. Reserve a stable subdomain so you set the callback in the provider's dashboard once and never re-paste:

$ lrok reserve auth-dev
$ lrok http 3000 --hint auth-dev
  Forwarding https://auth-dev.lrok.io  →  http://127.0.0.1:3000

In the provider's dashboard:

  • Auth0: Allowed Callback URLs → https://auth-dev.lrok.io/callback
  • Clerk: OAuth redirect URIs → https://auth-dev.lrok.io/oauth/callback
  • Google: Authorized redirect URIs → https://auth-dev.lrok.io/api/auth/callback/google
  • GitHub: Authorization callback URL → https://auth-dev.lrok.io/api/auth/callback/github

The URL is permanent for as long as you keep the reservation. Across days, machines, restarts.

Multiple environments

Most providers accept multiple callback URLs per app. Convention:

https://your-app.com/callback           ← prod
https://staging.your-app.com/callback   ← staging
https://auth-dev.lrok.io/callback       ← local dev

Configure all three. Your app reads its own host at runtime and builds the redirect; the provider matches against the registered list.

Per-provider walkthroughs

Or the broader use-case guide: /use-case/expose-nextjs (which has the OAuth + lrok specifics for Next.js).

// shipping?

lrok gives your localhost a public HTTPS URL with a reserved subdomain on the free plan. $9/mo flat for unlimited.

Related