Skip to content

Commit e2f111f

Browse files
committed
docs: update configuration docs for siteUrl, add changeset
Update public docs, skills reference, and demo config to document siteUrl replacing passkeyPublicOrigin. Add EMDASH_SITE_URL / SITE_URL to env vars table. Changeset: minor bump with breaking-change note.
1 parent 2922e86 commit e2f111f

4 files changed

Lines changed: 53 additions & 13 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"emdash": minor
3+
---
4+
5+
Adds `siteUrl` config option to fix reverse-proxy origin mismatch. Replaces `passkeyPublicOrigin` with a single setting that covers all origin-dependent features: passkeys, CSRF, OAuth, auth redirects, MCP discovery, snapshots, sitemap, robots.txt, and JSON-LD.
6+
7+
Supports `EMDASH_SITE_URL` / `SITE_URL` environment variables for container deployments where the domain is only known at runtime.
8+
9+
Disables Astro's `security.checkOrigin` (EmDash's own CSRF layer handles origin validation with dual-origin support and runtime siteUrl resolution). When `siteUrl` is set in config, also sets `security.allowedDomains` so `Astro.url` reflects the public origin in templates.
10+
11+
**Breaking:** `passkeyPublicOrigin` is removed. Rename to `siteUrl` in your `astro.config.mjs`.

demos/simple/astro.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export default defineConfig({
3030
baseUrl: "/_emdash/api/media/file",
3131
}),
3232
plugins: [auditLogPlugin()],
33-
// HTTPS reverse proxy: uncomment so passkey verify matches browser origin
34-
// passkeyPublicOrigin: "https://emdash.local:8443",
33+
// HTTPS reverse proxy: uncomment so all origin-dependent features match browser
34+
// siteUrl: "https://emdash.local:8443",
3535
}),
3636
],
3737
devToolbar: { enabled: false },

docs/src/content/docs/reference/configuration.mdx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,13 @@ Use Cloudflare Access as the authentication provider instead of passkeys.
200200
magic links, and self-signup are disabled.
201201
</Aside>
202202

203-
### `passkeyPublicOrigin`
203+
### `siteUrl`
204204

205-
**Optional.** Pass a full **browser-facing origin** (scheme + host + optional port, **no path**) so WebAuthn **`rpId`** and **`origin`** match what the user’s browser sends in `clientData.origin`.
205+
**Optional.** The public browser-facing origin for the site (scheme + host + optional port, **no path**).
206206

207-
By default, passkeys follow **`Astro.url`** / **`request.url`**. Behind a **TLS-terminating reverse proxy**, the app often still sees **`http://`** on the internal hop while the tab is **`https://`**, or the reconstructed host does not match the public name — which breaks passkey verification. Set `passkeyPublicOrigin` to the origin users type in the address bar (for example `https://cms.example.com` or `https://cms.example.com:8443`).
207+
Behind a **TLS-terminating reverse proxy**, `Astro.url` returns the internal address (`http://localhost:4321`) instead of the public one (`https://cms.example.com`). This breaks passkeys, CSRF origin matching, OAuth redirects, login redirects, MCP discovery, snapshot exports, sitemap, robots.txt, and JSON-LD structured data. Set `siteUrl` to fix all of these at once.
208208

209-
The integration **validates** this value at load time: it must be a valid URL with **`http:`** or **`https:`** protocol and is normalized to **`origin`**.
209+
The integration **validates** this value at load time: it must be a valid URL with **`http:`** or **`https:`** protocol and is normalized to **origin** (path is stripped).
210210

211211
```js
212212
emdash({
@@ -215,17 +215,23 @@ emdash({
215215
directory: "./uploads",
216216
baseUrl: "/_emdash/api/media/file",
217217
}),
218-
passkeyPublicOrigin: "https://cms.example.com",
218+
siteUrl: "https://cms.example.com",
219219
});
220220
```
221221

222-
#### Reverse proxy and passkeys
222+
When `siteUrl` is not set in config, EmDash checks environment variables in order: `EMDASH_SITE_URL`, then `SITE_URL`. This is useful for container deployments where the public URL is set at runtime.
223+
224+
<Aside type="tip">
225+
`siteUrl` replaces `passkeyPublicOrigin` (removed). If you were using `passkeyPublicOrigin`, rename it to `siteUrl` -- it now covers passkeys and all other origin-dependent features.
226+
</Aside>
227+
228+
#### Reverse proxy setup
223229

