Skip to content

Platform SSR correctness: route rules, emitted entries, and navigation errors #2522

Closed as not planned

Description

@benpsnyder

Please provide the environment you discovered this bug in.

  • Analog 3.0.0-alpha.86, untouched upstream alpha at 4225f45090a4cf4bdfb562a788e908787b6ec73c.
  • Linux, Node 24.15.0, pnpm 10.33.0, Angular 22.0.0, Vite 8.0.8, Nitro 3.0.260903-beta.
  • Reproduction uses the repository's existing application and Playwright test; no private consumer is needed.
git clone --branch alpha https://github.com/analogjs/analog.git analog-ssr-repro
cd analog-ssr-repro
git checkout 4225f45090a4cf4bdfb562a788e908787b6ec73c
pnpm install --frozen-lockfile
pnpm exec playwright install chromium
CI=true NX_DAEMON=false NX_NO_CLOUD=true pnpm exec nx e2e analog-app-e2e --grep="only injects JSON-LD client-side" --skipNxCache

Use Node 24.15.0 for the reproduction. The test target builds and starts its own local server.

Which area/package is the issue in?

platform, with the router's buffered-render error handoff relevant to the later error-handling portion.

Description

This tracks the platform SSR workstream requested in Brandon's review of analogjs/analog#2516: route policies, emitted SSR entry discovery, and buffered error behavior.

The first independently reproduced blocker is a route with ssr: false still being server-rendered:

nitro({
  routeRules: {
    '/client': { ssr: false },
    '/admin/**': { ssr: false },
    '/admin/help': { ssr: true },
  },
});

The /client response should contain the client application shell, with its component and JSON-LD added only after client bootstrap. Instead, the raw HTTP response already contains <analog-client> and the rendered page content. The unchanged apps/analog-app-e2e/tests/json-ld.spec.ts assertion fails on untouched alpha.

The renderer currently reads the no-SSR hint from response headers before Nitro's response-header middleware applies it. The matched rule is already available on the event context. In addition, an explicit child ssr: true needs to reset an inherited opt-out instead of retaining the parent's hint.

This also blocks the otherwise focused compiler PR analogjs/analog#2520 in the shared E2E job. The route-policy repair is isolated in analogjs/analog#2523, which is green and ready for review. Entry discovery, buffered errors, and Worker output corrections are published in analogjs/analog#2528. Compiler functionality remains in its dedicated PR.

The follow-up's regression commit records missing/ambiguous entry failures and actual failed resolvers returning HTTP 200. The same package and built-server assertions pass after repair, including safe 404/503/500 documents and concurrent successful requests.

Worker qualification also found the Pages output layout being imposed on Module Workers, placing server code inside their asset directory. A synthetic local Miniflare reproduction served a server chunk with bytes identical to the server file. The scoped output fix preserves Nitro's separate Module/Durable server and asset directories while retaining Pages behavior. Its package gates and the later packed local Workerd qualification pass. Deployment scripts that referenced the accidental Module _worker.js layout should use the generated Wrangler configuration.

Please provide the exception or error you saw

JSON-LD > only injects JSON-LD client-side for no-SSR routes
expect(rawHtml).not.toContain('analog-client')
Received: server-rendered HTML containing <analog-client>

The failure reproduces before any implementation changes in a separate baseline worktree. It is not an intermittent browser timing assertion: it inspects the raw server response.

Other information

Workstream acceptance criteria:

  • Route policies apply before rendering; an explicit child opt-in resets inherited opt-out.
  • The existing built-app no-SSR/JSON-LD E2E assertion passes unchanged, with focused generated-renderer tests for precedence and normal rendering.
  • Emitted .js/.mjs entries are discovered deterministically; missing or ambiguous output fails at build time.
  • Buffered SSR navigation failures preserve valid error statuses and return safe, non-cacheable HTML without exception details or application bootstrap scripts.
  • Worker bundling honors the target policy, and Module/Durable server output remains outside the asset directory.
  • Every PR includes its own reproducible tests and documentation. The route-policy PR does not close the remaining criteria.

Both focused PRs target alpha and pass CI: analogjs/analog#2523 at 535fb080ee9b29931a6f6232a08f532213f164f0, and analogjs/analog#2528 at 01ae947367216b3dbcea314111cb8d5d5a36ab03. The route-policy PR also restores caller-supplied x-analog-no-ssr filtering with request-adapter and built HTTP tests: callers cannot disable an SSR-enabled route or enable a configured client-rendered route. The entry/error PR includes deterministic output discovery, safe status/error handling, complete prerendered HTML/sitemap checks, and Worker output separation. The earlier regression commits above remain historical reproductions. This issue stays open until both contributions are merged.

I would be willing to submit a PR to fix this issue

  • Yes

Prepared with OpenAI Codex; the route-policy defect was reproduced on untouched alpha.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions