Behind the Fix: Making Invite & Pricing Pages Publicly Accessible
Behind the Fix: Making Invite & Pricing Pages Publicly Accessible
Release v1.0.84
What Changed
In v1.0.84 we corrected a subtle but impactful bug in the Next.js middleware route matcher. Two pages that should be publicly accessible — /pricing and /invite/[token] — were inadvertently protected behind authentication.
The Problem
Next.js middleware uses a matcher config to decide which routes it intercepts. Our middleware enforces authentication for all matched routes, so anything in the matcher list requires a signed-in session.
The previous matcher pattern was:
// src/middleware.ts
export const config = {
matcher: ['/((?!api/auth|_next/static|_next/image|favicon.ico|sign-in|sign-up|^$).+)'],
};
At first glance, the ^$ anchor looks like it excludes the root path /. In practice, the outer .+ (match one or more characters) already prevented / from matching — but that same .+ also meant that any non-empty path not explicitly listed in the lookahead would be intercepted, including /pricing and /invite/abc123.
Why This Mattered
| Route | Expected behaviour | Actual behaviour (pre-fix) |
|---|---|---|
/pricing | Publicly viewable | Redirected to sign-in |
/invite/[token] | Publicly viewable; auth required only to accept | Redirected to sign-in |
/sign-in | Public | Public (correct) |
/ | Public | Public (correct) |
The /invite/[token] case was the most disruptive: new users receiving an invitation email would be immediately bounced to the sign-in page before they could even see what they were being invited to. This broke the invite acceptance flow for users who did not yet have an account.
The Fix
The matcher pattern was updated to explicitly exclude both invite and pricing from middleware interception:
// src/middleware.ts (v1.0.84)
export const config = {
matcher: ['/((?!api/auth|_next/static|_next/image|favicon.ico|sign-in|sign-up|invite|pricing).*)'],
};
Two changes were made:
- Added
inviteandpricingto the negative lookahead so those path prefixes bypass the authentication middleware entirely. - Replaced
.+with.*— this makes the outer group match zero or more characters, cleanly handling the root path/without the now-redundant^$anchor.
How Invite Authentication Still Works
Making /invite/[token] publicly accessible at the middleware level does not mean anyone can accept an invitation without signing in. Authentication for the acceptance action is enforced inside the invite component itself:
- Viewing the invite page — no sign-in required. Users can see who invited them and what workspace they are joining.
- Accepting the invite — the component checks for an active session and redirects to sign-in (or sign-up) if one is not present, preserving the invite token so the flow completes after authentication.
This is the correct pattern: public visibility, protected action.
Upgrade Notes
- No database migrations required.
- No API changes.
- No configuration changes needed for self-hosted deployments.
- If you have customised
src/middleware.tslocally, apply the same lookahead and.*changes to your fork.