
Create a stable public test origin for passkey registration and authentication
WebAuthn depends on more than reaching a local application over HTTPS. The browser, relying party configuration, application origin, RP ID, challenge, and stored credential must all agree. In this guide, we explain how to expose a local WebAuthn application through a Localtonet HTTP tunnel, configure it for the assigned public HTTPS hostname, register an appropriate test credential, verify authentication, and diagnose common origin or RP ID failures without disabling browser security protections.
๐ What's in this guide
Why passkeys can fail when localhost works
A WebAuthn test can work perfectly at http://localhost and then fail after the same application is opened through a public HTTPS URL. That result is often mistaken for a tunnel, certificate, browser, or authenticator problem. In many cases, the actual cause is that the public address creates a different WebAuthn security context and a different relying-party relationship.
Browsers treat the origin as a combination of scheme, hostname, and effective port. For example, a local development page and a public HTTPS page do not have the same origin, even when both requests eventually reach the same application process. The browser sees the address in its own location bar, not the internal destination behind the tunnel.
WebAuthn also uses a relying party identifier, commonly called the RP ID. The RP ID is a domain value associated with a credential at registration time. Its hash is included in authenticator data, and the browser and server enforce its relationship to the page that initiates the ceremony. A credential registered for localhost is therefore not automatically a credential for an unrelated public hostname.
Localtonet solves the network reachability part of the workflow. Our client creates an outbound connection to a Localtonet relay, and an HTTP tunnel can provide a public HTTPS address that forwards requests to the local IP address and port you configured. You do not need inbound router port forwarding, a public IP address, firewall changes, or a VPN for this tunnel workflow.
The tunnel does not rewrite WebAuthn's security model. Your application must treat the assigned public hostname as a real test origin, validate that origin on the server, use a compatible RP ID, and register a credential under that configuration. This separation is important: successful HTTPS delivery proves that the browser can securely reach the page, but it does not prove that an existing credential is valid for the new relying party.
Keep local and tunneled test credentials conceptually separate. A credential created with an RP ID of localhost should not be expected to authenticate under the tunnel hostname. Use a fresh test credential after configuring the public origin and RP ID.
Understand secure contexts, origins, and RP IDs

