ハマった経緯

最初に

  • これまで example.com でサイトを運営していた。

そして

  • これまで運営していた example.comother.example.com としてドメインを変更して引き続きサイトを運営しつつ、
  • example.com には新たなサイトを割り当てることにした。

そのため

  • まずは example.comother.example.com にリダイレクトさせるようにしておいた。
  • 一定期間たったあとでそのリダイレクトを解除し example.com を新たなサイトに割り当てた。

すると起きた問題が

  • すでにリダイレクト設定を解除しているにも関わらず example.com にアクセスすると other.example.com にリダイレクトされてしまう。

原因は

  • リダイレクトでは 301 リダイレクトしていた。
  • Google Chrome がこれをリダイレクトキャッシュとしてブラウザにキャッシュしているのが原因。
  • このキャッシュ期限は永久で、意図的に削除しない限り残り続ける。
  • (Google Chrome 以外に Firefox も同様に永久キャッシュするようです)

【注意喚起】Google Chrome は 301 リダイレクトを永久キャッシュするので要注意

意外と知らない人が多いのですが、Google Chrome は 301 リダイレクトを永久キャッシュします。永久というのは 1 年先でも 2 年先でも、ユーザーがブラウザのキャッシュを削除しない限り、リダイレクト元がどうなっていようがリダイレクト先にアクセスしてしまうということです。

リダイレクト元への HTTP サーバへのアクセスは勿論、DNS リクエストすら発生しません。

となると、もし間違って 301 リダイレクトしてしまうと、それが永久キャッシュされてしまい、取り返しがつかなくなります。(間違った設定の期間にアクセスした人は、永遠に間違ったリダイレクトがされてしまい、正しいリダイレクト先にアクセスできなくなります)

Ctrl + F5 でスーパーリロードしても、リダイレクト先がリロードされるだけなので無駄です。ユーザにキャッシュ削除させるしか事実上の救済先がなく、サイトオーナーの立場ではどうしようもならなくなります。

また、例えば数か月後に、元あるドメインに戻そうとした場合とか、URL 構成変更などを行った場合でリダイレクト前の URL を再利用したいときとかにも支障が生じます。(ただし、元の URL に対して 301 リダイレクトをした場合には、最初のリダイレクトキャッシュはクリアできます。

そのため、現存しないリダイレクトキャッシュによる無限リダイレクトは発生しませんし、リダイレクト元へリダイレクトすることとでリダイレクトキャッシュを無効化することは可能です)

https://it.srad.jp/comment/4080153

そもそも

301 Moved Permanently なリダイレクトではなく 302 Found か 307 Temporary Redirect を使用すべきでした。そうはいっても時すでに遅しなので、ここからどう復旧させるか。

対応方法

自分のブラウザのキャッシュをクリアするだけで良いのであれば、Chrome の設定画面からキャッシュを削除するなり、デベロッパーツールのキャッシュ無効化機能を利用すれば良いだけ。

しかし何より問題なのは、サイト閲覧者であるユーザのブラウザに残ってしまったキャッシュをどのようにクリアするのか。

もちろん、キャッシュの削除手順を説明してユーザにポチポチと実施してもらうことも可能ですが、現実的ではありません。

Bad な方法:JavaScript でスーパーリロードはできない

location.reload(true); でスーパーリロードできる(からこれを使えばユーザのキャッシュもクリアできる)と紹介している記事が見つかるかもしれませんが、少なくとも現在これはもう使えません。

location.reload() has no parameter

Firefox supports a non-standard forceGet boolean parameter for location.reload(), to tell Firefox to bypass its cache and force-reload the current document. However, in all other browsers, any parameter you specify in a location.reload() call will be ignored and have no effect of any kind.

So a boolean parameter is not part of the current specification for location.reload() — and in fact has never been part of any specification for location.reload() ever published.

https://developer.mozilla.org/en-US/docs/Web/API/Location/reload

Not Good な方法:browsingData API を使用する

今回は採用しない方法ですが browsingData API をユーザ側で実行させてもキャッシュをクリアできるのではないかと思います。

browsingData - Mozilla | MDN

しかしこの API を利用するには permission が必要なため、ユーザの許可を得る手順を踏まないといけません。

これがなかなか厄介ですが、幸いにも次に紹介する別の方法でさらに簡単に解決できますので、上記方法を採用する必要はありません。

Better な方法:Fetch API を使用する

Fetch API を使用するとキャッシュが更新されるようで、この挙動を利用します。

(この方法は以下の記事で知りました)

https://www.ryadel.com/en/clear-google-chrome-redirect-cache-for-single-url-page-howto/

#4: Use the Chrome Fetch API

  • Go to www.google.com (or any other wesite with a non-restrictive CORS policy)
  • Press SHIFT+CTRL+J to open the Google Chrome’s console window.
  • Write the following line:
fetch('https://www.example.com', { method: 'post' }).then(() => {});

Needless to say, you’ll have to replace www.example.com with the actual URL that you want to remove from the cache.

ということで以下のような関数を用意します。

const clearCacheThenGotoNewSite = async () => {
  const url = 'https://example.com';
  await fetch(url, { method: 'POST' });
  window.location.href = url;
};

旧サイト other.example.com のほうに「新サイトへ移動する」ボタンを1つ用意しておいて、ユーザがそちらのボタンを押したときにこの関数を実行しましょう。

そうするとリダイレクトキャッシュがクリアされ、そのまま新サイト example.com に移動します。

(為念ですが、上記の Fetch を成功させるために、新サイトの方で CORS を受け入れる設定をしておくのをお忘れなく。)