Vercel Authentication breaks Multi-Zone rewrite domain

I’m using a Vercel Multi-Zone setup where one project acts as the public entry point, and another project is mounted behind it via rewrites.

The issue happens only when Vercel Authentication / Deployment Protection is enabled on the destination zone.

In short:

  • Project A is the public/root domain.
  • Project B is mounted under Project A via a rewrite.
  • Without Vercel Authentication on Project B, the browser stays on Project A’s domain as expected.
  • With Vercel Authentication enabled on Project B, the authentication flow redirects the browser to Project B’s domain.

I would like to keep the browser URL on Project A’s domain while still protecting direct access to Project B’s preview/deployment URLs.

Current behavior

Given this setup:

https://a.example.com/some-path
  -> rewrite to Project B

When Vercel Authentication is disabled on Project B, the browser URL remains:

https://a.example.com/some-path

This is the expected Multi-Zone rewrite behavior.

However, when Vercel Authentication is enabled on Project B, accessing the same path from Project A causes the browser to enter the Vercel Authentication flow and eventually navigate to Project B’s domain.

For example, the browser may end up at something like:

https://b.vercel.app/some-path

or another Project B deployment/custom domain.

So the user-facing domain changes from Project A to Project B.

Expected behavior

When a user accesses Project B through Project A’s rewrite, I expected the browser URL to remain on Project A’s domain throughout the authentication flow.

Expected:

https://a.example.com/some-path

The destination zone should be protected, but the user should not be redirected to or exposed to Project B’s domain.

Ideally, I would like this behavior:

User -> https://a.example.com -> authenticated
https://a.example.com -> rewrite/proxy -> protected Project B
Direct access to Project B URL -> still protected
Browser URL -> always remains https://a.example.com

Configuration

Project A has a rewrite to Project B.

Example vercel.json / rewrite configuration:

{
  "rewrites": [
    {
      "source": "/some-path/:path*",
      "destination": "https://b.example.com/:path*"
    }
  ]
}

or conceptually:

/some-path/:path* -> https://b.vercel.app/:path*

Steps to reproduce

  1. Create Project A on Vercel.

  2. Assign a custom domain to Project A, for example:

    https://a.example.com
    
  3. Create Project B on Vercel.

  4. Add a rewrite in Project A that forwards a path to Project B.

    Example:

    https://a.example.com/some-path
      -> https://b.example.com
    
  5. Open:

    https://a.example.com/some-path
    
  6. Confirm that the browser URL stays on a.example.com when Vercel Authentication is disabled on Project B.

  7. Enable Vercel Authentication / Deployment Protection on Project B.

  8. Open the same URL again:

    https://a.example.com/some-path
    
  9. The browser is redirected through the Vercel Authentication flow and ends up on Project B’s domain instead of staying on Project A’s domain.

Question

Is this behavior expected when using Vercel Authentication with Multi-Zone rewrites?

If it is expected, what is the recommended architecture for this use case?

Specifically:

  1. Can Vercel Authentication preserve the original host when the request comes through a rewrite?
  2. Is there a supported way to protect Project B while keeping the browser URL on Project A?
  3. Is using x-vercel-protection-bypass from a server-side proxy in Project A the recommended approach?
  4. Can this be done with Vercel rewrites directly, or does it require a Route Handler / Middleware / custom reverse proxy?

Project information

  • Framework: Next.js
  • Hosting: Vercel
  • Architecture: Multi-Zone
  • Project A: public/root project with the user-facing custom domain
  • Project B: destination zone mounted behind Project A via rewrite
  • Environment: Preview and Production
  • Vercel Authentication / Deployment Protection:
    • Disabled on Project B: rewrite works and the browser stays on Project A’s domain
    • Enabled on Project B: browser is redirected to Project B’s domain during authentication

Security concern

A possible workaround is to disable Vercel Authentication on Project B and only protect Project A.

However, if Project B is not protected, then Project B’s preview/deployment URLs may be directly accessible by anyone who knows the URL.

So I am looking for a setup where:

Access through Project A -> allowed after authentication
Direct access to Project B preview/deployment URL -> protected
Browser URL -> always remains on Project A's domain

Hi ya2s,

I’d treat this as a limitation of combining a plain Multi-Zone rewrite with Vercel Authentication on the destination project.

A rewrite can keep the browser URL on Project A for normal traffic, but once Project B’s Deployment Protection challenges the request, the auth flow belongs to Project B. That means the redirect/cookie flow can reasonably end up on Project B’s deployment/custom domain instead of staying fully masked behind Project A.

For the architecture you want, I don’t think a vercel.json rewrite alone is the right layer, because it can’t safely attach a private bypass secret or manage the auth flow for the destination. I’d consider one of these patterns instead:

  1. Put the user-facing protection at Project A, and do not expose Project B as a user entry point. This is simplest, but only works if direct Project B deployment URLs being reachable is acceptable or separately handled.

  2. Use app-level auth shared by both zones, rather than Vercel Authentication on Project B. That keeps the multi-zone routing model clean because both apps decide access based on the same session/cookie/auth provider.

  3. If Project A must be the only entry point and Project B must stay protected, use a real server-side proxy in Project A and send Project B’s Protection Bypass for Automation header from the server only:

await fetch("https://b.example.com/some-path", {
  headers: {
    "x-vercel-protection-bypass": process.env.VERCEL_AUTOMATION_BYPASS_SECRET!,
  },
})

I would not put x-vercel-protection-bypass in a client-visible URL or static rewrite destination, because that would expose the bypass secret. Keep it in a server-only environment variable.

The main tradeoff is that proxying a whole Next.js zone through a Route Handler can get messy with assets, streaming, cookies, redirects, and relative URLs. If Project B is a full app zone, I’d lean toward shared app-level auth across zones. If Project B is mostly API/routes or a smaller internal surface, the server-side proxy + bypass header is more reasonable.

Vercel’s bypass header behavior is documented here:
https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation