Error message
SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain. Subsequently, an attempt to fetch the .well-known/webauthn resource of the claimed RP ID failed.
Also printed as
- SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain.
Chromium versions without Related Origin Requests support end the sentence after "…nor equal to the current domain.". Other browsers word it differently; the DOMException name `SecurityError` is the stable part.
WebAuthn navigator.credentials.create() / get() — DOMException SecurityError (Chromium)
What it means
The RP ID in your options is not the page's own host name or a registrable parent of it, so the browser refused to create or use a credential for it. A passkey is bound to its RP ID; letting any page claim any RP ID would let one site use another site's passkeys. Chromium also tried the WebAuthn Related Origin Requests mechanism (fetching `https://<rp-id>/.well-known/webauthn` to see whether the RP ID lists your origin) and that did not authorise the request either.
Why it happens
Production RP ID on a development or preview host
common`rp.id: "app.example.com"` sent to a page on `localhost`, a preview deployment URL (`*.vercel.app`, `*.netlify.app`) or a staging domain. The RP ID must match the environment the page is served from.
A sibling or child domain as RP ID
commonA page on `login.example.com` may use `login.example.com` or `example.com`, but not `app.example.com` or `api.login.example.com`. Choosing the parent domain up front lets all subdomains share passkeys.
Another domain without a .well-known/webauthn entry
occasionalUsing one RP ID across different domains (country TLDs, rebrands) requires the RP ID's domain to list the other origins in `/.well-known/webauthn`. Without it, or if the file is unreachable, the request is refused.
Opening the site by IP address
occasionalIP addresses are not valid RP IDs; WebAuthn needs a domain name (`localhost` is allowed for development).
How to fix it
- 1.
Configure the RP ID per environment
Keep RP ID and expected origin together in environment configuration so development, preview and production each use their own host — and remember that passkeys do not move between RP IDs.
js // Server: derive the RP ID from configuration per environment, never hard-code production. const rpID = process.env.WEBAUTHN_RP_ID; // "localhost" in dev, "example.com" in production const expectedOrigin = process.env.WEBAUTHN_ORIGIN; // "http://localhost:3000" / "https://app.example.com" const options = await generateRegistrationOptions({ rpName: "Example", rpID, userName }); - 2.
Publish related origins deliberately
If several of your domains must share passkeys, serve `/.well-known/webauthn` on the RP ID's domain listing them (supported by current Chromium and Safari).
json // https://example.com/.well-known/webauthn (served as application/json) { "origins": ["https://example.co.uk", "https://example-shop.com"] }
Related errors
NotAllowedError: The operation either timed out or was not allowed. See: https:…WebAuthn navigator.credentials.get() / create()Unexpected registration response origin "http://localhost:3000", expected "http…SimpleWebAuthn (@simplewebauthn/server)TypeError: Cannot read properties of undefined (reading 'digest')WebCrypto