🌧️

next-on-pagesを使ってデプロイしてたページをopennext.jsを利用したデプロイに移行する

に公開

Next.js製のサイトをいい感じにCloudflare上のサービスに展開するためnext-on-pagesを利用していた

が、Next.js15.2.0のリリース時に対応できず(デプロイしてもInternal Server Errorが起きるようになった/ 2025-04-22追記:どうやらNext.js15.3.0で解決したらしい )、実際next-on-pagesはNext.js14までを対応ということで進めるようで、それ以降のバージョンを使うならOpenNEXTを使うというのが方針となって固まりそうであった

ちなみにこの2つのツールの関係と、そもそもなぜ2つあるのかについては下記のサイトに詳しい

https://laiso.hatenablog.com/entry/2024/10/12/000528

Next.jsも大きなCVEが報告されアップデートできないのは困るので、今までnext-on-pagesでデプロイしていたページをOpenNEXTを利用してデプロイできるよう移行し、そこで行ったことをまとめようと思う

資料

ひとまず公式ドキュメント

Cloudflare側のドキュメント
https://developers.cloudflare.com/workers/frameworks/framework-guides/nextjs/

OpenNEXT側のドキュメント
https://opennext.js.org/cloudflare/get-started

作業

1. opennextjs/cloudflareをプロジェクトに入れる

$ npm install @opennextjs/cloudflare@latest

