Overview
RLink handles requests in layers:- Your browser requests a page or API route.
proxy.tscan inspect the request before it reaches the route.- Better Auth validates the session for protected flows.
- Route handlers under
app/api/or pages underapp/home/continue the request.
Where proxy.ts fits
proxy.ts is the request boundary for rules that should run before a page or route handler.
Typical responsibilities include:
- Redirecting unauthenticated users away from protected pages
- Applying cross-origin rules for API access
- Normalizing request handling for shared entry points
- Rejecting requests early when origin or auth state is invalid
Keep
proxy.ts focused on request-level concerns. Put business logic in app/api/* or lib/*.Page requests vs API requests
Protected app pages
Pages under/home/* are authenticated application routes. If the user has no valid session cookie, RLink should send them back to /login or block the page in the app layer.
API routes
Routes underapp/api/* are HTTP endpoints. Some are private dashboard APIs. Others may be public or secret-protected, such as webhooks or cron routes.
Before you expose a route publicly, decide:
- Who is allowed to call it
- Whether it uses cookies, a bearer secret, or a signature header
- Whether browsers from another origin should be allowed
- What status code you return on auth failure
Session and cookie flow
RLink uses Better Auth with cookie-based sessions. The usual flow is:- A user signs in on
/login. - Better Auth creates a session and sets cookies in the browser.
- Later requests to
/home/*and protected APIs include those cookies automatically. - The server validates the session before returning private data.
NEXT_PUBLIC_APP_URLdoes not match the real origin- You switched between
httpandhttps - You are testing on a Vercel preview URL with production cookie settings
- Your API client did not forward browser cookies
CORS and ALLOWED_ORIGIN
CORS matters only when a browser on one origin calls an API on another origin.
Examples:
https://admin.example.comcallinghttps://admin.example.com/api/*: same origin, no CORS issuehttps://preview-app.vercel.appcallinghttps://prod-app.vercel.app/api/*: cross origin, CORS rules apply
ALLOWED_ORIGIN to match the browser origin you want to permit. If it is wrong, the browser may block the request before your app logic even runs.
Symptoms of a bad CORS setup:
- Failed
OPTIONSpreflight request - Request appears in DevTools but response is blocked
- Cookies are missing on a cross-origin call
- Browser shows a CORS error even though the server returned
200
Server fetch vs browser fetch
Do not assume allfetch() calls behave the same way.
- Browser fetch after login usually sends same-origin cookies automatically
- Server-side fetch inside Next.js may need you to forward headers or cookies yourself
- Postman / curl never know your browser session unless you copy the cookies manually
Debugging checklist
When a request fails, check in this order:- URL Is the request hitting the right host, protocol, and path?
- Cookies Did the browser actually send auth cookies?
- Origin
Does the request origin match
ALLOWED_ORIGINwhen cross-origin? - Response code
401usually means unauthenticated.403usually means authenticated but not allowed. - Redirects
Are you being bounced from
/home/*to/loginrepeatedly? - Headers For cron and webhooks, did you send the secret or signature header the route expects?
Edge cases
Localhost vs preview vs production
Treat each environment as a separate origin. A cookie fromlocalhost does not apply to a Vercel preview URL, and a preview URL does not equal production.
Redirect loops
If a signed-in user keeps landing back on/login, check:
- Session cookie presence
- Cookie domain and security flags
- App URL environment variables
- Whether
proxy.tsand page-level guards disagree
Preflight requests
Browsers may sendOPTIONS before the real request. If your route or proxy blocks that preflight incorrectly, the browser never sends the actual API call.
Secret-protected routes
Cron and webhook routes often do not use user cookies at all. They usually expect:Authorization: Bearer ...- A provider-specific signature header
- An allowlist check
Best practices
- Keep
proxy.tssmall and predictable - Prefer explicit route protection over hidden assumptions
- Return clear
401and403responses for APIs - Log request context, not secrets
- Test auth and CORS in every environment you deploy
Next steps
Authentication
Better Auth, sessions, and 2FA
Authorization
Module access and permission checks
Webhooks
Secret validation and retries
Troubleshooting
Request, cookie, and deployment failures
