/ src / proxy.ts
proxy.ts
 1  import { NextResponse } from 'next/server';
 2  import type { NextRequest } from 'next/server';
 3  
 4  export function proxy(request: NextRequest) {
 5    // Only check state-changing methods
 6    if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method)) {
 7      const origin = request.headers.get('origin');
 8      const host = request.headers.get('host');
 9      const referer = request.headers.get('referer');
10  
11      // If Origin is present, it must match the Host (protocol + hostname + port)
12      // Note: host header usually doesn't include protocol, but origin does.
13      // Construct expected origin from protocol and host.
14      if (origin && host) {
15        // In production behind proxies, protocol might need 'x-forwarded-proto'
16        const protocol = request.headers.get('x-forwarded-proto') || request.nextUrl.protocol.replace(':', '');
17        const expectedOrigin = `${protocol}://${host}`;
18  
19        if (origin !== expectedOrigin) {
20          // Also allow if origin is just the localhost with http
21          // (Simpler check: does origin end with host?)
22          try {
23            const originUrl = new URL(origin);
24            if (originUrl.host !== host) {
25              console.warn(`CSRF Blocked: Origin ${origin} does not match Host ${host}`);
26              return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 });
27            }
28          } catch {
29            console.warn(`CSRF Blocked: Invalid Origin ${origin}`);
30            return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 });
31          }
32        }
33      }
34  
35      // Strict Referer check as fallback/addition
36      if (referer && host) {
37        try {
38          const refererUrl = new URL(referer);
39          if (refererUrl.host !== host) {
40            console.warn(`CSRF Blocked: Referer ${referer} does not match Host ${host}`);
41            return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 });
42          }
43        } catch {
44          console.warn(`CSRF Blocked: Invalid Referer ${referer}`);
45          return new NextResponse('Forbidden: CSRF Check Failed', { status: 403 });
46        }
47      }
48    }
49  
50    return NextResponse.next();
51  }
52  
53  export const config = {
54    matcher: '/api/:path*',
55  };