# 「確認済み」の印が付いていたのに、リンク先を一度も読めていなかった：t.co の中継ページ

> X の告知の応募リンクを、ブラウザの名乗りを付けて開くと、t.co は転送せず中継ページを返した。表示中の 220 件が全部読めておらず、9 件は閉じたフォームだった。直し方と、誤った「確認済み」の印の外し方の記録。

- URL: https://koshinstudio.com/notes/tco-relay/
- 公開: 2026-10-17
- 書いた人: Koshin（https://koshinstudio.com/about/）
- サイト: Koshin Studio

  English version (https://koshinstudio.com/en/notes/tco-relay/)

トレカダッシュ 開発の記録 ／ 2026年10月

  iOS アプリ「トレカダッシュ (https://koshinstudio.com/works/torecadash/)」は、お店の X（旧 Twitter）の告知から、トレカの抽選販売の情報を集めています。告知には、応募用のフォームへのリンクが付いています。アプリは、そのリンク先も読みに行って、締切の日時や、もう受付が終わっていないかを確かめています。

  ある日、「締切の日時が違う」という報告が来ました。調べると、締切の日時どころではなく、リンク先のページを、一度も読めていませんでした。しかも、読めていないのに、「確認済み」の印が付いていました。

  この記事は、2026 年 10 月 16 日の作業の記録（調査の内容と、数字）から書いています。

## 症状：閉じたフォームが、受付中のまま出ていた

  報告された抽選は、ゲームショップの「30th CELEBRATION カードセット 9 種」の X の告知でした。応募先の Google フォームを開くと、「回答の受け付けは終了しました」と出ます。ところが、アプリでは、この抽選が表示中のままで、締切の日時も空でした。

  おかしいのは、この記事に「受付が終わっていないか確認した」という印（日時）が付いていたことです。確認したのに、終わっていることに気づかなかった。確認が、中身を見ていなかったことになります。

## 原因：t.co は、ブラウザのふりをすると、別のページを返す

  X の投稿に貼られたリンクは、t.co という短縮 URL を通ります。普通は、開くと、本来のリンク先へ転送されます（HTTP の 301）。

  ところが、ブラウザの User-Agent（「私は Chrome です」という名乗り）を付けて開くと、転送されず、JavaScript で飛ばす、約 500 バイトの中継ページが返ってきます。アプリの読み取り処理は、2 か所とも、ブラウザの名乗りを付けて開いて、「転送に従う」設定にしていました。その結果、次のことが起きていました。

- 転送されないので、最後の URL は、t.co のままです。

- 読んだ本文は、応募フォームではなく、中継ページの HTML でした。

- つまり、応募フォームの中身を、一度も読んでいませんでした。

  ブラウザの名乗りを足したのは、8 月 6 日のことでした。今回の不具合は、その変更の副作用でした。

## 実際に、どれくらい読めていなかったか

  報告された 1 件だけの問題かを確かめるために、表示中の X の抽選・予約の記事のうち、t.co のリンクを持つものを全部調べました。

    調べたこと | 結果

      t.co を持つ、表示中の記事 | 220 件

      そのうち、中継ページが返ってきた記事 | 220 件（全部）

      そのうち、実は閉じたフォームなのに、表示中だった記事 | 9 件

  9 件は、複数の店にまたがっていました。1 つの店の問題ではなく、仕組みの問題です。

  もう 1 つ、見逃せない手がかりがありました。毎日動いている、「締切が空の記事を直す」処理が、「対象 126 件、更新 0 件」を、毎日繰り返していたことです。対象が 126 件あるのに、直せたのが 0 件。これは、「直すものが無かった」のではなく、「読めていないから、直せなかった」の結果でした。

## 直し方

  転送先を調べる処理を、fetchTcoTarget という 1 つの関数にまとめました。手順は、こうです。

- 名乗りを付けずに、t.co を開く。転送には従わず（redirect: "manual"）、最初の転送先だけを受け取る。

- 転送先が返ってきたら、それを使う。

- 中継ページが返ってきたら、その中の自動転送の指定（<meta http-equiv="refresh">）を読んで、本当の転送先を取り出す。

- その転送先を、今度はブラウザの名乗りを付けて開く。

  名乗りを付けるか付けないかで、返ってくるものが違う。だから、「短縮 URL を解く」ときは名乗りなし、「本来のページを読む」ときはブラウザの名乗りあり、と使い分けました。同じ失敗を繰り返さないよう、テストも足しています。

## すでに付いていた、「確認済み」の印をどうするか

  直しただけでは、足りません。誤って付いた「確認済み」の印が、記事に残っているからです。印がある記事は、「もう確認した」と見なされて、再確認の対象から外れます。コードを直しても、既存の記事は直りません。

  そこで、条件に合う 195 件（X の抽選・予約で、投稿へのリンクを持ち、t.co を持つもの）から、印を外しました。そのうえで、受付終了の確認と、締切の再抽出を、手で実行しました。

    処理 | 直す前 | 直した後

      締切の再抽出（対象になった件数／更新できた件数） | 対象 126 件・更新 0 件（毎日） | 対象 123 件・更新 14 件

  同じ日に、受付終了の確認の頻度も見直しました。締切の無い記事は 6 時間おき（締切のある記事は 24 時間おき）、1 回に見る件数は 40 件から 80 件、1 回の上限は 7 分です。t.co が読めるようになったので、閉じたフォームを丸 1 日出し続けないようにするためです。

## 分かったこと

> 「確認した」の印は、読めたときだけ付ける。

- 印は、成功したときだけ書く。読めなかったのに「確認した」と書くと、直した後も、その記事は二度と見直されません。

- 外部のページを読んだら、目的のページかを確かめる。最後の URL が、短縮 URL のままではないか。本文が、期待した種類のページか。

- 「更新 0 件」が続くのは、異常かもしれない。対象があるのに、毎日 0 件。「ない」のか「できない」のかを、区別します。

- 通信の設定を変えたら、それを使う全部の通信を見直す。8 月に足したブラウザの名乗りが、2 か月後に、t.co の読み取りを壊していました。

  この話に出てくるもの

- トレカダッシュ (https://koshinstudio.com/works/torecadash/) — トレカの抽選・予約・先着の情報を集める iPhone アプリ

- 「取れた」と読んでいたのは、ただの案内ページだった (https://koshinstudio.com/notes/blocked-page/) — 同じ、「取れたことにしていた」失敗の記録

- 確認できなかったときに、通していた (https://koshinstudio.com/notes/fail-open/) — 「確認できなかった」の扱いで起きた、別の失敗
