Middleware Not Triggered for Safaricom Callback in Next.js Hosted on Vercel

Hello Vercel Community,

I’m encountering an issue in my Next.js project where middleware is not being triggered for external API requests from Safaricom’s callback server. Internal API calls and tools like Postman and cURL successfully trigger the middleware and the API route, but the Safaricom callback fails to do so. Here’s the breakdown:


Project Setup

  • Framework: Next.js 13 with the app directory structure.
  • Middleware Location: /src/middleware.ts
  • Hosted On: Vercel
  • Callback URL:
    https://wifi-magement-system.vercel.app/api/stkPushCallback/?Order_ID=${Order_ID}
    

Middleware Code

Here’s my middleware.ts file:

import { NextRequest, NextResponse } from 'next/server';

const cors = (req: NextRequest) => {
  console.log('CORS middleware triggered for:', req.url);

  const allowedOrigins = ['*']; // Currently allowing all origins for testing
  const origin = req.headers.get('origin');
  const isAllowedOrigin = allowedOrigins.includes(origin || '');

  console.log('Request Origin:', origin);
  console.log('Is Allowed Origin:', isAllowedOrigin);

  const headers: Record<string, string> = {
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    'Access-Control-Allow-Origin': isAllowedOrigin ? origin || '*' : '*',
    'Access-Control-Allow-Credentials': 'true',
  };

  if (req.method === 'OPTIONS') {
    console.log('Handling OPTIONS preflight request');
    return new NextResponse(null, {
      status: 200,
      headers,
    });
  }

  const response = NextResponse.next();
  Object.entries(headers).forEach(([key, value]) => {
    response.headers.set(key, value);
    console.log(`Header Set: ${key} = ${value}`);
  });

  return response;
};

export function middleware(req: NextRequest) {
  console.log('Middleware triggered for:', req.url);
  return cors(req);
}

export const config = {
  matcher: ['/api/:path*'], // Matches all API routes
};

The Problem

  1. Internal Requests Work:

    • Middleware is triggered correctly for internal API requests within the project.
    • Tools like Postman and cURL successfully trigger the middleware and the API route.
  2. External Requests Fail:

    • Safaricom’s callback server does not trigger the middleware, and no logs appear from the middleware when the callback is made.
    • The callback URL is accessible, confirmed via manual testing with Postman and cURL.
  3. Callback Behavior:

    • Safaricom successfully sends the STK push callback to the URL, but it seems blocked or ignored by the middleware on Vercel.

What I’ve Tried

  1. Verified middleware placement (/src/middleware.ts) and configuration (matcher: ['/api/:path*']).
  2. Tested with wildcard origins ('Access-Control-Allow-Origin': '*').
  3. Checked logs to confirm the middleware isn’t triggered for Safaricom callbacks.
  4. Tested the callback URL with Postman, cURL, and ngrok—all work as expected.
  5. Confirmed the Safaricom callback server is not hitting the middleware, even though the endpoint is reachable.

My Questions

  1. Could Vercel’s infrastructure block certain requests or headers from external servers like Safaricom?
  2. Is there a specific configuration required to ensure external requests trigger the middleware?
  3. Are there any known limitations with middleware handling external callbacks in Next.js hosted on Vercel?

Additional Information

  • Hosted on Vercel with no custom Vercel configurations for the project.
  • Middleware is required to handle CORS preflight and control headers dynamically for external APIs.

Any assistance or guidance on resolving this issue would be greatly appreciated! Thank you for your time and expertise.

Hi, and welcome @tmoh-squim!

Thank you for providing such a detailed post :handshake:

Vercel uses an Edge Network to handle incoming requests, which might affect how certain requests are processed before reaching your middleware.

Your current middleware configuration looks correct, but we can try to make it more inclusive to ensure it catches all incoming requests.

The structure of your callback URL might be affecting how Vercel routes the request. You could consider simplifying your callback URL structure. Instead of using query parameters, you could include the Order_ID as part of the path. Something like:

https://wifi-magement-system.vercel.app/api/stkPushCallback/{Order_ID}

Then update your API route to handle this structure.

While you mentioned that you don’t have any custom Vercel configurations, it might be worth adding a vercel.json file to explicitly define how requests should be handled.

Let us know how you get on!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.