公証に3時間かけた直後、ダウンロードしたら「.man」だった

Macアプリの公証(Notarization)を、丸一日がかりで通しました。証明書の種類を直し、Hardened Runtimeを有効にし、get-task-allow の注入を止め、タイムスタンプを付け、Appleの検査を通し、合格証を貼り付け、DMGを作り、そのDMGにも署名して公証して——。

別のノートPCで起動確認もしました。警告なし。完璧。

「せっかくだから、本番と同じ経路でも確認しておくか」

自分のサイトにDMGを置いて、ノートPCのSafariでダウンロードしてみました。

落ちてきたファイルが、これです。

electronic_bookkeeping_renamer_v1.2.2.man

.man

目次

.man って何

Unixのマニュアルページ(man ls とかで見るアレ)のファイル形式です。ディスクイメージとは縁もゆかりもありません。

サイズは26.4MBで元のDMGと同じなので、中身は壊れていない。名前だけがおかしい。

でも、これをダブルクリックしても何も起きません。受け取った人から見れば、「ダウンロードしたけど開けない壊れたファイル」です。

原因:サーバーが嘘をついていた

ヘッダを見たら一発でした。

curl -sI https://.../electronic_bookkeeping_renamer_v1.2.2.dmg | grep -i content-type
content-type: application/x-troff-man

サーバーが「これはmanページです」と宣言していました。Safariは素直なので、「サーバーがそう言うなら」と .man を付けて保存する。

.dmg という拡張子でURLを叩いているのに、.man で落ちてくる。HTMLの download 属性も付けていたのに、Content-Type の方が優先されたわけです。

サーバー側で .dmg のMIMEタイプが定義されていないと、こういうことが起きます。直し方は .htaccess に1行。

AddType application/x-apple-diskimage .dmg

「このファイルはmacOSのディスクイメージです」と正しく伝える。それだけ。

ここから、私は2回続けて外した

.htaccess をアップして、確認しました。

content-type: application/x-troff-man

変わらない。

推測1「レンタルサーバーのキャッシュだろう」

管理パネルで高速化機能をOFF→ONして、キャッシュを捨てさせました。変わらない。

推測2「配信経路が違うのでは」

静的ファイルは高速化機能が自前で返していて、.htaccess が読まれていないのでは——それらしい理屈をこねました。クエリ文字列を付けたURLだと正しいタイプが返ることも、その説を補強しているように見えました。

外れ。

そこで、ヘッダを全部見ました。

HTTP/2 200
content-type: application/x-troff-man
server: cloudflare              ← !?
cf-cache-status: HIT            ← !!
age: 952
cache-control: max-age=14400

Cloudflare。

前段にCDNがいました。レンタルサーバーのキャッシュをいくら消しても、そりゃ変わりません。見ていた場所が違った。

時系列がぴったり合いました。

  • 12:31 DMGをアップし、最初にアクセス → この時点の誤ったヘッダをCloudflareが記憶
  • 12:40 .htaccess をアップ(サーバー側は正しくなった)
  • 12:47 でもCloudflareは16分前の記憶を返し続けている(age: 952

Cloudflareのキャッシュをパージしたら、一発で直りました。

content-type: application/x-apple-diskimage
cf-cache-status: MISS

沈黙は「問題なし」ではない

いちばん怖いのはここです。

サーバーの設定は最初からこうでした。つまりv1.0.0 から v1.2.1 まで、全部同じ状態で配布していたことになります。

Safariで無料版を試そうとした人には、開けないファイルが届いていた可能性がある。

問い合わせは来ていませんでした。でもそれは、問題が無かったことを意味しません。「ダウンロードしたけど開けなかった」という人は、たいてい連絡してこないからです。 わざわざ開発者にメールを書いて「あなたのアプリが開けません」と教えてくれる人は、ごく一部の親切な人だけ。残りは黙って去る。

Macユーザーの多くはSafariを使います。私は、自分でも知らないうちに、いちばん多い層を取りこぼしていたかもしれません。

.htaccess の1行。それが今日のどの作業より売上に効くかもしれない、というのは皮肉な話です。公証には丸一日かけたのに。

なぜ見つかったか

本番と同じ経路で確認したからです。それだけ。

技術的には、公証が通った時点で「完了」でした。実際、AirDropで送ったDMGは完璧に動いていた。あそこで終わりにしていたら、この問題は見つかりませんでした。

「せっかくだから本番でも確認しておくか」と思ったのは、半分は気まぐれです。でも、その気まぐれが1ヶ月分の取りこぼしを見つけました。

学んだこと

推測する前にヘッダを見る。 私は2回外しました。どちらも「それらしい理屈」でした。curl -sI を全部表示していれば、1回目で server: cloudflare が目に入っていた。部分的に見ることは、見ていないことに近い。

自分のサイトのダウンロード、実際に落としたことがありますか。 個人開発でアプリやファイルを配っている人は、いま一度、購入者と同じ経路で試してみてください。URLを直接叩くのではなく、サイトのボタンから、普段使わないブラウザで。5分で終わります。

動いた ≠ 届いた。 ビルドが通ったこと、起動したこと、公証が通ったことは、ユーザーの手元で開けることを保証しません。最後の1メートルは、自分で歩いて確かめるしかない。


ちなみに調べる過程で、Cloudflareに「開発モード」があることを知りました。3時間だけキャッシュをバイパスするモード。サイトをいじる日は、最初にこれをONにしておけばいい。 今日の回り道の半分は、これで消えていました。

来月の自分のために書いておきます。

目次