Public routes read the session store on every anonymous request (~250 ms on Cloudflare KV)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript, typescript
- Domain
- authentication, backend, performance
Research direction
The guard belongs in handlePublicRouteAuth in dist/astro/middleware/auth.mjs; read that file alongside the already-correct dist/astro/middleware.mjs, which checks cookies.get("astro-session") before calling resolveSessionUser. Add the same cookie check at the top of handlePublicRouteAuth so anonymous requests short-circuit to next(), then rerun the anonymous public-page timing from the issue to confirm the KV read is gone. Related history is #1538, which introduced resolveSessionUser.
Written by the indexing model from the issue text.
Description
Description
handlePublicRouteAuth in dist/astro/middleware/auth.mjs calls resolveSessionUser(session) for every public route, whether or not the request carries a session cookie:
async function handlePublicRouteAuth(context, next) {
const { locals, session } = context;
const { emdash } = locals;
try {
const sessionUser = await resolveSessionUser(session);
With no astro-session cookie, Astro generates a fresh session id, so session.get("user") looks up a key that cannot exist. On Cloudflare Workers with the session driver on KV, a read of a never-written key is not served from the colo cache. We measured 248-283 ms per anonymous render (warm isolates, Tokyo colo), against 4-10 ms for a key that exists. That wait sits in front of every anonymous page render, before any site middleware or page code runs.
The main middleware (dist/astro/middleware.mjs) already skips the lookup in this case:
const hasSessionCookie = cookies.get("astro-session") !== void 0;
const sessionUser = context.isPrerendered || !hasSessionCookie ? null : await resolveSessionUser(context.session);
Expected: handlePublicRouteAuth makes the same check. One line fixes it:
if (context.cookies.get("astro-session") === void 0) return next();
Without the cookie the old read could never find a user, so the guard changes no behaviour except the latency. Logged-in requests carry the cookie and keep the read. Related: #1538 added resolveSessionUser and its timeout backstop, but not a cookie check on this path.
Steps to reproduce
- Deploy an EmDash site to Cloudflare Workers with the default
SESSIONKV binding for Astro sessions. - Request any public page anonymously (no cookies), with a cache-busting query so it renders.
- Time the gap between the start of EmDash's middleware chain and the first line of a site middleware that runs after it (or compare the
Server-Timingdb.firstoffset). On our site it is ~250 ms on every anonymous render. - Add the guard above to
handlePublicRouteAuthand repeat. The gap disappears.
Environment
- emdash version: 0.32.0 (1.0.1 has the same
handlePublicRouteAuth) - Astro 6.1.10, @astrojs/cloudflare 13
- Runtime: Cloudflare Workers (compatibility_date 2026-02-24), Astro sessions on KV
- OS: not applicable
Screenshots
Not applicable.
Logs / error output
No error. Timing only: gap before site middleware 251-263 ms (warm), KV get of a random nonexistent key 248-283 ms, KV get of an existing key 4-10 ms, measured in the same requests.
We carry the guard as a pnpm patch for now and are happy to open a PR.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 1.3k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 365
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from emdash-cms/emdash
-
plugin-forms: autoresponder and Reply-To use the first email field, even an optional onePossibly taken @eisenbruch claimed this 4 days ago. Openarea/plugins bot:fix triage/awaiting-reporter
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
emdash-cms/emdash#4504 · 4 comments ·
Maintainers usually reply within 1 day
-
WordPress import review labels the SEO toggle "SEO settings (Yoast)" even when the site uses Rank MathPossibly taken @iNerdStack claimed this 13 days ago. Openbot:bug bot:needs-attention
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
emdash-cms/emdash#4110 · 3 comments ·
Maintainers usually reply within 1 day
-
bot:bug bot:needs-attention bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
emdash-cms/emdash#3992 · 2 comments ·
Maintainers usually reply within 1 day
-
bot:bug bot:needs-attention
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
emdash-cms/emdash#3972 · 2 comments ·
Maintainers usually reply within 1 day
-
bot:needs-attention bot:task
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
emdash-cms/emdash#3821 · 1 comment ·
Maintainers usually reply within 1 day
All issues in emdash-cms/emdash
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 74/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
remotion-dev/remotion#12905 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
supermomonga/paseo-plugin-zcode-provider#83 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
oxidecomputer/skepsis#102 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
voidzero-dev/void#677 ·
Maintainers usually reply within 1 day