Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
323 stars 54 forks source link

Hybrid Next.js deployment middleware host name #1012

Open cerimorse opened 1 year ago

cerimorse commented 1 year ago

The Problem

There doesn't seem to be a way to access the original host when using Next.js middleware as part of the recently released hybrid deployment. We're currently using the middleware layer to rewrite the URL pathname based on the subdomain. To do this we need access to the original host name.

To Reproduce

Add a middleware layer to any Next.js hybrid deployment and observe the host header. A basic middleware implementation, taken from a Vercel hostname rewrites example, might look like this:

import { NextRequest, NextResponse } from 'next/server'
import { getHostnameDataOrDefault } from './lib/db'

export const config = {
  matcher: ['/', '/about', '/_sites/:path'],
}

export default async function middleware(req: NextRequest) {
  const url = req.nextUrl

  // Get hostname (e.g. vercel.com, test.vercel.app, etc.)
  const hostname = req.headers.get('host')

  // If localhost, assign the host value manually
  // If prod, get the custom domain/subdomain value by removing the root URL
  // (in the case of "test.vercel.app", "vercel.app" is the root URL)
  const currentHost =
    process.env.NODE_ENV === 'production' &&
    hostname.replace(`.${process.env.ROOT_DOMAIN}`, '')
  const data = await getHostnameDataOrDefault(currentHost)

  // Prevent security issues – users should not be able to canonically access
  // the pages/sites folder and its respective contents.
  if (url.pathname.startsWith(`/_sites`)) {
    url.pathname = `/404`
  } else {
    console.log('URL 2', req.nextUrl.href)
    // rewrite to the current subdomain under the pages/sites folder
    url.pathname = `/_sites/${data.subdomain}${url.pathname}`
  }

  return NextResponse.rewrite(url)
}

When running this locally, the host header correctly returns the expected host name. When deploying to Azure Static Web Apps using the Next.js hybrid deployment, the host header returns a value similar to <randomly-generated-string>.azurewebsites.net. I assume this is the host value of the build time generated managed function app that the middleware is running on.

Expected Result

I think there are two possible expected results:

  1. The host header should reflect the domain name used to access the static web app
  2. We should be able to access the original host name via the X-Forwarded-Host header

Possible Solution

I can see that there is already the ability to access the original url via the x-ms-original-url header that was added in this pull request. If this is already possible, hopefully it shouldn't be too difficult to add the X-Forwarded-Host header in a similar manner (unless I've overlooked something!).

erwinsmit commented 1 year ago

Hi @cerimorse

I was able to resolve this by checking for the request header "x-forwarded-host". When using a custom domain this header was accessible through the Nextjs middleware, when a default URL is used (ending with .azurestaticwebapp.net) the header is not there. So I was able to add a redirect to the custom domain.

mburumaxwell commented 5 months ago

Same issue but cannot access the X-Forwarded-Host header from next.config.js.

/** @type {import('next').NextConfig} */
const config = {
  output: 'standalone',
  async redirects() {
    return [
      {
        source: '/',
        destination: 'https://contoso.com/products',
        has: [{ type: 'host', value: 'contoso.app' }],
        permanent: false,
      },
    ];
  },
};

export default config;

Removing the host condition in has works but that's not the intended behaviour.

@thomasgauvin seen you writing posts recently so I thought I could tag you on this one. Since this has been pending for a while is there any particular guideline on how such situations should be handled?

yannbolliger commented 4 months ago

Yep, would be great to use has with type: 'host'.