Next.js の allowedDevOrigins をいい感じに設定する
Next.js 14.2.30 / 15.2.2 以降で開発サーバーに外からアクセスすると次のような警告が出るようになった。
 ⚠ Blocked cross-origin request from 192.168.xxx.yyy to /_next/* resource. To allow this, configure "allowedDevOrigins" in next.config
Read more: https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins
これは脆弱性対応によるもの。詳しくは公式アナウンスを読むと良い。
追加されたオプションは allowedDevOrigins というもので、ここに指定したドメインとクロスオリジンなアクセスは警告を出す。現在はまだ警告のみでアクセスをブロックしたりしない。ブロック挙動は将来有効化されるらしい。
つまり、開発サーバーに外からアクセスする必要がある場合、今のうちにこのオプションを理解しておく必要がある。
設定
allowedDevOrigins には開発サーバーをたてているマシンの IP アドレスやホスト名を指定する。
next.config.ts などで次のように指定する。
export default {
  allowedDevOrigins: ["192.168.xxx.yyy", "your.hostname"]
}
設定例で気付いた方もいるだろうが、この値は人によって異なるのでチームメンバー間で統一できない。次の理由もあって、単純に next.config.ts に書いて共有できない類の設定だ。
- ホスト名はスクリプトで簡単に取得できるがそもそも設定していない場合がある
 - 最近のマシンは NIC が複数ささっているのが普通なので、どれを使うのかはマシンのユーザーでないと判別できない
 - IPv4 と IPv6 どちらを使うのかは好みなので、適切な IP アドレスは自動取得できない。
 
というわけで、メンバーが各自で設定できるようにするため、 allowedDevOrigins などローカル固有の設定を next.config.ts 内で動的に読み込むという形をとってみた。
まず次を定義する。プロジェクトルート直下にある next.config.local.cjs を動的に読み込んで Next.js 設定として追加する関数だ。
import type { NextConfig } from "next";
import { join } from "path";
import { existsSync } from "fs";
import { dirname } from "path";
const LOCAL_CONFIG_GENERATOR = "next.config.local.cjs";
export type LocalConfigGenerator = NextConfig | (() => NextConfig);
function findProjectRootSync(): string {
  let currentDir = process.cwd();
  while (currentDir !== "/") {
    const packageJsonPath = join(currentDir, "package.json");
    if (existsSync(packageJsonPath)) {
      return currentDir;
    }
    currentDir = dirname(currentDir);
  }
  return process.cwd();
}
export async function withLocalConfig(config: NextConfig) {
  let localConfigGenerator: { default: LocalConfigGenerator } | null = null;
  try {
    const projectRoot = findProjectRootSync();
    const configPath = join(projectRoot, LOCAL_CONFIG_GENERATOR);
    localConfigGenerator = await import(configPath);
    console.log(`${LOCAL_CONFIG_GENERATOR} is loaded`);
  } catch (e) {
    if (e instanceof Error) {
      if (!("code" in e && e.code == "MODULE_NOT_FOUND")) {
        console.error(`loading ${LOCAL_CONFIG_GENERATOR} raised error: `, e);
      }
    }
  }
  const localConfig =
    typeof localConfigGenerator?.default === "function"
      ? localConfigGenerator.default()
      : localConfigGenerator?.default ?? {};
  return {
    ...config,
    ...localConfig,
  };
}
で、こう使う。
import { withLocalConfig } from "@/utils/withLocalConfig";
import type { NextConfig } from "next";
const config: NextConfig = {
  // チーム内で共有する設定
};
export default withLocalConfig(config);
関数で指定できたほうが汎用的かなと思って、 next.config.local.cjs ファイルにローカル設定を書く形とした。 JavaScript で書かなければならないのは、Next.js が立ち上がるときには next.config.ts はトランスパイルされて JS として実行されるから。
個人ごとで違うので Git 管理されないように .gitignore に追加しておく。
next.config.local.cjs
ここまでを commit し、チーム内で共有する。
各メンバーはそれぞれ、次のように設定を書く。
module.exports = {
  allowedDevOrigins: ["192.168.xxx.yyy", "your.hostname"],
}
関数で書きたい場合は次のように。何か処理を挟みたいときに使うと良い。
module.exports = () => {
  // なにか処理を書く
  return {
    allowedDevOrigins: ["192.168.xxx.yyy", "your.hostname"],
  };
}
これで警告が出なくなる。
Discussion