Three related but distinct checks shape a WebAuthn ceremony. Understanding where each check occurs makes troubleshooting considerably faster.
Secure context does not mean matching identity scope
HTTPS protects the browser-to-public-endpoint connection and allows the page to qualify as a secure context. It does not make two hostnames interchangeable. If registration happened on one origin and authentication is attempted on another, the browser can reject the operation, offer no credential, or return a response that the server rejects.
This distinction explains a common sequence: the application loads over the tunnel, navigator.credentials.create() or navigator.credentials.get() is available, and an authenticator prompt may even appear, but the ceremony still fails. Secure-context eligibility is only the first requirement.
What belongs in an origin
Configure the expected origin as the exact browser-facing origin. If the browser opens a public address over HTTPS using the default HTTPS port, the expected origin is generally the scheme plus the public hostname. Do not add a path such as /login or /webauthn/register. Paths identify application routes, not origins.
A port matters when it is a non-default part of the browser-facing URL. The local application's internal port does not automatically belong in the public origin. The authoritative value is what the browser reports through window.location.origin.
What belongs in an RP ID
An RP ID is a hostname-like domain value. It must not include https://, a port, a slash, or a route. For the least ambiguous development setup, use the exact public hostname shown in the browser as the RP ID, provided your WebAuthn implementation and the hostname satisfy the browser's RP ID rules.
A parent domain can sometimes be used as an RP ID when it is a valid registrable domain suffix of the current hostname and is under your control. That is useful when multiple subdomains intentionally share a relying-party scope. It should not be assumed for an assigned tunnel hostname. Using the exact assigned hostname avoids accidentally claiming a parent-domain scope that the browser will reject or that you do not control.
| Value | Example form | WebAuthn role | Common mistake |
|---|---|---|---|
| Local application URL | http://127.0.0.1:LOCAL_PORT |
Internal target reached by the Localtonet client | Using this internal URL as the expected browser origin |
| Public origin | https://PUBLIC_HOSTNAME |
Expected origin for the tunneled browser ceremony | Adding a route, using HTTP, or retaining the localhost origin |
| RP ID | PUBLIC_HOSTNAME |
Domain scope to which the credential is registered | Including a scheme, port, path, or unrelated domain |
| Registration route | /webauthn/register |
Application endpoint or page path | Confusing an application path with an origin or RP ID |
| Credential identifier | Binary identifier represented by the application as required | Identifies the registered credential | Changing its byte representation or encoding between ceremonies |
The placeholders above are conceptual. Replace LOCAL_PORT and PUBLIC_HOSTNAME with values from your own application and active tunnel. Do not copy a sample domain, token, credential ID, or endpoint into a real configuration.
Prerequisites for the test workflow
Before exposing the application, establish a working local registration and authentication path. A tunnel adds another network boundary and another origin, so beginning with an unverified local implementation makes unrelated errors harder to distinguish.
- A local web application with server-side WebAuthn registration and authentication verification.
- A known local IP address and port that can be reached from the device running the Localtonet client.
- A Localtonet account and an installed client on that device.
- A device-specific Localtonet authentication token selected securely through the current product workflow.
- An available Localtonet relay server or region selected from the current dashboard rather than copied from an article.
- A WebAuthn-capable browser and an available authenticator, such as a platform authenticator, security key, or compatible passkey provider.
- Access to the application's WebAuthn settings so that the expected origin and RP ID can be changed for the public test environment.
- Access to browser developer tools and server logs that do not expose secrets or complete credential material.
Your exact framework may name the relevant settings differently. Common concepts include an expected origin, RP ID, relying-party name, public base URL, trusted host, proxy trust configuration, callback URL, or allowed web origin. This guide intentionally does not invent provider-specific option names. Map the concepts to settings documented by your selected WebAuthn server library, identity provider, and web framework.
A public tunnel makes the selected local service reachable from the internet while the client is connected and the tunnel is running. Use test accounts and test data, remove debug endpoints, protect administrative routes, apply least privilege, and stop the tunnel when testing ends. Never place Localtonet device tokens, session secrets, private keys, raw challenges, or credential private material in source code, screenshots, URLs, or public logs.
Prepare and verify the local WebAuthn application
Confirm that the complete application flow works locally before introducing the public hostname. The goal is not merely to display a passkey prompt. A complete registration must persist a usable public-key credential record, and a complete authentication must verify a fresh assertion and establish the intended application session.
Check the local application routes
A typical implementation has server endpoints that begin and finish each ceremony. Registration begins by obtaining creation options from the server and finishes by sending the authenticator response back for verification. Authentication follows the same pattern with request options and an assertion response. Your routes and payload structure depend on the framework, so retain the implementation established by your library rather than substituting generic route names.
Verify that static assets, API requests, forms, and client-side navigation all use relative URLs or an environment-aware public base URL. Hardcoded localhost API addresses are a frequent reason that the page loads through a tunnel while the ceremony's supporting requests still go to the developer's local origin.
Confirm local listening behavior
The Localtonet client must be able to reach the target IP and port. If the client and web application run on the same machine, a loopback target may be appropriate. If they run on different devices or containers, the service must listen on an address reachable from the client device, and host firewall policy must permit that local connection.
Do not change a development server to listen on every network interface unless that is necessary and understood. Prefer the narrowest listening configuration that still lets the Localtonet client reach the service. The public tunnel and local network exposure are separate concerns.
Inspect browser-visible values
The following browser console expressions provide a quick diagnostic after loading either the local or tunneled page:
window.isSecureContext
window.location.origin
window.location.hostname
window.location.port
typeof window.PublicKeyCredential
For the tunneled page, window.isSecureContext should be true, the origin should show the public HTTPS origin, and the hostname should match the domain value you intend to use when configuring the RP ID. The presence of PublicKeyCredential confirms browser API exposure, but it does not prove that registration or authentication will pass validation.
Preserve challenge state
Ensure that the server stores or otherwise securely correlates each challenge with the correct ceremony and user session. When a tunnel changes the browser-facing scheme and host, session cookies can behave differently because cookie domain, Secure, and SameSite attributes affect whether the browser returns them.
If the request that finishes registration cannot find the session that initiated it, the error may appear to be a challenge mismatch even though the browser generated a valid credential. Inspect the browser's cookie storage and the request headers, then verify that your public-environment cookie configuration is deliberate. Do not remove cookie protections globally simply to make a test pass.
Create a public HTTPS endpoint with Localtonet