224230
Astro only reflects **`X-Forwarded-*`** when the public host is allowed. Configure [**`security.allowedDomains`**](https://docs.astro.build/en/reference/configuration-reference/#securityalloweddomains) for the hostname (and schemes) your users hit. In **`astro dev`**, add matching **`vite.server.allowedHosts`** so Vite accepts the proxy **`Host`** header.
225231

226-
Prefer fixing **`allowedDomains`** (and forwarded headers) first; use **`passkeyPublicOrigin`** when the reconstructed URL **still** diverges from the browser origin (typical when TLS is terminated in front and the upstream request stays **`http://`**).
232+
Prefer fixing **`allowedDomains`** (and forwarded headers) first; use **`siteUrl`** when the reconstructed URL **still** diverges from the browser origin (typical when TLS is terminated in front and the upstream request stays **`http://`**).
227233

228-
With TLS in front, binding the dev server to loopback (**`astro dev --host 127.0.0.1`**) is often enough: the proxy connects locally while **`passkeyPublicOrigin`** matches the public HTTPS origin.
234+
With TLS in front, binding the dev server to loopback (**`astro dev --host 127.0.0.1`**) is often enough: the proxy connects locally while **`siteUrl`** matches the public HTTPS origin.
229235

230236
<Aside type="caution">
231237
Your reverse proxy should forward a **port-aware** `Host` / `X-Forwarded-Host` when you use non-default ports. If the proxy strips the port, **`rpId`** and Astro’s rebuilt URL can be wrong.
@@ -255,7 +261,7 @@ export default defineConfig({
255261
directory: "./uploads",
256262
baseUrl: "/_emdash/api/media/file",
257263
}),
258-
passkeyPublicOrigin: "https://cms.example.com",
264+
siteUrl: "https://cms.example.com",
259265
}),
260266
],
261267
});
@@ -435,6 +441,7 @@ EmDash respects these environment variables:
435441

436442
| Variable | Description |
437443
| ------------------------- | ---------------------------------------- |
444+
| `EMDASH_SITE_URL` | Public browser-facing origin (falls back to `SITE_URL`) |
438445
| `EMDASH_DATABASE_URL` | Override database URL |
439446
| `EMDASH_AUTH_SECRET` | Secret for passkey authentication |
440447
| `EMDASH_PREVIEW_SECRET` | Secret for preview token generation |

skills/building-emdash-site/references/configuration.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,31 @@ export default defineConfig({
3232
});
3333
```
3434

35-
### Reverse proxy and passkeys
35+
### Reverse proxy
3636

37-
Passkey `rpId` / `origin` follow Astro `context.url`, which only reflects `X-Forwarded-*` when you declare **allowed public hosts** ([`security.allowedDomains`](https://docs.astro.build/en/reference/configuration-reference/#securityalloweddomains)). In dev, add matching **`vite.server.allowedHosts`** or Vite rejects the proxy `Host`. Use **`emdash({ passkeyPublicOrigin: "https://…" })`** when the browser origin and reconstructed URL still disagree (common with TLS termination). With TLS terminated in front, **`astro dev --host 127.0.0.1`** (loopback) is usually enough: the proxy reaches the dev server locally while **`passkeyPublicOrigin`** matches the browser’s HTTPS origin—without opening the Node port on the LAN.
37+
When behind a TLS-terminating reverse proxy, `Astro.url` returns the internal address (e.g. `http://localhost:4321`) instead of the public one (`https://mysite.example.com`). This breaks passkeys, CSRF, OAuth, redirects, and more.
38+
39+
**Step 1:** Declare allowed public hosts via [`security.allowedDomains`](https://docs.astro.build/en/reference/configuration-reference/#securityalloweddomains) so Astro reconstructs the URL from `X-Forwarded-*` headers. In dev, add matching **`vite.server.allowedHosts`** or Vite rejects the proxy `Host`.
40+
41+
**Step 2:** If the reconstructed URL still disagrees with the browser (common with TLS termination), set **`siteUrl`**:
42+
43+
```javascript
44+
emdash({
45+
siteUrl: "https://mysite.example.com",
46+
// ...
47+
});
48+
```
49+
50+
Or via environment variable (useful for container deployments):
51+
52+
```bash
53+
EMDASH_SITE_URL=https://mysite.example.com
54+
# or: SITE_URL=https://mysite.example.com
55+
```
56+
57+
`siteUrl` replaces `passkeyPublicOrigin` (which only fixed passkeys). It applies to passkeys, CSRF origin matching, OAuth redirects, login redirects, MCP discovery, snapshot exports, sitemap, robots.txt, and JSON-LD structured data.
58+
59+
With TLS terminated in front, **`astro dev --host 127.0.0.1`** (loopback) is usually enough: the proxy reaches the dev server locally while **`siteUrl`** matches the browser’s HTTPS origin -- without opening the Node port on the LAN.
3860

3961
### Cloudflare (D1 + R2)
4062

0 commit comments

Comments
 (0)