Sessions across subdomains
Sign users in once across every subdomain of your verified custom domain.
Sessions across subdomains
By default the ploy_session cookie belongs to the host that set it. A user who signs in on app.example.com is signed out on api.example.com, even when both serve the same project.
Share sessions across subdomains
To sign users in once for every subdomain of a domain:
- Add the subdomains your project serves under Settings → Domains and verify them, for example
app.example.comandapi.example.com. - Open Auth → Settings in the dashboard, select the project, and under Sign-ups and sessions pick the domain in Share sessions across subdomains, for example
example.com. - Save. Only organization owners and admins can change this setting.
From then on, a sign-in on any host under the domain sets the session cookie for the whole domain, so the session is valid on example.com and all of its subdomains.
Which domains are allowed
The list offers verified project domains and their parents. Ploy checks eligibility when you save and on every auth request, including server-side binding checks:
- It must be a verified custom domain that serves this project, or a parent of one.
example.comis allowed onceapp.example.comis verified. - It must not cover a custom domain of another organization.
- Public suffixes such as
com,co.ukorgithub.io, IP addresses, and Ploy's own domains (deployment and preview URLs andploy.emaildomains) are never allowed. A session cookie on a shared domain would be readable by every other app on it.
If the domain is removed, loses verification, or overlaps another organization, existing shared sessions and sign-in flows are rejected, including bearer-token and server-side binding checks. New sign-ins use host-only cookies. Hosts outside the configured domain, such as your default deployment URL, always set host-only cookies.
Every host under the domain receives the session cookie and may make credentialed auth requests, including other projects or services on sibling subdomains. Verification of one subdomain does not prove control of every sibling: only share a parent domain whose entire subtree you control and trust.
What changes with a shared domain
| Area | Behavior |
|---|---|
| Cookies | The session and the short-lived sign-in flow cookies are set and cleared for the shared domain. Signing out revokes the session for every subdomain and clears the shared cookie plus the current host-only cookie. |
| Redirects | redirectTo may point to sibling origins with the same scheme and port as the auth host, not just the current host. |
| Origin checks | Form posts and passkey requests are accepted from sibling origins under the domain, and from nothing broader. |
| Cross-origin APIs | Pages on a sibling subdomain can call the auth endpoints with credentials, for example app.example.com calling https://auth.example.com/_ploy/auth/me. |
| Passkeys | The passkey relying party becomes the shared domain, so one passkey works on every subdomain. See Passkeys. |
Changing the setting
New sign-ins use the new setting immediately. Existing host-only sessions are not promoted to shared sessions; users sign in again to share them. If a sibling still has an older host-only cookie, Ploy prefers the valid shared session and expires that host-only copy. Shared sessions and pending flows are rejected when their signed domain no longer matches an eligible configured domain. On a host under the former domain, reading a rejected session, signing in again, or signing out expires the old shared cookie.
A server cannot remove cookies from every browser or from hosts outside its cookie scope. An old Domain cookie can still be delivered to a sibling server until the browser clears it or it expires, even though Ploy rejects it. Before transferring or relinquishing a domain or any sibling host, disable sharing, have users sign out, and allow the previous session lifetime (and the 10-minute flow-cookie lifetime) to elapse. Do not treat removing domain verification as browser-cookie erasure.
Passkeys are bound to their relying party. Before changing the setting, enable an alternative sign-in method and have passkey-only users add a password or link an enabled social provider while their old session still works. After the change, they sign in with that method, add a new passkey under the new relying party, verify it works, and only then remove the old passkey. Changing or disabling an existing shared domain rejects its sessions, so an existing shared session alone is not a migration path. When first enabling sharing, existing host-only sessions remain usable. Plan this before enabling, changing, or disabling sharing.
Serving auth from another subdomain
Every host of the project serves /_ploy/auth/*. With a shared domain your app can also use the endpoints of a sibling host:
<AuthProvider apiUrl="https://auth.example.com/_ploy/auth">
{children}
</AuthProvider>For Next.js, pass that host's sign-in page as signInUrl, for example authMiddleware({ protectedRoutes, signInUrl: "https://auth.example.com/_ploy/auth/sign-in" }) or protect({ signInUrl, returnTo }). Both send users back to the absolute URL of the page they came from. currentUser() reads the shared cookie and checks it with the PLOY_AUTH binding, so server-side checks work on every host the project serves.
Sessions belong to the project that owns the auth binding. Another project on a sibling subdomain can call the endpoints above from the browser, but its own PLOY_AUTH binding does not accept this project's sessions.
Local development
This is a dashboard/API auth-binding setting, not a ploy.yaml key. For custom integrations, PATCH the organization auth binding with cookieDomain: "example.com" (or null to disable it); save-time validation and owner/admin permissions are the same as in the dashboard.
ploy dev supports the same setting in the dev dashboard's auth settings. Locally the domain must be a localhost subdomain, for example myapp.localhost, which shares the session between app.myapp.localhost and api.myapp.localhost. Ports never separate cookies, so apps on different localhost ports already share a session without it.
How is this guide?
Last updated on