---
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-10-03T06:01:02.931Z'
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. An HTTP origin on a LAN IP is not one, 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.
- **The Relying Party ID** must be the tunnel's public domain or a registrable-domain
  suffix of the origin. Passkeys registered against one RPID are rejected against another.
- **No shared network is required.** An iPhone on LTE, an Android on 5G and a laptop on
  corporate Wi-Fi all reach the same tunnel URL.
- **Baseline device coverage in 2026**: iOS 16+, Android 9+ with Google Play Services,
  macOS 13+, Windows 10 1903+.

## 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
  differs noticeably between these versions, so treat each one as its own test lane.
- **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. How Corbado can help

Tunnel-based testing catches developer-environment bugs: a wrong RPID, a missing
authorized origin, a TLS mismatch. The failure modes that dominate production
[passkey adoption](https://www.corbado.com/blog/passkey-adoption-business-case) sit outside its reach, because
those need real users on real devices: stale credentials after an OS reset, iCloud
Keychain sync lag, a [password manager](https://www.corbado.com/blog/passkeys-vs-password-managers) intercepting
the WebAuthn ceremony, a silent Safari abort on iOS or hybrid drop-off at the QR-scan
step. None of them reproduce on your laptop.

> 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://www.corbado.com/observe) instruments the full passkey ceremony across registration,
login and fallback, and it collects the client-side signal a tunnel can never give you.

[Video: Login funnel](https://www.corbado.com/videos/login-funnel.mp4) ([See Login Funnel in Corbado Observe](https://www.corbado.com/observe/login-funnel))

- The [login funnel](https://www.corbado.com/observe/login-funnel) reports completion, fallback, error and
  duration by browser, operating system, touchpoint and market, so the OS and browser
  combinations dragging your numbers down are named, with their volume attached.
- [Passkey Errors](https://www.corbado.com/observe/passkey-errors) clusters the same `DOMException` types listed
  in section 5 by outcome, attachment, sync state and error message, and jumps from a
  cluster straight to the matching flows.
- [User Debugging](https://www.corbado.com/observe/user-debugging) reconstructs one user's exact ceremony, device
  context and branch transitions from a support ticket, which is the production version of
  the local reproduction you do while developing.
- Telemetry is UUID-only with zero PII by design, so it can be switched on in a regulated
  production environment without a data-protection review of the payload.

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, and real-user passkey success
rates across the 40+ OS-browser combinations that show up once passkeys are in production
stay out of its reach, including the places where cross-platform completion still trails
the account-password flow it replaced. 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://www.corbado.com/observe) 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 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.
