こんにちは。私はサーバーを持たない「完全フロントエンド主義」の疑似デスクトップ環境、SF-OS を開発・運用しています。HTML、Vanilla JS(ピュアなJavaScript)、そしてCSSだけで構成され、現在260以上の自作ツール群が統合されて動いています。
実は明後日、念願の「新アプリ30個の一斉追加」と「ウインドウのタイリング配置機能」という、SF-OS史上最大規模のアップデートをデプロイする予定でした。
ワクワクしながら最終調整をしていたその時、開発者としての平穏な日常を吹き飛ばす「エラー」がブラウザのコンソールに現れたのです。
🛑 前夜の悲劇:突然すべてのアプリが開かなくなった
"Refused to display 'https://sfosdemo.bitbucket.io/' in a frame because it set 'X-Frame-Options' to 'deny'."
SF-OSは、中核インフラである「jsPanel 4」を使い、ブラウザー内のデスクトップ上に生成したウィンドウ(iframe)の中に各ツールを読み込む設計になっています。しかし、そのiframeが突如として真っ白になり、何一つ起動しなくなってしまいました。
エラーの意味はシンプルです。これまでソースコードの管理とコンテンツ配信に愛用していた「Bitbucket Cloud(*.bitbucket.io)」の配信サーバーが、「このサイトのコンテンツをiframeの中に埋め込むことを一律で禁止する」という強力なセキュリティヘッダー(X-Frame-Options: DENY)を返し始めたのです。
調べてみると、近年GitHub Pagesなどの大手無料ホスティングでもフィッシング詐欺や不正スクリプト対策として同様の厳格化が進んでおり、ついにその波がBitbucketにも事前告知なしの「サイレント適用」として襲いかかってきたようでした。
外部プラットフォームに依存する個人開発の、最も恐れていたリスクが現実になった瞬間でした。
🔒 最大の難所:利用者のLocalStorage(データ)をどう救うか?
「無料ホスティングがダメなら、自前のレンタルサーバー(ロリポップ!)に引っ越せばいい」
結論はすぐに出ました。幸い、SF-OS作業用に sf-os.work という独自ドメインをもともと取得してありましたし、ロリポップには信頼性の高い日次7世代バックアップもあるため、今後の「目標500ツールへの拡張」を考えても新天地としては完璧です。実際にテスト環境を作ってファイルを一括展開してみると、何の問題もなくiframe内で各アプリがカチッと軽快に動き出しました。やっぱりブラウザのバグではなく、Bitbucket側のサーバー設定が原因だったわけです。
しかし、ここで最大の壁が立ちはだかります。「利用者のLocalStorage(ユーザーデータ)の保護」です。
SF-OSは「完全フロントエンド主義」を貫いているため、利用者が各ツールで作成・蓄積した設定やデータは、外部のサーバーではなく、すべてユーザー自身のブラウザ(LocalStorageやIndexedDB)に保存されています。データ自体はユーザーの端末に安全に残っています。
しかし、Webの世界には「Same-Originポリシー(同源原則)」という絶対的なセキュリティルールがあります。データはURL(ドメイン)ごとに厳格に隔離されるため、サーバーを bitbucket.io から sf-os.work に引っ越した瞬間、新しいURLからは過去のデータに一切アクセスできなくなってしまうのです。
「ドメインが変わるからデータは諦めてください」と言うのは簡単です。しかし、少ないとはいえSF-OSを信じてデータを蓄積してくれた大切な利用者に、そんな不快で不誠実な思いをさせるわけにはいきません。
💡 起死回生の「激変緩和措置」:別タブ強制起動(裏口)作戦
新ドメインに移行する前に、なんとかして旧ドメインの権限のまま、利用者が安全にデータをエクスポート(書き出し)できるルートを作れないか。
コードを睨みつけ、SF-OSの中核である「ウィンドウ生成関数(seisei)」に目をつけました。
「既存の260個のアプリの起動コード(イベントリスナー)には一切手を触れず、この親関数の中身だけを書き換えて、iframeではなく『別タブ』で強制起動させればどうだろう?」
iframeの中に閉じ込めるからサーバーにブロックされるのです。ブラウザの標準機能である window.open を使い、独立した新しいタブとしてツールを開いてしまえば、X-Frame-Options の制限を完全に回避できます。しかも、起動する場所は元の旧ドメインのままなので、LocalStorage内のユーザーデータにも完璧にアクセスできます。
思い立ったら即行動です。旧サーバー側に、データ救出専用のページ(indexothertab.html)を1枚配置し、seisei 関数を以下のように数行だけ書き換えました。
function seisei(title, url) {
// iframe生成の代わりに、別タブ(新しいウィンドウ)で直接ツールを開く
// ツールごとに固定のタブ名を割り当てて、多重起動防止の挙動をブラウザ機能で再現
var tabName = title.replace(/\s+/g, '_');
window.open(url, tabName);
}
テスト用のアプリを動かしてみると……大成功です!
ツールが別タブで綺麗に立ち上がり、これまでに蓄積されていたLocalStorageのデータが何事もなかったかのように画面上に呼び出されました。
これで、利用者がこの裏口ページから各ツールを開き、バックアップ(エクスポート)機能を実行して手元にデータを保存し、それを新ドメイン側でインポートすれば、1つもデータを失うことなく新天地へ引っ越せる「救出ルート」が完成しました。
🏁 まとめ:トラブルは強固な独立への第一歩
現在の対応状況ですが、Bitbucketのコミュニティに投げたダメ元の問い合わせの回答(おそらく一律解除不可という切ない回答になりますが)を明後日まで待つことにしています。
回答を待って、旧サーバーのトップページを、今回作成した「データ救出ルートへのリンク付き・誠実な技術開示を載せた移転案内ページ」へと差し替える予定です。
本当なら、30個の新アプリ追加とタイリング配置機能のデプロイという、最高に楽しいお祭りの日になるはずでした。突然のインフラトラブル対応に追われ、一時はがっくりと気力が削がれそうになりましたが、今ではこう思っています。
「SF-OSの神様が、これからの大進化を前に、誰の気まぐれにも左右されない『自分だけの安全な城(独自ドメイン)』へ移るタイミングを教えてくれたんだ」、と。
災い転じて福となす。
綺麗に整った新天地 https://sf-os.work/sfos で、誰に遠慮することもなく、間もなく新しい30個のツールと新機能を解き放ちます。目指せ、500ツール!
📝 追記:Bitbucket公式からの正式回答
Atlassian Communityに投げていた問い合わせに、コミュニティの有志の方からのコメントに続いて、Atlassian公式サポートチーム(ATLASSIAN TEAM)の Ben 氏より正式な回答がありました。
エンジニアリングチームへの確認の結果として、セキュリティポリシーを強化するための変更が最近実施され、その結果として静的ホスティングサイトはiframe内での読み込みができなくなった、という趣旨の説明が公式に示されました。
これにより、今回の事象が
- ブラウザ側のバグではなく
- 個別リポジトリの設定ミスでもなく
- Bitbucket Cloud全体に適用された、告知のない仕様変更(グローバルなセキュリティ強化)である
ということが、公式に裏付けられました。開発者であり最大の利用者でもある私が「ほぼ毎日使っていたのに、ある日突然動かなくなった」と感じていた違和感は、やはり正しかったわけです。
この回答に対して、私からも以下のように返信しておきました。
Ben、Arkadiuszさん、明確な確認をありがとうございました。これがbitbucket.io全体に適用されたグローバルなセキュリティ更新だと分かったので、自分のサイトを自前の独自ドメインへ無事に移行し、レスポンスヘッダーを自分で制御できるようにしました。また、旧URL上にwindow.openを使ったデータ救出用ゲートウェイを設置し、利用者がLocalStorageのデータを安全に持ち出せるようにしました。新天地では、すべて問題なく動いています。サポートに感謝します!
Bitbucket Cloudではレスポンスヘッダーをユーザー側で変更する手段が提供されておらず、iframeでの埋め込みが必須要件であるなら、レスポンスヘッダーを自分で制御できる別のホスティング環境への移行が必要になる、という点も併せて確認が取れています。
6年間お世話になったBitbucketとは、これでひとまず気持ちよく区切りをつけることができました。同じようにBitbucket Pagesでのiframe埋め込みが突然動かなくなって困っている方がいたら、この記事が「バグではなく仕様変更である」と判断する材料の一つになれば幸いです。