Skip to content

fix: look up redirects against the API instead of fetching our own route - #4

Open
adrians5j wants to merge 1 commit into
mainfrom
claude/redirect-lookup-without-self-fetch
Open

fix: look up redirects against the API instead of fetching our own route#4
adrians5j wants to merge 1 commit into
mainfrom
claude/redirect-lookup-without-self-fetch

Conversation

@adrians5j

Copy link
Copy Markdown
Member

Problem

middleware.ts looked up redirects by fetching its own /api/redirects route:

const redirectsUrl = new URL(`/api/redirects?...`, request.url);
const redirectResponse = await fetch(redirectsUrl);

That fails silently in two environments:

  • Behind a local HTTPS proxy (portless and similar), the Edge runtime does not trust the proxy's CA, so the request throws SELF_SIGNED_CERT_IN_CHAIN. NODE_EXTRA_CA_CERTS does not help — middleware runs on the Edge runtime, which doesn't use Node's TLS root store.
  • On a deployment protected by Vercel Authentication, the self-request carries no credentials and is answered with a 401 or an auth redirect instead of by our route. Preview deployments are the common case.

In both cases the surrounding catch {} discarded the error, so a completely broken lookup was indistinguishable from "no redirect is configured". Every redirect just quietly stopped working, with nothing in the logs.

Even where it worked, it cost a second function invocation and a full network round trip on every page request, for a chain of middleware → route handler → API.

Fix

Query the Website Builder API directly. It's the only host middleware needs to reach, it has a real certificate and its own authentication, and it removes a hop.

Also:

  • Checks response.ok. Previously a non-200 was parsed as JSON regardless, so an error body flowed on as if it were redirect data.
  • Logs failures. A failed lookup still doesn't take the page down, but it is no longer invisible. This is the change that would have made the original bug a five-minute diagnosis instead of a long one.
  • Declares the response shape locally instead of importing it, so middleware stays free of runtime dependencies. @webiny/website-builder-sdk, where PublicRedirect lives, isn't a direct dependency of this project anyway.

Verified

  • /a307 /b over plain http://localhost:3000
  • /a307 /b over https://website-builder-nextjs.localhostthe case that was broken
  • a path with no redirect configured passes through with no Location header
  • no errors logged in either environment
  • tsc --noEmit clean

Not verified: behaviour on a protected Vercel preview deployment. The reasoning is that we no longer make a request that protection can intercept, but that deserves confirmation on a real preview.

Note

/api/redirects is now unused by middleware. It still works as a public endpoint; if nothing else calls it, it can be removed in a follow-up.

🤖 Generated with Claude Code

Middleware fetched its own /api/redirects route, which meant a second
request to this deployment on every page view — and it failed in two
environments, silently.

Behind a local HTTPS proxy the Edge runtime doesn't trust the proxy's
certificate, so the request threw SELF_SIGNED_CERT_IN_CHAIN. On a
deployment protected by Vercel Authentication the self-request carries no
credentials and is answered with a 401 or an auth redirect rather than by
our route. In both cases the surrounding `catch {}` discarded the error,
so a broken lookup was indistinguishable from "no redirect is configured"
and every redirect quietly stopped working.

Query the Website Builder API directly instead. It's the only host we
need, it has a real certificate and its own authentication, and it drops
a hop: middleware -> API rather than middleware -> route -> API. Also
removes a function invocation per request.

The response shape is declared locally rather than imported, so
middleware stays free of runtime dependencies.

Failures are now logged. A failed lookup still doesn't take the page
down, but it's no longer invisible.

Verified: /a redirects to /b over both plain http://localhost:3000 and
https://website-builder-nextjs.localhost (the case that was broken), and
a path with no redirect passes through untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread src/middleware.ts
} catch (err) {
// A failed lookup must not take the page down, but it must not be silent either: swallowing it
// is indistinguishable from "no redirect is configured" and hides real API failures.
console.error(`[middleware] Redirect lookup failed for "${pathname}":`, err);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants