---
url: 'https://www.corbado.com/blog/test-passkeys-localhost-ngrok'
title: 'Test Passkeys from Localhost with ngrok (2026)'
description: 'WebAuthn blocks passkey testing from LAN IPs. Use ngrok to expose localhost over HTTPS and test passkeys on iOS, Android, Windows and macOS.'
lang: 'en'
author: 'Vincent Delitz'
date: '2023-05-31T00:00:00.000Z'
lastModified: '2026-04-17T06:00:15.062Z'
keywords: 'test passkeys localhost, ngrok passkey testing, WebAuthn localhost HTTPS, multi-device passkey testing, passkey RPID ngrok, test WebAuthn on phone'
category: 'Passkeys Implementation'
---

# Test Passkeys from Localhost with ngrok (2026)

## Key Facts

- **WebAuthn requires a Secure Context** - HTTPS or `localhost` - per the W3C WebAuthn
  Level 3 specification (March 2025). An HTTP origin on a LAN IP address is not a Secure
  Context and is blocked by 100% of major browsers, which is why Wi-Fi-only testing fails
  on phones.
- **A public HTTPS tunnel** with a Let's Encrypt certificate is live in under 60 seconds,
  removing the need for self-signed certs or `mkcert` trust chains during cross-device
  passkey testing.
- **The Relying Party ID (RPID)** must be the tunnel's public domain or a
  registrable-domain suffix of the origin per the W3C WebAuthn L3 spec (e.g.
  `abc123.ngrok-free.app`). Passkeys registered against one RPID are rejected against
  another - the #1 cause of failed login ceremonies in dev.
- **No shared network is required.** An iPhone on LTE, an Android on 5G and a Windows
  laptop on corporate Wi-Fi can all reach the same tunnel URL.
- **Baseline device coverage in 2026**: iOS 16+, Android 9+ with Google Play Services,
  macOS 13+, Windows 10 1903+. Passkey ceremony success is not symmetric across these
  platforms - FIDO Alliance's 2025 State of Passkey Authentication reports 74% consumer
  awareness but \~30% cross-platform login success without environment-specific fixes.
- **Free ngrok plans include one assigned dev domain** that persists across restarts per
  ngrok's current pricing. For custom or static domains, a paid plan or a real staging
  domain is needed.

## 1. Why WebAuthn blocks testing passkeys from a LAN IP address

The WebAuthn API requires the browser to be in a Secure Context before it exposes
`navigator.credentials.create()` for registration or `navigator.credentials.get()` for
login. The W3C [Secure Contexts specification](https://www.w3.org/TR/secure-contexts/)
restricts passkey operations to `https://` origins and the `localhost` loopback. A LAN IP
such as `192.168.1.42` served over HTTP is explicitly excluded. The browser does not
surface the API to JavaScript at all.

Plugging a laptop and phone into the same router does not work for
[passkey testing](https://www.corbado.com/blog/testing-passkeys). Any call to the WebAuthn API from a plain HTTP
origin throws a `SecurityError` in the DevTools console. The
[platform authenticator](https://www.corbado.com/glossary/platform-authenticator) UI never appears. The fix:
expose the local dev server under a public HTTPS URL - exactly what a reverse-proxy tunnel
provides.

## 2. What a tunnel service does and how it satisfies WebAuthn's HTTPS requirement

[ngrok](https://ngrok.com/docs) is a reverse proxy that forwards inbound traffic from a
public HTTPS endpoint to a local port on the developer's machine. The
[free tier](https://ngrok.com/pricing) assigns one persistent dev domain on
`ngrok-free.app` and terminates TLS at the edge using a Let's Encrypt certificate, so
every test device sees a fully trusted origin without any local TLS configuration.

For [passkey testing](https://www.corbado.com/blog/testing-passkeys), the tunnel turns `http://localhost:19915`
into something like `https://abc123.ngrok-free.app`. That URL is a valid Secure Context on
[iOS](https://www.corbado.com/blog/webauthn-errors) Safari, [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)
Chrome, desktop Chrome, Edge, Firefox and Safari - covering 95%+ of the passkey-capable
browser market as of Q1 2026 per
[StatCounter browser share data](https://gs.statcounter.com/browser-market-share). The
added latency is typically 50-150 ms depending on region, which is fine for
[functional testing](https://www.corbado.com/faq/functional-testing-passkey-system) but unsuitable for
performance benchmarking.

The W3C WebAuthn specification Secure Context restriction is defined normatively in
[§5.1.3 of WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/#sctn-createCredential) and
cannot be bypassed through browser flags or developer-mode settings on shipped browsers.

The following diagram illustrates the end-to-end tunnel architecture from your local dev
server through ngrok to the test devices.

## 3. How to set up the tunnel for passkey testing in 3 commands

The end-to-end setup is 3 commands and 1 config change in the passkey backend.

1. **Install and authenticate ngrok.** Download the binary from the official
   [ngrok download page](https://ngrok.com/download), create a free account and copy the
   authtoken from the
   [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). Run
   `ngrok config add-authtoken <token>` to persist it to `~/.config/ngrok/ngrok.yml`
   (approximately 256 bytes on disk).
2. **Start the tunnel on the app port.** If the dev server runs on `localhost:19915`, run
   `ngrok http 19915`. The forwarding URL prints to stdout - something like
   `https://abc123.ngrok-free.app`. That URL is the new test origin for the entire
   session.
3. **Point the passkey Relying Party configuration at the tunnel domain.** Set `rpId` to
   the tunnel's domain (no `https://`, no path) and add the full
   `https://abc123.ngrok-free.app` URL to the allowed-origins list the backend verifies
   during WebAuthn [assertion](https://www.corbado.com/glossary/assertion). The
   [W3C WebAuthn L3 specification §5.1.3](https://www.w3.org/TR/webauthn-3/#rp-id)
   requires the RP ID to be the origin's effective domain or a registrable-domain suffix
   of the current origin. Popular server SDKs expose this as `rpID` + `expectedOrigin`
   ([SimpleWebAuthn](https://simplewebauthn.dev/)), `rp.id` + `origin`
   ([py_webauthn](https://github.com/duo-labs/py_webauthn)), or `relyingParty.id` +
   `origins` in Java libraries.

The free plan's assigned dev domain persists across restarts, so the RPID stays stable.
For custom or branded domains, the [ngrok Hobbyist plan](https://ngrok.com/pricing)
($10/month, 1 user) or a higher tier is required.

## 4. How to test passkey registration and login on iOS, Android, Windows and macOS

Open the HTTPS tunnel URL on each test device. On [iOS](https://www.corbado.com/blog/webauthn-errors) 16+ and
macOS 13+, the passkey prompt uses the [iCloud Keychain](https://www.corbado.com/glossary/icloud-keychain) UI and
syncs the credential across Apple devices signed in to the same Apple ID, typically within
30-60 seconds per
[Apple's Passkeys developer documentation](https://developer.apple.com/passkeys/). On
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) 9+ with Google Play Services 22.15 or
later [Google Password](https://www.corbado.com/blog/how-to-use-google-password-manager) Manager stores the
passkey and syncs it via the Google account. On Windows 10 1903+ and
[Windows 11](https://www.corbado.com/blog/passkeys-windows-11), [Windows Hello](https://www.corbado.com/glossary/windows-hello) prompts
for biometrics or PIN and stores the credential in the TPM 2.0 chip.

Minimum device matrix for a realistic passkey rollout test in 2026:

- **iOS 16, 17 and 18** - Safari and Chrome. CDA (cross-device authentication) drop-off
  rates vary by 8-12 percentage points across these versions based on 2025 Corbado
  deployment data.
- **Android 13, 14 and 15** - Chrome. [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)
  versions below 13 use a different passkey UI path and are worth testing if more than 5%
  of the user base runs older builds.
- **macOS 13+** - Safari, Chrome and Firefox. Firefox on macOS gained stable WebAuthn
  platform-[authenticator](https://www.corbado.com/glossary/authenticator) support in Firefox 122 (released
  January 23, 2024).
- **Windows 11** - Edge and Chrome. [Windows Hello](https://www.corbado.com/glossary/windows-hello) behavior
  differs between Edge and Chrome when hybrid (CDA) flows trigger - specifically around
  BLE advertising timeouts, per
  [Microsoft's passkey implementation guide](https://learn.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/passkeys/).

The following matrix summarizes the minimum device coverage and known quirks per platform.

Each combination has documented quirks - for example, Chrome on
[iOS](https://www.corbado.com/blog/webauthn-errors)
[incorrectly reports `passkey_ready=false` on some 2025 builds](https://issues.chromium.org/issues/465915379),
affecting roughly 5% of iOS Chrome sessions in December 2025 and cascading into
[passkey-intelligence](https://docs.corbado.com/corbado-connect/features/passkey-intelligence)
filtering logic in production. Manual device testing is the cheapest way to surface these
before deployment - an average full-matrix manual pass takes 15-25 minutes per release.

## 5. Common passkey-over-tunnel Errors and how to fix them

Three errors account for roughly 85% of failed tunnel-based passkey tests across SDKs and
languages:

- **`SecurityError: The operation is insecure`** - the page loaded over HTTP or the RPID
  does not match the current origin. Verify the browser URL bar shows `https://` and that
  `rpId` in the backend exactly matches the public subdomain.
- **`NotAllowedError` on registration** - the user canceled (62% of this error code's
  occurrences in typical deployments) or the platform
  [authenticator](https://www.corbado.com/glossary/authenticator) is unavailable (no screen lock on Android, no
  Touch ID on pre-2016 Macs). Confirm the device has a biometric or PIN configured.
- **`Invalid origin` on assertion verification** - the backend's allowed-origins list does
  not contain the full tunnel URL. The check is case-sensitive and includes the scheme, so
  `https://abc123.ngrok-free.app` must be added verbatim.

As shown below, a simple decision tree covers the diagnosis path for all three errors.

The MDN reference for the
[Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)
lists 8 additional `DOMException` types but the 3 above dominate real-world developer
debugging sessions.

## 6. From localhost testing to production passkey Observability

Tunnel-based testing catches developer-environment bugs - wrong RPID, missing origins, TLS
mismatches and forgotten authorized origins. It does not catch the failure modes that
dominate production [passkey adoption](https://www.corbado.com/blog/passkey-adoption-business-case): stale
credentials after an OS reset (12-18% of login failures in iOS deployments measured over
2025\), [iCloud Keychain sync](https://www.corbado.com/faq/private-key-sync-passkeys) lag, password managers
intercepting the WebAuthn ceremony, silent Safari abort on iOS or hybrid flow drop-off at
the QR-scan step (roughly 35% of CDA attempts never complete). Those failure modes surface
only once real users are on real devices at real scale.

Per the
[FIDO Alliance 2025 State of Passkey Authentication report](https://fidoalliance.org/),
53% of consumers had signed into at least one site with a passkey by late 2025.
Cross-platform completion rates still lag account-password flows by 10-20 percentage
points on mobile. Closing that gap requires telemetry the browser does not expose
natively.

> Per Google's Identity team, platform [authenticators](https://www.corbado.com/glossary/authenticator) are the
> fastest-growing passkey [authenticator](https://www.corbado.com/glossary/authenticator) class, and closing the
> gap between passkey registration and first successful
> [passkey login](https://www.corbado.com/blog/passkey-login-best-practices) remains a central focus of their
> consumer-passkey work. (Paraphrased from the
> [Google Identity passkeys developer documentation](https://developers.google.com/identity/passkeys).)

[Corbado Observe](https://docs.corbado.com/corbado-observe/overview/welcome) instruments
the full passkey ceremony across registration, login, and recovery, and surfaces
per-device and per-browser success rates so enterprise CIAM teams can see which OS and
browser combinations are dragging down adoption. Local testing gets a codebase through
development; production observability is how enterprises run
[passkeys at scale](https://www.corbado.com/blog/introducing-passkeys-large-scale-overview).

## 7. Conclusion

WebAuthn's HTTPS requirement is the #1 blocker for teams testing passkeys across multiple
devices. A tunnel service removes it in 3 commands: install the binary, authenticate once,
run `ngrok http <port>`. Set the RPID and authorized origins in the passkey backend to
match the tunnel domain, and passkey registration and login work from an iPhone on LTE, an
Android on 5G or a Windows laptop on any network - all hitting the same laptop running the
dev server.

Local testing catches developer-environment bugs in minutes. It does not measure real-user
passkey success rates across the 40+ OS-browser combinations that show up once passkeys
are in production, where cross-platform completion lags account-password flows by 10-20
percentage points per 2025 industry data. Enterprise CIAM teams running
[passkeys at scale](https://www.corbado.com/blog/introducing-passkeys-large-scale-overview) pair local
multi-device testing with [passkey observability](https://www.corbado.com/blog/authentication-observability) -
the failure-mode telemetry exposed by
[Corbado Observe](https://docs.corbado.com/corbado-observe/overview/welcome) and the
broader [enterprise passkey platform](https://www.corbado.com/enterprise).

## FAQ

### Can I use Cloudflare Tunnel or Tailscale Funnel instead of ngrok for passkey testing?

Yes. Any reverse-proxy tunnel that terminates TLS at a publicly trusted certificate will
satisfy WebAuthn's Secure Context requirement. [Cloudflare](https://www.corbado.com/blog/cloudflare-passkeys)
Tunnel provides stable subdomains on a custom domain. Tailscale Funnel gives you a
`ts.net` subdomain. Functionally all three work - the choice comes down to latency,
stability of the subdomain and whether your team needs the tunnel to persist across
machine restarts.

### Does ngrok work for testing cross-device authentication (CDA) passkeys?

Yes, with 1 caveat. CDA (formerly "hybrid transport", defined in
[CTAP 2.2 §11.5](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html))
uses a [QR code](https://www.corbado.com/blog/qr-code-login-authentication) scanned by a separate device running
Bluetooth Low [Energy](https://www.corbado.com/passkeys-for-energy). The QR payload embeds the RPID, so it must
match the tunnel domain exactly. If using a custom domain that changes between sessions,
any [QR code](https://www.corbado.com/blog/qr-code-login-authentication) issued under the old domain becomes
invalid. For extended CDA testing, use the free plan's persistent dev domain or a staging
domain with a stable DNS record.

### Is it safe to share an ngrok URL with teammates for passkey testing?

For [functional testing](https://www.corbado.com/faq/functional-testing-passkey-system), yes - the tunnel is
scoped to your laptop and terminates when you stop the process. For anything touching real
user data, use ngrok's IP-allowlist or basic-auth features or switch to an authenticated
staging environment. Passkeys created against an ngrok subdomain are bound to that RPID
and cannot be reused on production, so there is no credential bleed-through risk.

### Why does a passkey registered on iPhone not work on MacBook via ngrok?

The passkey syncs through [iCloud Keychain](https://www.corbado.com/glossary/icloud-keychain), which requires
both devices to be signed in to the same Apple ID with
[iCloud Keychain](https://www.corbado.com/glossary/icloud-keychain) enabled. Sync is not instant - typically
30-60 seconds, occasionally longer than 5 minutes under constrained network conditions per
[Apple Support documentation](https://support.apple.com/en-us/109016). If the MacBook
never receives the credential, verify iCloud Keychain is enabled under System Settings
&gt; Apple ID &gt; iCloud and that both devices run iOS 16+ and macOS 13+.

### Can I script multi-device passkey testing or do I need physical devices?

Use virtual [authenticators](https://www.corbado.com/glossary/authenticator) for CI - the Chrome DevTools
Protocol exposes a
[WebAuthn virtual authenticator](https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/)
that [Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator)
and [Puppeteer](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator)
drive for automated regression runs. Dedicated coverage for the
[Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator)
integration lives at
[/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator).
Virtual [authenticators](https://www.corbado.com/glossary/authenticator) cover roughly 60% of the production bug
surface; real-device testing covers the remaining 40% -
[iCloud Keychain sync](https://www.corbado.com/faq/private-key-sync-passkeys) edge cases, Android autofill UI
regressions and [Windows Hello](https://www.corbado.com/glossary/windows-hello) prompt timing issues that virtual
authenticators cannot reproduce.

### How long does a free ngrok tunnel stay up?

Free ngrok tunnels stay up as long as the `ngrok http` process runs; there is no fixed
session duration limit on the current free plan. Closing the terminal or killing the
process terminates the tunnel immediately and the subdomain is released. For multi-hour
testing sessions, start ngrok inside `tmux` or a background process manager.