Use an HTTP tunnel for a browser-based local web application. Localtonet forwards the public endpoint to the local IP address and port configured for the tunnel. HTTP tunnel process types can provide a public HTTPS address using a random subdomain, a supported custom subdomain, or a custom domain.
The available process types, server choices, and plan-specific capabilities can vary. Use the values currently displayed in your dashboard. If you choose a custom domain, check the current documentation for its DNS requirements rather than assuming a record type or target.
Install and run the Localtonet client
Run the Localtonet client on the machine that hosts the application or on another device that can reach the application's local IP address and port. The client establishes the outbound connection used by the tunnel.
Authenticate or select the client device
Use the device-specific authentication token through the current Localtonet workflow. Treat the token as a secret. Do not paste it into application code, browser-side JavaScript, public logs, or tutorial screenshots.
Select an available relay server
Choose a server or region from the values currently available in the Localtonet dashboard. Do not hardcode a server code copied from an older guide because available values can change.
Create the HTTP tunnel configuration
Select the HTTP tunnel workflow, choose the appropriate process type, and configure the local IP address and port that serve your WebAuthn application. Confirm locally that this exact target responds before continuing.
Start the tunnel
Creating a tunnel does not start it. Use the Start action and wait until the selected client is connected and the tunnel is running. The endpoint remains available only while the device is connected and the tunnel is active.
Open and record the assigned public address
Open the assigned HTTPS URL in the test browser. Record the exact browser origin and hostname because those are the values that matter to the WebAuthn configuration. Stop and resolve ordinary routing or application errors before testing passkeys.
For the current dashboard workflow, consult our Localtonet HTTP tunnel documentation. The documentation supplements the concepts here, but your active dashboard remains the authoritative place to obtain the device, server, process type, and assigned public address values for your account.
If the home page loads through HTTPS, networking is working at a basic level. WebAuthn can still fail because the application expects localhost, the RP ID is incompatible with the public hostname, session cookies are missing, or the credential was registered under a different relying party.
Align the application origin and RP ID
Once the tunnel is running, configure the application as if the public address were a distinct test deployment. Avoid trying to make one ambiguous set of values silently cover localhost, temporary tunnel hosts, staging, and production. Explicit environment configuration is easier to audit and less likely to weaken validation.
Set the expected origin
Copy the exact value reported by window.location.origin on the tunneled page and use it as the expected public origin in your WebAuthn server configuration. It will normally have this conceptual form:
https://PUBLIC_HOSTNAME
Do not use the internal target such as http://127.0.0.1:LOCAL_PORT. That is where Localtonet forwards the request, but it is not the origin from which the browser initiated WebAuthn.
Some libraries support a controlled list of expected origins for development and deployment environments. If yours does, list only the exact origins that you actually use. Do not implement origin acceptance with arbitrary suffix matching, unchecked request headers, or a wildcard.
Set the RP ID
Use the public hostname without a scheme, port, or path:
PUBLIC_HOSTNAME
The same RP ID must be used when generating registration options, verifying the registration response, generating authentication options, and verifying the authentication response. A mismatch at any stage can produce an empty credential picker, a browser exception, or server rejection.
If you change to another generated hostname later, treat it as a new WebAuthn relying-party environment unless the old and new hosts intentionally use a valid shared RP ID under a domain you control. Credentials registered against the earlier exact hostname should not be expected to work on the replacement hostname.
Configure a public base URL when needed
Frameworks often construct absolute URLs, redirects, cookie settings, or security checks from request information. A reverse-proxy deployment can cause an application to infer its internal HTTP target instead of the external HTTPS address. When supported, set an explicit public base URL for the test environment.
If your framework requires trusted-proxy configuration, follow that framework's documentation and trust only the necessary proxy path. We do not provide a universal setting because header names, proxy trust models, and security consequences differ by framework. Never trust forwarded headers from every source merely to suppress an origin error.
Review identity-provider allowlists separately
If an external identity system participates in the flow, it may maintain allowed web origins, callback URLs, or RP ID settings in addition to your application's WebAuthn configuration. Add the exact public values where required by that provider. An allowed callback URL and a WebAuthn RP ID solve different problems, so configuring one does not automatically configure the other.
Browser requests to your own challenge endpoints can also be affected by CORS if the front end and API use different origins. WebAuthn itself is not fixed by adding permissive CORS headers. Configure only the API origins required by your architecture, and keep WebAuthn origin verification intact.
Register a fresh test credential
After changing the expected origin and RP ID, start a new registration ceremony from the public page. Do not rely on a passkey registered through localhost. Store the newly returned credential with enough context to distinguish it from local, staging, and production credentials.
A useful development record can label the environment and display a shortened, non-sensitive identifier for operational clarity. Do not log complete attestation objects, assertions, session secrets, or unnecessary personally identifying authenticator data.
Verify registration and authentication end to end

