Repository: next.js
Issue: CORS middleware not applied to preview and staging domains, but works on production domain
Labels: bug,Middleware

### Link to the code that reproduces this issue

https://github.com/vercel/turbo

### To Reproduce

I'm developing in a Trubo monorepo with 2 apps: `api` and `app`

Each package has 3 associated domains:
```
APP:
--- app.acme.com [Production]
--- app-dev.acme.com [Preview]
--- app-staging.acme.com [Staging]

API:
--- api.acme.com [Production]
--- api-dev.acme.com [Preview]
--- api-staging.acme.com [Staging]
```

I need the apps to communicate with each other, particularly I need `app-[X].acme.com` to make CORS requests to `api-[X].acme.com`. To do this I have a middlware in my `API` package to handle CORS requests. 

My issue is that CORS connections are accepted on production domain `api.acme.com`, but the connections are rejected on `api-dev.acme.com` and `api-staging.acme.com`. 

This is the middlware to handle CORS:


### `apps/api/middleware.ts`
```ts
import { NextRequest, NextResponse } from 'next/server';

import { AcmeURLConfig } from '@acme/config';

const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Credentials': 'true',
};

const { IS_DEVELOPMENT } = AcmeURLConfig('API');

export function middleware(request: NextRequest) {
  // Check the origin from the request
  const origin = request.headers.get('origin') ?? '';
  const isAllowedOrigin = IS_DEVELOPMENT
    ? true
    : origin.match(/acme\.com$/)?.length;

  // Handle preflighted requests
  const isPreflight = request.method === 'OPTIONS';

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
      'Access-Control-Allow-Headers':
        request.headers.get('access-control-request-headers') || '',
    };
    return NextResponse.json({}, { headers: preflightHeaders });
  }

  // Handle simple requests
  const response = NextResponse.next();

  if (isAllowedOrigin) {
    response.headers.set('Access-Control-Allow-Origin', origin);
  }

  Object.entries(corsOptions).forEach(([key, value]) => {
    response.headers.set(key, value);
  });

  // Copy the request headers to the response
  response.headers.set(
    'Access-Control-Allow-Headers',
    request.headers.get('access-control-request-headers') || '',
  );

  return response;
}

export const config = {
  matcher: '/api/:path*',
};
```




### Current vs. Expected behavior

CORS is rejected on `api-dev.acme.com` and `api-staging.acme.com`, but allowed on `api.acme.com`. It should be allowed on all `*.acme.com` domains.

All apps/environments are deployed on Vercel using custom domains assigned to git branches. I suspect Vercel rejects CORS on non-production instances. 

### Provide environment information

```bash
  System:
    OS: Linux 6.6 Manjaro Linux
    CPU: (32) x64 AMD Ryzen Threadripper 1950X 16-Core Processor
    Memory: 51.25 GB / 62.65 GB
    Container: Yes
    Shell: 5.9 - /usr/bin/zsh
  Binaries:
    Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
    npm: 10.5.0 - ~/.nvm/versions/node/v18.19.0/bin/npm
    pnpm: 9.3.0 - ~/.local/share/pnpm/pnpm
  Browsers:
    Brave Browser: 125.1.66.118
    Chromium: 126.0.6478.55
  npmPackages:
    @tanstack/react-query: ^5.40.1 => 5.40.1 
    @trpc/client: 11.0.0-rc.401 => 11.0.0-rc.401+91720833b 
    @trpc/next: 11.0.0-rc.401 => 11.0.0-rc.401+91720833b 
    @trpc/react-query: 11.0.0-rc.401 => 11.0.0-rc.401+91720833b 
    next: ^14.2.3 => 14.2.3 
    react: ^18.3.1 => 18.3.1 
    typescript: 5.3.3 => 5.3.3 
```

### `apps/[api & app]/next.config.mjs`

```ts
import dotenv from 'dotenv';

import { SetupNextConfigForTailwind } from '@acme/ui/tailwind-utils';

try {
  if (process.env.NODE_ENV === 'development') {
    process.stdout.write(`\x1b]2;${'API'}\x1b\x5c`);
  }
} catch (error) {
  // Ignore error
}

dotenv.config({
  override: true,
});

// For local development, we need to load the .env.local file from the root of the monorepo
dotenv.config({
  path: '../../.env.local',
});

// For local development with git worktrees, we need to load the .env.local file from the root *bare* repository
dotenv.config({
  path: '../../../.env.local',
});

/** @type {import('next').NextConfig} */
const config = {
  transpilePackages: ['@acme/ui'],
  webpack: (cfg) => {
    // Grab the existing rule that handles SVG imports
    const fileLoaderRule = cfg.module.rules.find((rule) =>
      rule.test?.test?.('.svg'),
    );

    cfg.module.rules.push(
      // Reapply the existing rule, but only for svg imports ending in ?url
      {
        ...fileLoaderRule,
        test: /\.svg$/i,
        resourceQuery: /url/, // *.svg?url
      },
      // Convert all other *.svg imports to React components
      {
        test: /\.svg$/i,
        issuer: fileLoaderRule.issuer,
        resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
        use: ['@svgr/webpack'],
      },
    );

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i;

    return cfg;
  },
};

export default SetupNextConfigForTailwind(config);

```

### Which area(s) are affected? (Select all that apply)

Middleware

### Which stage(s) are affected? (Select all that apply)

Vercel (Deployed)

### Additional context

I am not interested in modifying `next.config.mjs` file because I need the flexibility of the middlware.