<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>cloudflare on デジタルアーカイブシステムの技術ブログ</title><link>https://tech.ldas.jp/ja/tags/cloudflare/</link><description>Recent content in cloudflare on デジタルアーカイブシステムの技術ブログ</description><generator>Hugo</generator><language>ja</language><lastBuildDate>Tue, 07 Apr 2026 11:00:00 +0900</lastBuildDate><atom:link href="https://tech.ldas.jp/ja/tags/cloudflare/index.xml" rel="self" type="application/rss+xml"/><item><title>Cloudflare Zero TrustでSSHを保護する</title><link>https://tech.ldas.jp/ja/posts/cloudflare-zero-trust-ssh/</link><pubDate>Tue, 07 Apr 2026 11:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/cloudflare-zero-trust-ssh/</guid><description>&lt;h1 id="cloudflare-zero-trustでsshを保護する">Cloudflare Zero TrustでSSHを保護する&lt;/h1>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>サーバにSSHでアクセスするには、通常22番ポートをインターネットに公開する必要がある。しかし公開されたSSHポートは常に攻撃の標的になる。&lt;/p>
&lt;p>Cloudflare Zero Trustを使えば、&lt;strong>SSHポートを閉じたまま&lt;/strong>、認証済みのユーザーだけがSSH接続できる環境を構築できる。&lt;/p>
&lt;h2 id="zero-trustとは">Zero Trustとは&lt;/h2>
&lt;p>従来のセキュリティモデルでは「社内ネットワーク内は信頼する」という前提があった。Zero Trustはこの前提を捨て、&lt;strong>全てのアクセスを検証する&lt;/strong>モデル。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">【従来】
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">インターネット → ファイアウォール → 社内（信頼済み）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ 一度入れば自由にアクセス可能
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">【Zero Trust】
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">インターネット → Cloudflare（認証・認可） → サーバ
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ 毎回アクセスを検証する
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ 認証されていなければ接続不可
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="仕組み">仕組み&lt;/h2>
&lt;p>SSHの場合、Cloudflare TunnelとAccess機能を組み合わせる。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">開発者のPC
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── ssh コマンド
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── cloudflared（ProxyCommand）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── Cloudflare（Access認証）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── Tunnel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── サーバのSSHデーモン
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>SSHコマンドを実行すると、&lt;code>cloudflared&lt;/code>がプロキシとして動作&lt;/li>
&lt;li>Cloudflare Accessが認証を要求（ブラウザでメール認証）&lt;/li>
&lt;li>認証成功後、Tunnel経由でサーバのSSHに接続&lt;/li>
&lt;li>サーバ側のSSHポートは閉じたまま&lt;/li>
&lt;/ol>
&lt;h2 id="前提条件">前提条件&lt;/h2>
&lt;ul>
&lt;li>Cloudflare Tunnelが設定済みであること（&lt;a href="https://tech.ldas.jp/ja/posts/cloudflare-tunnel/">Cloudflare Tunnelの設定方法&lt;/a>を参照）&lt;/li>
&lt;li>ローカルPCに&lt;code>cloudflared&lt;/code>がインストール済みであること&lt;/li>
&lt;/ul>
&lt;h2 id="手順">手順&lt;/h2>
&lt;h3 id="1-tunnel-ingressにsshを追加">1. Tunnel IngressにSSHを追加&lt;/h3>
&lt;p>Cloudflare APIでSSHのルーティングを追加する：&lt;/p></description></item><item><title>Cloudflare Tunnelで学術サーバを安全に公開する</title><link>https://tech.ldas.jp/ja/posts/cloudflare-tunnel/</link><pubDate>Tue, 07 Apr 2026 10:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/cloudflare-tunnel/</guid><description>&lt;h1 id="cloudflare-tunnelで学術サーバを安全に公開する">Cloudflare Tunnelで学術サーバを安全に公開する&lt;/h1>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>学術研究用のサーバでElasticsearch（全文検索）やCantaloupe（IIIF画像配信）を運用する場合、通常はサーバのポートを外部に公開する必要がある。しかしポートを開放すると、脆弱性を突いた攻撃のリスクが生まれる。&lt;/p>
&lt;p>Cloudflare Tunnelを使えば、&lt;strong>サーバのインバウンドポートを一切開けずに&lt;/strong>、サービスを安全に外部公開できる。&lt;/p>
&lt;h2 id="cloudflare-tunnelとは">Cloudflare Tunnelとは&lt;/h2>
&lt;p>通常のサーバ公開では、サーバ側がポートを開けて外部からの接続を待ち受ける（インバウンド接続）。Cloudflare Tunnelはこの構造を逆転させる。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">【従来】
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">外部 → (80/443ポート) → サーバ
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ サーバがポートを開けて待ち受ける
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">【Cloudflare Tunnel】
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">外部 → Cloudflare → ← サーバ（cloudflared）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ サーバ側からCloudflareに接続しに行く（アウトバウンド）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ※ インバウンドポートは不要
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>サーバ上で動作する&lt;code>cloudflared&lt;/code>というエージェントが、Cloudflareに対してアウトバウンド接続を維持する。外部からのリクエストはCloudflareが受け取り、このトンネル経由でサーバに転送される。&lt;/p>
&lt;h2 id="メリット">メリット&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>ポート開放不要&lt;/strong>: インバウンドポートを全て閉じられる&lt;/li>
&lt;li>&lt;strong>WAF・DDoS防御&lt;/strong>: Cloudflareが自動で攻撃を吸収&lt;/li>
&lt;li>&lt;strong>SSL自動化&lt;/strong>: Let&amp;rsquo;s Encryptの設定やリバースプロキシ（Traefik等）が不要&lt;/li>
&lt;li>&lt;strong>無料&lt;/strong>: Tunnelは無料プランで利用可能&lt;/li>
&lt;/ul>
&lt;h2 id="構成">構成&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Cloudflare
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── iiif-cf.example.jp → Cantaloupe (8182)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── es-cf.example.jp → Elasticsearch (9200)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ Tunnel（暗号化済み）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">サーバ（Docker）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── cloudflared（Tunnelエージェント）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── elasticsearch（全文検索）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── cantaloupe（IIIF画像配信）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="手順">手順&lt;/h2>
&lt;h3 id="1-cloudflareにドメインを登録">1. Cloudflareにドメインを登録&lt;/h3>
&lt;p>Cloudflareのダッシュボードでドメインを追加し、レジストラ（お名前.com等）のネームサーバをCloudflareに変更する。&lt;/p></description></item><item><title>ネームサーバの仕組み</title><link>https://tech.ldas.jp/ja/posts/nameserver/</link><pubDate>Tue, 07 Apr 2026 09:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/nameserver/</guid><description>&lt;h1 id="ネームサーバの仕組み">ネームサーバの仕組み&lt;/h1>
&lt;h2 id="ネームサーバとdnsの違い">ネームサーバとDNSの違い&lt;/h2>
&lt;p>ほぼ同じ文脈で使われますが、厳密には異なります。&lt;/p>
&lt;div class="table-wrapper">
 &lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;/th>
 &lt;th>DNS&lt;/th>
 &lt;th>ネームサーバ&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>何か&lt;/td>
 &lt;td>仕組み・ルール全体の名前&lt;/td>
 &lt;td>その仕組みの中で動く実際のサーバ&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>例えるなら&lt;/td>
 &lt;td>「電話帳のシステム」&lt;/td>
 &lt;td>「電話帳を持っている窓口の人」&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>
&lt;p>&lt;strong>DNS（Domain Name System）&lt;/strong> は、ドメイン名からIPアドレスを調べるための仕組み全体を指します。プロトコル、ルール、レコードの形式などを含む概念です。&lt;/p>
&lt;p>&lt;strong>ネームサーバ&lt;/strong> は、DNSという仕組みの中で実際に問い合わせに回答するサーバのことです。&lt;strong>DNSサーバ&lt;/strong>とも呼ばれますが、同じものです。&lt;/p>
&lt;blockquote>
&lt;p>「ネームサーバ」=「DNSサーバ」（呼び方が違うだけ）&lt;/p>
&lt;p>紛らわしいのは「DNS」が2つの意味で使われること：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>DNS&lt;/strong> = 仕組み全体（Domain Name System）&lt;/li>
&lt;li>&lt;strong>DNSサーバ&lt;/strong> = その仕組みの中で動くサーバ = ネームサーバ&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">DNS = 仕組み全体
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── ネームサーバ = 問い合わせに答えるサーバ（例: kellen.ns.cloudflare.com）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── DNSレコード = サーバが持っている対応表（A, CNAME, MX, NS, TXT...）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── リゾルバ = ユーザー側で問い合わせを行うプログラム
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── プロトコル = 問い合わせのルール（UDP 53番ポートなど）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>日常的には「DNS変更した」「ネームサーバ変更した」はほぼ同じ意味で使われます。厳密に言い分けるなら：&lt;/p>
&lt;ul>
&lt;li>「&lt;strong>ネームサーバを変更した&lt;/strong>」= 問い合わせ先のサーバを変えた&lt;/li>
&lt;li>「&lt;strong>DNSレコードを変更した&lt;/strong>」= 対応表の中身を変えた&lt;/li>
&lt;/ul>
&lt;h2 id="dnsサーバはどれだけの情報を持っているのか">DNSサーバはどれだけの情報を持っているのか&lt;/h2>
&lt;p>Cloudflareのような大手DNSプロバイダは、全ユーザーのDNSレコードを自社サーバに保持しています。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">【CloudflareのDNSサーバ】
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── example.jp の対応表
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── someone-else.com の対応表
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── big-company.io の対応表
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── ... 数千万ドメイン分
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>これは膨大な数ですが、1つのDNSレコードは数十〜数百バイト程度の小さなデータです。仮に1億レコードあっても数十GB程度で、現代のサーバであれば全てメモリに載る量です。&lt;/p></description></item><item><title>Elasticsearch → Static JSON / D1 移行検証 — 小規模データなら全文検索エンジンは不要だった</title><link>https://tech.ldas.jp/ja/posts/elasticsearch-to-static-json-and-d1/</link><pubDate>Mon, 06 Apr 2026 22:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/elasticsearch-to-static-json-and-d1/</guid><description>&lt;p>Cloudflare Pages上で動くNext.js製の日本語テキスト検索APIで、Elasticsearchの代替としてCloudflare D1（SQLite）とStatic JSON（インメモリ検索）を実装し、3方式の検索性能を比較しました。&lt;/p>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>古典日本語テキストの全文検索APIを運用しています。Elasticsearchを外部クラスタとして利用していましたが、以下の理由で代替を検討しました。&lt;/p>
&lt;ul>
&lt;li>外部サービスへの依存を減らしたい&lt;/li>
&lt;li>Cloudflare Pages内で完結させたい&lt;/li>
&lt;li>データ量が小さい（約1,800件）ので、全文検索エンジンは過剰かもしれない&lt;/li>
&lt;/ul>
&lt;h2 id="データ規模">データ規模&lt;/h2>
&lt;div class="table-wrapper">
 &lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>値&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>レコード数&lt;/td>
 &lt;td>1,812件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>テキスト総量（UTF-8）&lt;/td>
 &lt;td>約2.5 MB&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>1レコード平均&lt;/td>
 &lt;td>約1.4 KB&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>
&lt;p>各レコードは古典日本語テキスト（数行〜十数行）、ページ番号、巻名、IIIFキャンバスURLで構成されています。&lt;/p>
&lt;h2 id="3方式の概要">3方式の概要&lt;/h2>
&lt;h3 id="1-elasticsearch既存">1. Elasticsearch（既存）&lt;/h3>
&lt;p>外部Elasticsearchクラスタに対して、fetch APIでwildcardクエリを実行する方式です。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wildcard&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s1">&amp;#39;original_text_lines.keyword&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`*&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">*`&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ngramアナライザーでインデクシングしていますが、実際の検索ではwildcardクエリ（&lt;code>*query*&lt;/code>）を使っており、ngramインデックスの恩恵を受けていませんでした。&lt;/p>
&lt;h3 id="2-cloudflare-d1sqlite">2. Cloudflare D1（SQLite）&lt;/h3>
&lt;p>Cloudflare D1にデータを格納し、&lt;code>LIKE&lt;/code>で部分一致検索する方式です。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">page&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">original_text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vol_str&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">texts&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">original_text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIKE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%検索語%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">page&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ASC&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OFFSET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>集計（ファセット）はSQLの&lt;code>GROUP BY&lt;/code>で実現します。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vol_str&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">doc_count&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">texts&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">original_text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIKE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%検索語%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vol_str&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vol_str&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ASC&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>D1の&lt;code>batch()&lt;/code>APIで検索・カウント・集計の3クエリを同時実行できます。&lt;/p></description></item><item><title>Vercel ProをHobbyに下げるためにNext.jsアプリをCloudflare Pagesに移行した</title><link>https://tech.ldas.jp/ja/posts/vercel-pro-to-hobby-cloudflare-migration/</link><pubDate>Mon, 06 Apr 2026 19:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/vercel-pro-to-hobby-cloudflare-migration/</guid><description>&lt;p>Vercel Proプランで60以上のNext.jsプロジェクトを運用していましたが、組織向けアプリをCloudflare Pagesに移行することで、Hobbyプラン（無料）へのダウングレードを実現しました。&lt;/p>
&lt;h2 id="背景">背景&lt;/h2>
&lt;h3 id="vercel-proの課題">Vercel Proの課題&lt;/h3>
&lt;p>Vercel Hobbyプランは&lt;strong>非商用・個人利用のみ&lt;/strong>です。組織のアプリをホストしている場合、Proプラン（$20/月〜）を維持する必要があります。&lt;/p>
&lt;p>しかし、60以上あるプロジェクトのうち組織向けアプリは2つだけでした。この2つをCloudflareに移せば、残りは個人プロジェクトとしてHobbyプランで運用できます。&lt;/p>
&lt;h3 id="移行対象">移行対象&lt;/h3>
&lt;ul>
&lt;li>多言語対応（日本語/英語）の検索アプリ（Next.js + next-intl + Elasticsearch）&lt;/li>
&lt;li>IIIF画像ビューアアプリ&lt;/li>
&lt;/ul>
&lt;p>いずれもカスタムドメインで運用中。&lt;/p>
&lt;h2 id="nextjs-16の壁--proxytsが未対応">Next.js 16の壁 — proxy.tsが未対応&lt;/h2>
&lt;p>最初にそのままCloudflare Pagesへの移行を試みましたが、ビルドの最終段階でエラーが発生しました。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Node.js middleware is not currently supported.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Consider switching to Edge Middleware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next.js 16では従来の &lt;code>middleware.ts&lt;/code> が &lt;code>proxy.ts&lt;/code> にリネームされ、&lt;strong>Node.jsランタイム固定&lt;/strong>になりました。&lt;code>@opennextjs/cloudflare&lt;/code> v1.18.0時点ではこの新しい &lt;code>proxy.ts&lt;/code> に未対応です（&lt;a href="https://github.com/opennextjs/opennextjs-cloudflare/issues/962">opennextjs/opennextjs-cloudflare#962&lt;/a>）。&lt;/p>
&lt;h3 id="解決策nextjs-15にダウングレード">解決策：Next.js 15にダウングレード&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># package.json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;next&amp;#34;&lt;/span>: &lt;span class="s2">&amp;#34;^15.3.1&amp;#34;&lt;/span>, &lt;span class="c1"># 16.x → 15.x&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;next-intl&amp;#34;&lt;/span>: &lt;span class="s2">&amp;#34;^3.26.5&amp;#34;&lt;/span>, &lt;span class="c1"># 4.x → 3.x（Next.js 15対応版）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-config-next&amp;#34;&lt;/span>: &lt;span class="s2">&amp;#34;^15.3.1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同時に &lt;code>proxy.ts&lt;/code> を &lt;code>middleware.ts&lt;/code> にリネームし、エクスポート名も &lt;code>proxy&lt;/code> → &lt;code>middleware&lt;/code> に変更しました。&lt;/p>
&lt;h2 id="cloudflare-pagesへのデプロイ手順">Cloudflare Pagesへのデプロイ手順&lt;/h2>
&lt;h3 id="1-依存パッケージのインストール">1. 依存パッケージのインストール&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev @opennextjs/cloudflare wrangler
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-設定ファイルの作成">2. 設定ファイルの作成&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// open-next.config.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">defineCloudflareConfig&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;@opennextjs/cloudflare&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">defineCloudflareConfig&lt;/span>&lt;span class="p">({});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsonc" data-lang="jsonc">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// wrangler.jsonc
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;my-app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pages_build_output_dir&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;.open-next/assets&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;compatibility_date&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2025-04-01&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;compatibility_flags&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;nodejs_compat&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-ビルド">3. ビルド&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npx @opennextjs/cloudflare build
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-pagesへのデプロイここが最大のポイント">4. Pagesへのデプロイ（ここが最大のポイント）&lt;/h3>
&lt;p>&lt;code>@opennextjs/cloudflare&lt;/code> のビルド出力はWorkers向けですが、Pagesにデプロイするには以下の手順が必要です。&lt;/p></description></item><item><title>AWS AmplifyからCloudflare Pagesへの移行 — Next.js APIサーバーの月額$23→$0化</title><link>https://tech.ldas.jp/ja/posts/amplify-to-cloudflare-pages-migration/</link><pubDate>Mon, 06 Apr 2026 17:00:00 +0900</pubDate><guid>https://tech.ldas.jp/ja/posts/amplify-to-cloudflare-pages-migration/</guid><description>&lt;p>Next.js製のAPIサーバーをAWS AmplifyからCloudflare Pagesに移行しました。月額約$23（Amplify $15 + WAF $8）のコストが$0になりました。&lt;/p>
&lt;h2 id="背景">背景&lt;/h2>
&lt;h3 id="移行前の構成">移行前の構成&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>フレームワーク&lt;/strong>: Next.js 15（App Router）&lt;/li>
&lt;li>&lt;strong>ホスティング&lt;/strong>: AWS Amplify（WEB_COMPUTE / SSR）&lt;/li>
&lt;li>&lt;strong>WAF&lt;/strong>: Amplifyが自動作成したWebACL&lt;/li>
&lt;li>&lt;strong>バックエンド&lt;/strong>: 外部のElasticsearchに接続するAPIルート&lt;/li>
&lt;li>&lt;strong>月額コスト&lt;/strong>: Amplify $15 + WAF $8 = &lt;strong>約$23/月&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>無料枠（12ヶ月）が切れた後、1アプリのSSRホスティングとしてはやや割高でした。特にWAFはAmplify作成時に自動的に有効化されており、$5/WebACL/月 + リクエスト課金で$8程度かかっていました。&lt;/p>
&lt;h3 id="なぜcloudflare-pagesか">なぜCloudflare Pagesか&lt;/h3>
&lt;div class="table-wrapper">
 &lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>Amplify&lt;/th>
 &lt;th>Vercel&lt;/th>
 &lt;th>Cloudflare Pages&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>SSR対応&lt;/td>
 &lt;td>有料&lt;/td>
 &lt;td>個人無料/チーム$20/人&lt;/td>
 &lt;td>無料&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>チーム利用&lt;/td>
 &lt;td>IAM管理&lt;/td>
 &lt;td>有料&lt;/td>
 &lt;td>&lt;strong>無料&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>帯域&lt;/td>
 &lt;td>15GB後従量&lt;/td>
 &lt;td>100GB/月&lt;/td>
 &lt;td>&lt;strong>無制限&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>カスタムドメイン&lt;/td>
 &lt;td>CloudFront経由&lt;/td>
 &lt;td>簡単&lt;/td>
 &lt;td>CNAME設定で可能&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>
&lt;p>VercelはNext.jsとの相性が最も良いですが、チーム利用は有料（$20/ユーザー/月）です。Cloudflare Pagesは無料プランでもチーム・商用利用が可能で、帯域も無制限です。&lt;/p>
&lt;h2 id="移行手順">移行手順&lt;/h2>
&lt;h3 id="1-opennextjscloudflare-のインストール">1. @opennextjs/cloudflare のインストール&lt;/h3>
&lt;p>Cloudflare上でNext.jsを動かすために、&lt;a href="https://opennext.js.org/cloudflare">@opennextjs/cloudflare&lt;/a>を使います。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev @opennextjs/cloudflare wrangler
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-open-nextconfigts-の作成">2. open-next.config.ts の作成&lt;/h3>
&lt;p>プロジェクトルートに設定ファイルを作成します。&lt;/p></description></item></channel></rss>