Test registration and authentication as separate ceremonies. Registration proves that the application can create and validate a new public-key credential for the public RP ID. Authentication proves that the browser can discover or use that credential and that the server can validate a fresh signed assertion.
Registration verification checklist
- Open the active Localtonet HTTPS address in the browser.
- Confirm that the application page, registration-options request, and registration-verification request all use the intended public environment.
- Check that
window.isSecureContextistrue. - Confirm that the registration options contain the intended RP ID through your application's safe diagnostic tooling.
- Start registration and complete the authenticator prompt.
- Verify that the server finds the same fresh challenge it issued for the current session.
- Verify that the server accepts the public origin and the intended RP ID.
- Confirm that the credential public key and required metadata are stored successfully.
Do not count a successful browser prompt as completed registration. The operation is complete only after the server validates and stores the result. If the browser succeeds but the server responds with an error, inspect the finish request and the server's structured validation result.
Authentication verification checklist
- Start a fresh authentication ceremony from the same public origin used for registration.
- Confirm that the server generates a new challenge and associates it with the correct session.
- Check that the authentication options use the same RP ID as registration.
- Complete the authenticator prompt using the newly registered test credential.
- Confirm that the server validates the challenge, origin, RP ID hash, signature, and required authenticator flags.
- Verify that the application establishes the intended authenticated session.
- Repeat after signing out or in a fresh browser session to ensure the result does not depend on stale application state.
For discoverable credentials, the browser may offer an account without the application first sending a credential allowlist. For non-discoverable flows, the server may need to identify the user and supply eligible credential IDs. Follow the model supported by your WebAuthn library and authenticator. Do not switch models while diagnosing an origin problem.
Test the negative cases
A secure implementation should reject invalid input, not merely accept the happy path. In a controlled test environment, confirm that the server rejects an expired or unknown challenge, an unexpected origin, a response for the wrong RP ID, a credential not associated with the account, and a replayed response.
Do not generate negative tests by weakening browser settings or modifying production data. Exercise validation through test fixtures, library-supported test tooling, or isolated test accounts. Record enough server-side reason information to diagnose the failure, but return restrained errors to the browser so that internal security details are not exposed publicly.
Troubleshoot registration and login failures
Begin at the earliest failing layer. First establish that the page is loaded from the expected public URL. Next verify browser eligibility and configuration values. Then inspect challenge and session continuity. Finally investigate authenticator selection or provider-specific requirements.
| Symptom | Likely area | What to inspect | Safe corrective action |
|---|---|---|---|
| WebAuthn API is unavailable | Secure context or browser support | window.isSecureContext, public scheme, browser capability |
Use the assigned HTTPS address and a supported browser context |
SecurityError or domain-related browser rejection |
RP ID compatibility | Current hostname and the RP ID sent in options | Use a valid RP ID compatible with the public hostname |
| Authenticator prompt appears, but registration fails | Server verification | Expected origin, RP ID, challenge, session, and payload decoding | Correct the server's public-environment configuration |
| No credential is offered during login | Credential scope or selection | Where the credential was registered, RP ID, account, and allowlist | Register a fresh credential under the public RP ID or correct selection data |
| Challenge is missing or mismatched | Session and cookie continuity | Cookie domain, Secure, SameSite, expiry, and session storage |
Configure cookies for the public environment without disabling protections globally |
| Page loads, but API requests still target localhost | Front-end configuration | Network panel, hardcoded URLs, environment variables, public base URL | Use relative URLs or an explicit public API origin appropriate to the architecture |
| Application redirects to an internal HTTP URL | Proxy or base-URL inference | Generated redirects and framework proxy configuration | Set the documented external URL or trusted-proxy configuration narrowly |
| Authentication worked before the tunnel hostname changed | RP ID change | Old registration RP ID and current public hostname | Restore a stable valid hostname or register a new test credential |
Origin mismatch
Compare three values directly: window.location.origin, the expected origin configured on the server, and the origin reported in the WebAuthn client data being verified by your trusted library. They must represent the intended public origin. Avoid manual string normalization that strips schemes, ports, or meaningful distinctions.
If the server expects localhost while the browser reports the public HTTPS origin, change the environment configuration. Do not configure the server to accept every origin. If multiple exact test origins are necessary and your library supports a list, keep the list explicit and environment-specific.
RP ID mismatch
Compare window.location.hostname with the RP ID generated by the server. The RP ID must be a domain value that the browser permits for that hostname. Remove any scheme, path, and port. Then confirm that registration and authentication use the same value.
An RP ID error can surface before the authenticator is used, so no browser prompt may appear. It can also surface during server verification through an unexpected RP ID hash. The exact exception text varies by browser and library, so diagnose the values rather than relying only on the message wording.
Credential registered on localhost
If a passkey exists but is not offered on the tunnel page, determine where it was registered. A credential created for localhost belongs to that RP ID. It is not migrated merely because the same source code is now reachable through HTTPS at another hostname.
Configure the public environment first, then register a fresh test passkey through the public page. Keep both credentials if you need to test both environments. Delete only the intended test credential from the application and authenticator when cleaning up.
Challenge or session mismatch
Use the browser network panel to follow the begin and finish requests. Confirm that they use the same intended environment and that the session cookie is present where required. Check whether the application restarted between requests, whether an in-memory challenge store was cleared, and whether multiple application instances use shared session state.
Also verify binary encoding boundaries. WebAuthn APIs use byte arrays in several fields, while JSON transports commonly require a text representation. Use your library's supported conversion helpers. A base64 and base64url mismatch, altered padding, or accidental text encoding can corrupt a challenge or credential identifier even when the visible strings look similar.
Browser or passkey-provider conflicts
A platform authenticator, operating-system passkey interface, browser password manager, and third-party password-manager extension can all participate in credential selection. If the wrong interface appears or no expected credential is visible, retry in a clean browser profile with a single intended provider enabled. Also test with another supported browser to determine whether the problem is provider-specific.
Keep that diagnostic separate from RP ID analysis. If the configured RP ID is invalid, changing password managers will not fix it. If the RP ID and origin are correct but one browser-provider combination behaves differently, document the exact browser version, operating system, authenticator type, and extension state for review.
Avoid experimental browser preferences, insecure-origin overrides, certificate bypasses, disabled origin validation, or broad CORS settings as a substitute for correcting the application. Such changes can hide the real defect and produce a test environment that does not represent production security behavior.
Tunnel is stopped or the device is disconnected
A Localtonet tunnel is reachable only while the selected client device is connected and the tunnel is running. If the public page suddenly becomes unavailable, confirm both states before changing WebAuthn code. Creating a tunnel configuration alone does not start it.
If you delete and recreate a tunnel, the assigned address can differ depending on the process type and configuration. Recheck the actual public origin and hostname. A changed hostname can require updated application settings and a newly registered test credential.
Operate the WebAuthn test environment safely
A public passkey test endpoint is still an authentication service exposed to internet traffic. Keep the environment isolated from production identities and data. Use a dedicated test account set, a separate database where practical, and credentials created specifically for testing.
Use a stable hostname deliberately
WebAuthn credentials are easier to test when the public hostname remains stable. Localtonet HTTP tunnels support random subdomain, custom subdomain, and custom domain process types, but availability can vary. If a supported stable hostname option is important to repeated testing, select it intentionally and verify its current requirements in our dashboard and documentation.
A custom domain can make the test environment resemble a controlled deployment and can support an RP ID under a domain you administer. Exact DNS instructions are time-sensitive, so follow current Localtonet documentation rather than copying a generic DNS record from another service or an older article.
Separate environments explicitly
Maintain separate configuration values for local development, public tunnel testing, staging, and production. At minimum, distinguish the expected origin, RP ID, public base URL, cookie policy, credential database, and identity-provider allowlists where applicable.
Do not automatically derive trusted security values from an unvalidated incoming Host header. Security-sensitive configuration should come from controlled environment settings or a framework mechanism designed for trusted proxy deployments.
Protect logs and diagnostic output
Log high-level reasons such as origin mismatch, unknown credential, expired challenge, or signature verification failure. Avoid recording complete WebAuthn payloads unless your reviewed development process specifically requires them and protects the output. Authentication tokens, session cookies, Localtonet device tokens, credential private material, and user secrets must never be included.
Stop access when the test is complete
Stop the tunnel when remote testing ends, or delete it if it is no longer required. Stopping preserves the distinction between configuration and active exposure. Deleting removes a configuration you do not intend to reuse. Also remove obsolete test origins from application and identity-provider allowlists, and clean up test credentials according to your retention policy.
Record the tested public origin, RP ID, browser, operating system, authenticator type, application version, and whether registration and authentication both passed. Do not record secrets. This small compatibility record makes future regressions easier to reproduce when browsers, authenticators, or application dependencies change.
Frequently asked questions
Why does my passkey work on localhost but not through the HTTPS tunnel?
Localhost and the public tunnel URL are different origins and normally have different RP IDs. A credential registered for localhost is not automatically valid for the public hostname. Configure the expected public origin and RP ID, then register a fresh test credential through the public page.
Does HTTPS alone make a WebAuthn deployment valid?
No. HTTPS supplies the secure browser context required for public WebAuthn use, but the server must still validate the challenge, expected origin, RP ID relationship, credential, signature, and relevant authenticator data. Session and cookie continuity must also work.
Should the RP ID include https:// or the tunnel port?
No. An RP ID is a domain value. It does not include a URL scheme, port, path, query string, or fragment. The expected origin is the separate value that includes the scheme and, when applicable, a non-default browser-facing port.
Can I reuse a credential after the assigned public hostname changes?
Not when the credential was registered for the old exact hostname and the new hostname does not share an intentionally configured valid RP ID. In the normal generated-hostname workflow, update the application configuration and register a new test credential for the new hostname.
Why does the authenticator prompt appear if the server later rejects registration?
The browser and authenticator can complete their part before the server performs final validation. The server may then reject an unexpected origin, incorrect RP ID hash, missing challenge session, malformed encoding, unsupported algorithm, or another verification condition. Inspect the server's structured validation result.
Should I disable a browser WebAuthn preference to make testing work?
No. First correct the origin, RP ID, challenge, session, and provider configuration. A clean browser profile or another supported browser can help isolate an extension or passkey-provider conflict, but disabling security checks is not a valid replacement for correct WebAuthn configuration.
Does Localtonet change my application's WebAuthn RP ID automatically?
No. Localtonet provides the public endpoint and forwards traffic to the configured local target. Your application or identity system remains responsible for generating and validating the expected WebAuthn origin and RP ID.
Is a Localtonet HTTP tunnel the same as a VPN?
No. In this workflow, an HTTP tunnel publishes a selected local web service through a public address. It is not a VPN. Localtonet VPN Manager is the separate feature intended for private mesh VPN connectivity.
Test your local passkey flow through a real HTTPS origin
Start with a locally verified WebAuthn application, create a Localtonet HTTP tunnel, configure the assigned public origin and RP ID explicitly, and validate both registration and authentication with a fresh test credential.
Get Started Free โ