【ITニュース解説】Things that were harder than expected building a drag-and-drop static host
2026年09月16日に「Dev.to」が公開したITニュース「Things that were harder than expected building a drag-and-drop static host」について初心者にもわかりやすく解説しています。
ITニュース概要
ドラッグ&ドロップ静的ホスト開発では、相対リンク解決、多様なファイル名エンコード、重い処理の永続ワーカー化、多言語UIの一貫性テスト、サイトのSEOやセキュリティなど、予想外の課題に直面。シンプルな機能でも細部の考慮が重要だった。
ITニュース解説
ファイルをドラッグ&ドロップするだけでWebサイトを公開できるシンプルなサービスは、一見すると簡単に作れそうに思える。しかし、実際に開発を進めていくと、予想外の複雑な問題に直面することが少なくない。ある開発者が「plopino.com」という静的ホストを構築する中で経験した具体的な困難な点を解説しよう。
まず、URLの構造が最初の大きな課題となる。多くのサービスはプロジェクトごとに「my-project.host.com」のようなサブドメインを割り当てるが、このサービスでは「plopino.com/b/<boardId>/...」のように、メインサイトのパスの一部として各ボード(ユーザーのWebサイト)をホストする方式を採用した。この方式は、TLS証明書の管理やDNS設定がシンプルになるという利点がある。一つの証明書で全体をカバーでき、サブドメインごとに設定を更新したり、その変更がインターネット全体に反映されるのを待つ必要がなくなるためだ。
しかし、このパスベースのホスティングは、ユーザーがアップロードするHTMLコンテンツ内のリンクの解釈を複雑にする。ユーザーのボードは任意のHTMLを含んでおり、「../../other.html」のような相対リンクや、「/absolute/path」のようなルート相対リンクが存在する。システムは、これらのリンクがボード内部のコンテンツを指すのか、それともボードの外部、つまりサイト全体や全く別の場所を指すのかを厳密に判断する必要がある。特に、「/style.css」のようなルート相対リンクは、ボード内のCSSファイルを指すように見えるが、実際にはサイト全体のルートにある「plopino.com/style.css」を要求してしまう。このため、ルート相対リンクはボード外部のリンクとして扱うのが安全な設計となる。この判断は、ストレージの計上や、ボードが自身の範囲外にアクセスできるかどうかを決定する上で非常に重要となる。
次に、ファイルのアップロードとZipファイルの処理には、メモリ効率とパフォーマンスが求められる。ユーザーがフォルダをドラッグ&ドロップでアップロードする際、その構造(例: site/css/main.css)を保つために、ファイル解析ツール(Busboy)の設定でパス情報を維持する必要がある。また、アップロードされた各ファイルは、メモリに一時的にバッファリングせず、直接一時ファイルにストリーミングする設計が重要だ。これは、大容量のファイルがアップロードされた場合でも、サービスのメモリ使用量を最小限に抑え、パフォーマンスを維持するためである。Zipファイルの解凍についても同様に、一度に全てのファイルをメモリに展開せず、一つずつ遅延的に処理するライブラリ(yauzl)を利用することで、数千のファイルを含むZipでも安定して処理できる。
さらに厄介な問題として、ファイル名のエンコーディングがある。Zipファイル形式には、ファイル名の文字エンコーディングが明確に規定されていないため、作成ツールやオペレーティングシステムによって異なるエンコーディング(CP437、UTF-8、あるいは中国語環境で使われるGB18030など)が使われることが頻繁にある。また、HTTP通信でファイル名を伝える際も、Content-Dispositionヘッダのfilenameパラメータとfilename*パラメータの扱いにブラウザ間で一貫性がない。これらの問題を無視すると、例えば中国語ロケールのコンピューターからアップロードされたファイル名が文字化け(いわゆる「モジバケ」)してしまい、ユーザー体験を著しく損ねる。このため、受け取ったバイト列をまずUTF-8として解釈し、それが失敗した場合はGB18030などの他のエンコーディングを試すといった、多段階のデコード処理が必要となる。
ボードのサムネイル生成やOffice文書のプレビュー表示には、実際のブラウザレンダリングエンジン(例えばヘッドレスChromium)が必要となる。リクエストごとにレンダリングプロセスを起動すると、動作が遅くなり、サーバーのCPU負荷も高騰してしまう。これを解決するため、レンダリング処理は「永続的なワーカープール」と呼ばれる仕組みで実行される。これにより、レンダラーは常に起動状態にあり、新しいリクエストが入ってもすぐに処理を開始できる。処理はキューに入れられ、一定の負荷を超えないように制御されるため、急なリクエストの増加にも耐えられる。また、サムネイル生成時には、プレビューページのサイトロゴやダウンロードボタン、サイドバーといったUI要素を一時的に非表示にし、ユーザーのコンテンツ本体だけをキャプチャするような細かい調整も必要となる。これにより、生成されるサムネイルがコンテンツを正確に反映したものになる。
サービスが多言語対応(英語、日本語、中国語、アラビア語など20以上の言語)をする場合、翻訳ファイルの品質と整合性を保つことが大きな課題となる。特に、アラビア語やペルシャ語のような右から左に記述する言語(RTL言語)にも対応する場合、UIの表示方向なども考慮しなければならない。このサービスでは、翻訳ファイルの整合性を確保するために、通常のテストに加えて「翻訳キーの順序」を強制する特別なテストを導入した。これにより、全ての言語の辞書ファイルが、キーのセット、キーの順序、そして文字列内のプレースホルダー名において英語の辞書ファイルと完全に一致することを保証する。キーの順序までチェックすることで、手作業で編集されて以降メンテナンスされなくなった翻訳ファイルを検出し、翻訳漏れが発生してもサイレントに英語がそのまま表示されるのではなく、テスト失敗として検出されるようになる。また、クローラーやSNSのスクレイパーはJavaScriptを実行しないため、titleやdescription、og:*、hreflangといったSEOに関わるメタタグは、サーバー側で言語ごとに正しくレンダリングして出力することが不可欠である。
最後に、匿名アップロードを許可するサービスでは、悪用防止策とコンテンツの管理が重要となる。不正な利用を防ぐため、IPアドレスごとに1日あたりのアップロード回数を制限し、このカウンターはサービスが再起動してもリセットされないように永続化する。アカウントを持つユーザーには、より多くの許容量を与える。モデレーションに関しては、ユーザーからの悪用報告に基づき、問題のあるコンテンツを調査し、削除する仕組みを構築する。これは、見知らぬユーザーから任意の内容のHTMLをホストするサービスにおいて、最も避けられない質問に対する回答となる。また、アップロードされたボードを検索エンジンのインデックス対象にするかどうかの判断も重要だ。デフォルトで全てのボードをインデックスさせない設定(X-Robots-Tag: noindex)にしておき、ユーザーが公開設定にしたボードのみインデックス可能にする。これにより、プライベートなボードのURLが検索結果に残り続け、文脈なしのリンクとして表示されるのを防ぎつつ、公開されたコンテンツは正しく検索可能となる。ただし、サイトマップにはユーザーコンテンツを一切含めず、あくまでユーザーが意図的に共有した場合にのみ検索可能とする方針である。
これらの経験から学べる教訓として、開発者はサービス開始の初期段階でURLの構造を決定することの重要性を強調する。一度決めた構造を後から変更するのは、リンク解決ロジック、TLS設定、SEOなど多岐にわたる影響があるため、非常に困難となる。また、ファイル名のようなユーザーからの入力は、常に悪意のある、あるいは不完全なデータが含まれる可能性があるとみなし、複数のエンコーディングに対応できる堅牢な処理を実装する必要がある。ユーザーが提供するコンテンツをレンダリングする際には、レンダリングプロセスを常に準備しておき、リクエストをキューに入れて処理する仕組みがパフォーマンスと安定性の鍵となる。そして、多言語対応を進めるならば、翻訳品質を保証するためのテストは、言語の追加が進む前に早期に導入すべきである。これらの課題は、一見シンプルなサービスでも、実際に構築する際には深く考慮すべき複雑な側面があることを示している。