新規に入れるのはこれだけ(既存のプロジェクトなのでwranglerや.dev.varsの設定は済んでいるとする

2. 新規ファイルを作成する

プロジェクトルートに open-next.config.ts を作成

open-next.config.ts
import { defineCloudflareConfig } from '@opennextjs/cloudflare'

export default defineCloudflareConfig()

いろいろ設定はあるようだがひとまずこれでいい

3. 既存のファイルを変更する

package.json

まず package.json にコマンド類を追加・変更する

package.json
{
  // 略
  "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
  "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
  "cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
  // 略
}

このあたりは wrangler や next-on-pages を利用していた部分の書き換えになる

ちなみにだが opennextjs-cloudflare deploy は --env/-e で wrangler -env 相当の設定ができる(特にドキュメントとかヘルプとか見ても書いてなかったがソースコードがそうなってたのでたぶんできる

wrangler.toml

続いて wrangler.toml ファイルを書き換える。ドキュメントではjsonc形式だが既存のプロジェクトがあるのでそちらから引き継いだとしてtomlで書き続けることにする(もしjsonだったらいい感じに変換してやればいい

mainとassetsを変更しているが、今回は新しくデプロイすることを考慮しnameも変更する

wrangler.toml
main = ".open-next/worker.js"
name  = "pagename-v2"
# 変更がない部分は略

[assets]
directory = ".open-next/assets"
binding = "ASSETS"
# その他略

また、Binding等を利用していた場合など一部の記法がPagesとWorkersで違う(バージョンによるもの?)のでそこも変更したほうが良さそうである

実のところ、Pagesでは検証環境との兼ね合いでenvの設定を大きく変更しなければならなかったのでかなり書き換えることになったx

.gitignore

以下を追加

.open-next

.dev.vars

以下の変数を追加する

.dev.vars
NEXTJS_ENV=development
# 以下略

next.config.mjs

開発用の設定を読み込むように変更する。next-on-pagesでは setupDevPlatform() というのがあったのでそれのOpenNEXT版

next.config.mjs
import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare'

// 略
await initOpenNextCloudflareForDev()
// 略

.tsxでのedge指定を削除

今までCloudflare Pagesで動かすためにはedgeランタイムでビルドする必要があったが、Workersでの動作はその設定は不要なのでそれらを消す必要がある

next.configファイルで設定している場合は一括で済むが、そうでない場合すべての指定を削除する必要がある

[filename].(ts|tsx)
/// 略
export const runtime = 'edge' // ←これを消す
/// 略

頑張って消していこう。エディタの機能を使えばすぐだが

context取得処理を修正

CloudflareのBinding機能等を利用する際にcontextとenvにアクセスするが、この処理を書き換える

[filename].(ts|tsx)
import { getRequestContext } from '@cloudflare/next-on-pages' // これを
import { getCloudflareContext } from '@opennextjs/cloudflare' // これに変える

それぞれ返り値は同じようなので呼び出し場所も同様に変更する

4. 開発時に使用していた環境変数を .env ファイルに移行

追記:なぜか突然 .env ファイルを読まずに .dev.vars を読んでくれるようになったためもしかしたらこの項は不要かも。next dev起動時に .env ファイルを読み込んだか出るのでそのタイミングで精査する

next-on-pagesは.dev.varsにおいていた変数も上記のnext.config.mjsの初期化処理で読んでくれていたようだが、opennextの初期化処理は読んでくれないらしい(opennext側のドキュメントでは読むっぽいように書いているが読んでくれなかった。もしかしたら単にバグで今後読んでくれるかも

必要な環境変数があればそれを .env ファイルに移す必要があるのでそれを移す

5. next devで開発環境起動

とりあえず上記の全てをこなして npm run dev したところ無事起動した

6. next-on-pagesを削除

どっかでやっとかないといけないやつ。next devをやる前の手順でやるべきだったかもしれない

$ npm uninstall @cloudflare/next-on-pages

一応これをやったらもう一回 next dev しておこう

7. preview

next devが動いたら次はpreviewを動かす

ここでエラーが起きたのでいくつか対応を残す

edgeではdynamicだったページがstaticになるため処理できない

CMSからデータを取ってくるページの中にはページパスがstaticなものがあったが、これらは runtime = 'nodejs' ではビルド時にページ生成しようとする。それではまずいので

export const dynamic = 'force-dynamic'

上記を記述してSSGなページに変更

bindingで呼び出すworkerの処理の対応

当然だがローカルで起動する場合BindingしているWorkerは呼び出せない

このBindingしているWorkerは、開発時はパッケージとして読み込んで直接関数を呼び出しているが、ビルド時にはWebpackのexternals設定で置き換えている

next.config.mjs
const nextConfig = {
  // 略
  webpack: (config, option) => {
    if (!option.dev) {
      config.externals.push({
        'worker-package': 'worker-package',
      })
    }
    return config
  },
  // 略
}

これがビルドする際に取り除かれるので、previewで起動する場合に読み込めなくてエラーになるという問題がある

とりあえず環境変数を設定してそれで分岐条件を追加したところとりあえず通ったのでそれで進める

next.config.mjs
const nextConfig = {
  // 略
  webpack: (config, option) => {
    if (!option.dev && process.env.LOCAL !== 'true') {
      config.externals.push({
        'worker-package': 'worker-package',
      })
    }
    return config
  },
  // 略
}

ただビルド時にやはりうまく行かなかったので、一旦 config.external.push() 自体を設定から外すことにした

previewするときにbindしてるworkerのwrangler.tomlを読めない

従来のpreviewでは下記のようにしていた

"preview": "wrangler pages dev -c wrangler.toml -c ../bind-worker/wrangler.toml"

これをopennextjs-cloudflareでやろうとすると

"preview": "opennextjs-cloudflare preview -- -c wrangler.toml -c ../bind-worker/wrangler.toml"

とやれば一応できるっぽい。できるっぽいのだが、エラーで落ちる

npm error code EUSAGE
npm error
npm error Run a command from a local or remote npm package
npm error
npm error Usage:
npm error npm exec -- <pkg>[@<version>] [args...]
npm error npm exec --package=<pkg>[@<version>] -- <cmd> [args...]
npm error npm exec -c '<cmd> [args...]'
npm error npm exec --package=foo -c '<cmd> [args...]'
npm error
npm error Options:
npm error [--package <package-spec> [--package <package-spec> ...]] [-c|--call <call>]
npm error [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
npm error [-ws|--workspaces] [--include-workspace-root]
npm error
npm error alias: x
npm error
npm error Run "npm help exec" for more info
npm error A complete log of this run can be found in: /home/maretol/.npm/_logs/2025-04-19T01_29_48_610Z-debug-0.log

というエラーでどこで死んだかわからないがログの方を見ると verbose stack に

verbose argv "exec" "wrangler" "dev" "--call" "wrangler.toml" "--call" "../bind-worker/wrangler.toml"

というログがあった

おそらくだが、正しく記述するなら"exec" "--" "wrangler" "dev" "--config" "wrangler.toml" "--config" "../bind-worker/wrangler.toml"としなければならない(exec のあとに -- をつけるのと、--callではなく--config

exec -- になってないのはシンプルな不具合っぽいが -c が --call になってるのは謎

解決方法が思いつかないので、しょうがないということにして opennextjs-cloudflare preview ではなく wrangler dev で直接呼んでしまおうということで以下のように変える

"preview": "opennextjs-cloudlare build && wrangler dev -c wrangler.toml -c ../bind-worker/wrangler.toml"

一応この場合でも wrangler.toml に書いた main が呼ばれるため問題なく起動する

起動後ページを開くと connection lost でページが落ちる

一瞬ページが表示されたあと Internal Server Error で落ちる

サーバ側のログには Error: Network connection lost. が出ている

調べた感じ上記の wrangler -c で複数のWorkerを立ち上げService Bindingのローカル再現がうまく行ってないと思われる感じであった

こればっかりは修正できないので一旦パスする。一応回避方法を考えた中だとローカルビルド時はパッケージとして読み込むか、あるいは別のWorkerとして wrangler dev で立ち上げてhttpでつなぐかといったところか


これ以上の格闘は骨が折れそうだったので一旦previewの再現は一旦止めることにする。まあベータだからね

stagingとして同じデプロイ先環境を作っているのでできることでもある。そっちで稼働を試してみることにする

8. buildコマンドを変える

忘れてた

"build": "opennextjs-cloudflare build"

9. デプロイする

今回のプロジェクトはもともとGithub Actionsを利用して自動でデプロイしていたのでその部分を変更する。opennextjs-cloudflare deploy でもできるが wrangler deploy でデプロイできる

Pagesとはwrangler.tomlファイルの読み方が微妙に違うところがあるのでそこは適宜修正する必要がある

nameを変更している場合、別のWorkersでデプロイされるのでとりあえず workers.dev 等を利用してページが動いていることを確認する。問題なければドメインのルーティング先を変更して本番環境差し替えといく

実質ダウンタイムほぼなしで変更もできるはず。今回は個人サイトでアクセス数もかなり少ないので気にせず1時間ぐらいダウンタイムを作ったが

secret等の設定が必要になるためそういうのも忘れずに(忘れてて時間つぶした

10. (追記)Pagesでしか使えなかった処理を修正

忘れてた(というかできてないことに後で気づいた)ので追記

Cloudflare Pagesでしか使えない機能で実装されている部分をWorkersの処理に変更する必要がある

このプロジェクトではPages Functionsを使って処理してた部分があったのでそこを修正する必要があった

まとめ

とりあえずページをPagesに乗っていたページがWorkersに乗るまでをやれた

ただ開発環境でのpreviewがうまく行かなかったことは心残り。BindingしてるServiceの接続状態がそもそも出てなかったので詳細はわからない

やってみて思ったのはまだopennext.js自体が開発途中なので難しいなという点か。結局wranglerコマンドでやったシーンが多いのはそのため

opennextjs-cloudflare のCLIもバグなのか未対応なのかわからない部分が多いし、--helpでもとくにヘルプが出てこないので大変だった

Workers周りはWranglerとかもいまいちよくわからん部分が出てきがちなのでそこはぶっちゃけどの方法でもかなり大変だと思います

あと今回触れなかったのは大丈夫だったというだけですが、ビルド成果物のサイズが3MiB越えると無料枠を超えてしまうという問題もあります(3MiBだったっけ?うろ覚えなので必要になったら各々調査してください

もともとPagesで動いてたこともありかなり軽量に作ってるつもりのプロジェクトだったので大丈夫でしたが、場合によってはPagesなら乗るけどWorkersは乗りませんとかあるかもしれないです

それもそれでかなり大変そうなので結構覚悟しないといけないかもしれません。いっそもうちょっとまってCloudflareにContainerサービスが使えるようになるまで待って、Standaloneビルドで成果物のNode系のコンテナにおいてデプロイとかしたほうが楽になる可能性もあります。ケースバイケースですが業務で使うと大変そうだなぁと思いました

Discussion