はじめに
PHP2Shell の件で、各所が緊急の更新に追われています。他人事ではありませんでした。
LAMP の環境で 9 年ほど仕事をしてきて、最後にいたのはサポートの切れた PHP 7 と古いフレームワークで動いているのに、更新したくてもその時間を取らせてもらえない現場でした。
いまはもう PHP を離れていますが、言語を変えても同じことは起こりえます。公開する側でランタイムを動かしている限り、緊急の更新はいつか回ってきます。なら、そもそも置かなければいい。今回はその話です。
公開側にランタイムがなければ、この種の穴は開かない
PHP2Shell で狙われるのは、Web のリクエストを受けて動いている PHP です。
裏返せば、リクエストを受けてコードを動かすものが公開側になければ、この種の穴はそもそも開きません。HTML や JSON を事前に作って、S3 のようなストレージに置き、CDN から配る。返すのはファイルだけです。
L(Linux)・A(Apache)・M(MySQL)で身につけたことは、この形でも無駄になりません。権限をどう切るか、データをどう持つか、どこで何を返すか。考えることは同じで、変わるのは、それを公開側で動かし続けるかどうかです。
一覧や検索も、ランタイムなしで出せる
「それでも、データを一覧にしたり、検索させたりしたいことはある」と思われるかもしれません。公開側でクエリを動かさなくても、出し方はあります。
SSG のフレームワークで、JSON に書き出しておく
SSG は、React をやりたくて勉強していたころに覚えました。ただ、いまの現場ではまったく使っていません。今回のことで調べてみて、こういう要件で使うものだったのか、と驚きました。
いちばん手軽なのは、SSG のフレームワークに任せることです。データを更新するたびにビルドし直し、画面で使う切り口ごとに JSON を書き出しておきます。画面は fetch() で読むだけです。公開側にデータベースがないので、そこが落ちることも、そこを突かれることもありません。
Astro なら、src/pages/data.json.ts のようなエンドポイントが、ビルドのときに /data.json というファイルになります。Next.js でも、静的な書き出し(output: 'export')にして、GET の Route Handler に dynamic = 'force-static' を付ければ、同じように JSON のファイルを書き出せます。
文章の検索なら、Pagefind で足りる
記事やドキュメントを検索させたいだけなら、Pagefind のような道具があります。ビルドしたあとの HTML から索引を作り、ブラウザは検索のときに、小分けにした索引の必要な分だけを読みにいきます。サーバは要りません。
趣味の案: データ基盤の形でなんとかする
ここからは、ここしばらくの私の趣味です。何でもデータ基盤でなんとかしようとする癖があるので、強引に見えたら、そういうことです。
集計は手元や CI の DuckDB で行い、Parquet と JSON に書き出して置きます。私がいま作っている海運データのダッシュボードは、この形です。画面は静的なファイルで、その JSON を読んでいます。
件数が数万〜数十万件になる、あるいは画面の側で自由に絞り込みや集計をさせたいなら、もう一歩進めて、Parquet をそのままブラウザから読む方法もあります。ブラウザで動く DuckDB-Wasm から、ストレージに置いた Parquet に SQL を投げます。
SELECT port, count(*) FROM 'https://example.com/data.parquet' WHERE year = 2026 GROUP BY port;
Parquet は列ごとにまとまった形式なので、ファイル全体を落とさずに、必要な部分だけを読めます。サーバ側で動く計算はありません。ブラウザから別のオリジンのファイルを読むので、ストレージ側で CORS を設定しておく必要があります。使い方は DuckDB のドキュメントに詳しく書かれています。
AI に書かせたコードは、公開側に出さない
いまは、データの加工から静的なページの生成まで、AI に指示すれば短い時間で一通り書けます。以前なら、入稿用の管理画面を作るだけでも一仕事でした。
ただ、AI が書いたコードと、中身をよく知らない依存ライブラリを、そのままインターネットに出して動かし続けるのは、なかなか怖いことです。
手元や CI で生成して、出てきたファイルだけを置く形なら、公開側に出るのは出力物だけです。生成のコードが多少粗くても、そのコードが外からのリクエストを受けることはありません。
おわりに
公開側にランタイムを置かずに済むシステムなら、置かなければいいだけじゃないかという発想です。保守を頑張るのではなく、保守する対象をそもそも持たない。
もちろん、すべてのシステムがこの形にできるわけではありません。ログインや書き込みがあるなら、どこかで何かを動かす必要があります。生成する側の CI や、ストレージに書き込む権限は、引き続き守る対象です。ストレージの公開範囲を誤れば、置いてはいけないものまで外から読めてしまいます。※少しずれますが.env を gitignore し忘れるケースに備えて、bot は決め打ちでファイルを探します。お気をつけて。
私には、Web は何かしらの Web フレームワークで作るもの、という思い込みがありました。簡単なものなら S3 に置くだけで配信できる、と Claude Code に言われたのを思い出して、書いてみたのがこの記事です。
なにせ、サーバ側の言語が分かっている Web サイトのうち、7 割ほどが PHP だそうです。ここに挙げたやり方のどれかで助かる現場もあるのではないかと思います。
興味のある方の参考になれば幸いです。ご指摘は歓迎します。
参考文献
- W3Techs「Usage statistics of PHP for websites」(2026-10-27 時点で 69.2%) https://w3techs.com/technologies/details/pl-php
- PHP「Security advisory: Critical remote code execution vulnerabilities, update now」(2026-10-21) https://www.php.net/
- GMO Flatt Security「PHPの重大な脆弱性『php2shell』(CVE-2026-109618、CVE-2026-109619)の概要と対応指針」 https://blog.flatt.tech/entry/php2shell
- Astro「Endpoints」 https://docs.astro.build/en/guides/endpoints/
- Next.js「How to create a static export of your Next.js application」 https://nextjs.org/docs/app/guides/static-exports
- Pagefind https://pagefind.app/
- DuckDB「Data Ingestion(DuckDB-Wasm)」 https://duckdb.org/docs/stable/clients/wasm/data_ingestion
- maritime-lake(海運データのダッシュボード) https://github.com/dkurokawa/maritime-lake