プログラミング
C言語によるモダンなWebアプリケーション
A Modern Web App in C (prophitt.me)
要約
この記事では、C言語とCGI(Common Gateway Interface)を用いて、現代的なWebアプリケーションを構築した経験が語られています。著者は、依存関係が少なく、シンプルでセキュアなスタック(OpenBSD、C、httpd、SQLite)で商用製品を開発し、その利点と実現可能性を詳細に解説しています。CGIは古い技術ですが、現代のインフラストラクチャと組み合わせることで、依然として強力な選択肢となり得ることが示されています。
全文翻訳
INTRO
1994年に動的なウェブサイトを作成したい場合、Cプログラムを書き、それをcgi-binディレクトリに配置し、リクエストごとにApacheにフォークさせて実行させていました。32年後、私は基本的に同じ技術を使って商用Web製品を出荷しました。
marketyour.gamesのすべてはモダンです。ユーザーアカウント、コンテンツクリエイター向けの検索ダッシュボード、整理用のフォルダ、Stripe経由の支払い、Postmark経由のトランザクションメールを備えたWeb製品です。プログレッシブエンハンスメントに従っており、JavaScriptを必要としません。
モダンでないのは、その作り方と提供方法だけです。2026年にC言語で実際の製品を書き、CGI経由で提供することについての私の考えを述べます。
TLDR
CGIの衰退は2004年には正当化されましたが、2026年には通用しません。
C言語でモダンなWebアプリケーションを書くことができます。私はそれを実現しました。単一のバイナリ、1つのSQLiteファイル、3つの依存関係で、すべてがあなたの頭の中に収まります。
HOW IT WAS DONE
スタックはBCHSです:OpenBSD、C、httpd、SQLite。
フレームワークはありません。Cプログラムを書き、CGI環境変数を読み取り、応答します。
テンプレート言語はありません。Cで直接HTMLを出力します。
独立したデータベースサーバーはありません。SQLiteのC APIを通じてデータベースファイルを開きます。
コンテナはありません。静的にリンクされたバイナリをビルドし、フォルダに配置します。
すべてのリクエストは新しいプロセスを生成します。新しいバージョンのデプロイは、1つのファイルを置き換えることです。つまり、サーバーの再起動やダウンタイムはありません。新しいリクエストは新しいバイナリをそのまま引き継ぎます。
OpenBSDは健全なデフォルト設定を提供し、pledgeやunveilのようなCプログラムを実際に保護するためのツールを提供します。Httpdはデフォルトでchrootからサービスを提供するため、CGIバイナリはファイルシステムの自分のコーナーにサンドボックス化されて実行されます。
私はさらに3つのライブラリに依存しています:
libcurlは、StripeとPostmarkへのすべての送信リクエストを処理します。
libsodiumは、ランダムな識別子生成、パスワードハッシュ、Stripe WebhookのHMAC検証を処理します。
libcjsonは、StripeとPostmarkのJSON解析を処理します。
これらの中でビルド依存関係となるのは、libcurlとlibsodiumの2つだけです。
libcjsonは単一のCファイルであり、外部依存関係はありません。
SQLiteは公式のアマルガム(単一Cファイル)ビルドを提供します。
どちらもプロジェクトの一部としてベンダーされ、ビルドされます。
結果として、ほとんど動く部分のないビルドが完成しました。クリーンなビルドは数秒で完了し、再ビルドは0.5秒未満で完了します。
静的アセットを従来の方法で提供する代わりに、httpdからのすべてのトラフィックをCGIバイナリにルーティングします。すべてのアセットは実行可能ファイルにコンパイルされ、メモリから提供されます。
WHAT I LIKED
フレームワークよりも基礎。
CGIは学習するフレームワークではありません。1.1仕様は2004年に公開され、それ以来変更されていません。
内部化すべきAPIはありません。インターフェース全体は、少数の基本的なHTTP概念で構成されています:
リクエストヘッダー。
リクエストメソッド。
クエリ文字列。
ボディデータ。
HTTPを理解していれば、CGIも理解していることになります。
言語とプラットフォームに依存しません。一度学べば、その知識は永遠に有効です。
懸念事項の明確な分離。
モダンなWebアプリケーションは通常、nginxのようなWebサーバーを前面に置き、Ruby、Python、Go、Rustなどで書かれたアプリケーションサーバーにリバースプロキシします。それは私には常に奇妙に感じられました:実質的に2つのWebサーバーがあり、そのうちの1つがビジネスロジックも担当しているのです。
CGIは、単一のWebサーバーがビジネスロジックを直接呼び出すことを可能にします。誰が何をするかは明確です。
デバッグも簡単になります。アプリケーションはシェルから実行できる通常のプログラムです:リクエストをパイプで渡し、stderrを監視し、gdbでステップ実行します。
ある意味で、CGIはサーバーレスアーキテクチャに似ています:スーパーバイザーがリクエストごとにコードを呼び出し、環境変数の入ったバッグを渡し、結果を収集します。CGIはサーバーレスですが、ベンダー(と予期せぬ請求)はありません。
最小限のサプライチェーンリスク。
自律エージェントの台頭とパッケージリポジトリへの毎週の攻撃を考えると、依存関係が非常に少ないことは安心感があります。
ほとんどのモダンな技術スタックには、パッケージマネージャーとそれに付随するエコシステムが付属しています。それは便利ですが、依存関係グラフは膨張します。1つか2つの直接的な依存関係が、何百もの間接的な依存関係を引き込む可能性があります。それぞれが、あなたが聞いたこともない誰かによって保守されており、2015年から再利用されているパスワードで保護された公開認証情報を持っています。
私のサプライチェーンはCコンパイラと3つのライブラリです。それを侵害するには、libcurlを侵害する必要があります…インターネット全体が監視しているプロジェクトであり、週に11回のダウンロードがあり、2019年に開発者が去ったオーナーがいるパッケージではありません。
シンプルなツール。
私は本質的にミニマリストです。プラグインゼロのクラシックVimを使っています。つまり、LSP、オートコンプリート、コードフォーマッター、AIはありません。
複雑な技術スタックは複雑なツールを要求します:抽象化の層をかき分けるための言語サーバーを要求します。しかし、このスタックはシンプルなので、プレーンなエディタで十分です。アプリケーション全体は、CファイルのディレクトリとMakefileです。
ファイルを開き、それを読み、それが何をするかを知ることができます。優れたツールは手の届くところにあります。
ツールが必要な場合、それは遠くありません。Sanitizers、valgrind、gdb、ctags、cscope…これらは成熟した(まあ、老人級の)ツールであり、何十年にもわたって洗練されており、Cをコンパイルできるほとんどのシステムでパッケージインストールで入手できます。
成熟度はツール自体を超えています:多くのエディタに統合が組み込まれています。Vimはctags、cscopeのネイティブサポートを備えており、グラフィカルなgdbフロントエンド(:h Termdebugを参照)があります。
C言語でHTMLとCSSを書くのはそれほど悪くありません。
フロントエンド全体をC言語で書くことに躊躇していましたが、想像していたほど苦痛ではありませんでした。
あまりにも多くの時間を費やしてきました:
Goのhtml/template構文をハイライトするためにvimと格闘する。
html検出を壊さずにGoテンプレートを検出するためにhelixと格闘する。
askamaを使用するプロジェクトのためにjinjaテンプレートを認識するようにrustroverと格闘する。
C言語を使用するということは、エディタが半検出する第二言語がないこと、ハイライトが壊れる混合構文がないこと、2つの言語を協力させるためのプラグインダンスがないことを意味します。
ただC言語を書くだけでリフレッシュできました。それは美しくはありませんが、Tailwindも美しくはありません。
SQLiteは新鮮な空気です。
私は常にWebアプリケーションのためにPostgresをデフォルトにしてきました。それは素晴らしいデータベースですが、運用上の負担が伴います。世話をするサーバープロセスがあり、頻繁にアップグレードする必要のあるリリースがあります。
SQLiteにはそれらがありません。プロセスも、設定も、依存関係もありません。アプリケーションはデータベースファイルを直接開きます。運用上の負担は最小限です。
WHAT I WOULD CHANGE
CGIはWebサーバーとアプリケーションの間のインターフェースです。フレームワークやライブラリはありません。これは、箱から出してすぐに得られるものが非常に少ないことを意味します。
以下のようなことを処理するために、独自のユーティリティを書く必要があります:
Cookieの解析と作成。
クエリパラメータの解析と検証。
フォームデータの解析と検証。
表示用にインプットのエスケープ。
私は単一のヘッダーファイル(cgi.h)で自分で作成しましたが、それが正しい解決策だったとは確信していません。kcgiのようなライブラリは存在しますが、それらを採用するということは、そのAPIとセキュリティモデルを受け入れることを意味します。
開発者がドロップインできる(stbライブラリのような)共有ユーティリティのセットがあればいいと思います。
おそらく、まずcgi.hを公開するかもしれません。
CONCLUSION
CGIは古いですが、忘れられてはいません。
2000年代初頭に私たちがそれを放棄した理由のほとんどは、今日では通用しません。プロセスフォークは、かつてほど高価ではなくなりました。現代のカーネルはそれを大幅に最適化しています。
そして、フォークのコストが問題になったとしても、FastCGIは単純なアップグレードです。
C言語でモダンなWebアプリケーションを書くことは、十分に可能です。marketyour.gamesは、2004年に公式に標準化された提供モデルを使用した単一のバイナリで実行されている商用製品です。
私がこれまでにリリースしたものの中で、最も動く部分が少ないです。私は再びこのスタックに頼るでしょう。