Last active
November 6, 2026 07:25
-
-
Save karlhorky/61f751daf34799caee18f4f6d895ee29 to your computer and use it in GitHub Desktop.
Next.js and Expo Router API origin checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Security: Reject write requests from other websites (CSRF protection) | |
| // to prevent attacks like unwanted account changes or login CSRF | |
| // | |
| // Expo Router API Routes do not yet have built-in CSRF protection | |
| // - https://github.com/expo/expo/issues/53058 | |
| // eslint-disable-next-line @typescript-eslint/naming-convention -- Expo Router requires this export name | |
| export const unstable_settings = { | |
| matcher: { | |
| // Skip screens and other requests with safe methods | |
| methods: ['POST', 'PUT', 'PATCH', 'DELETE'], | |
| patterns: [ | |
| '/api', | |
| '/api/[...path]', | |
| // Expo also serves auth API routes under /(auth)/api | |
| '/(auth)/api/[...path]', | |
| ], | |
| }, | |
| }; | |
| export default function middleware(request: Request) { | |
| const origin = request.headers.get('origin'); | |
| // Allow non-browser clients without Origin | |
| if (origin === null) return; | |
| if ( | |
| !URL.canParse(origin) || | |
| new URL(origin).host !== | |
| (request.headers.get('x-forwarded-host') || request.headers.get('host')) | |
| ) { | |
| return new Response('Cross-site request forbidden', { status: 403 }); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Security: Reject write requests from other websites (CSRF protection) | |
| // to prevent attacks like unwanted account changes or login CSRF | |
| // | |
| // Next.js Route Handlers do not yet have built-in CSRF protection | |
| // - https://github.com/vercel/next.js/discussions/101245 | |
| import type { NextRequest } from 'next/server'; | |
| export const config = { | |
| // Route groups like (auth) are omitted from URLs, so this also matches their API routes | |
| matcher: '/api/:path*', | |
| }; | |
| export function proxy(request: NextRequest) { | |
| const origin = request.headers.get('origin'); | |
| // Allow safe methods and non-browser clients without Origin | |
| if (['GET', 'HEAD', 'OPTIONS'].includes(request.method) || origin === null) { | |
| return; | |
| } | |
| if ( | |
| !URL.canParse(origin) || | |
| new URL(origin).host !== | |
| (request.headers.get('x-forwarded-host') || request.headers.get('host')) | |
| ) { | |
| return new Response('Cross-site request forbidden', { status: 403 }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment