パラメータによるキャッシュ無効化


122

運用展開でバストをキャッシュしたいのですが、そうするためのシステムを考え出すために多くの時間を無駄にしないでください。私の考えは、現在のバージョン番号を持つcssおよびjsファイルの最後にparamを適用することでした:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

2つの質問:これはキャッシュを効果的に壊しますか?paramはこれが動的コンテンツであることを示しているので、paramはブラウザにそのURLからの応答を決してキャッシュしないようにしますか?

回答:


115

param ?v=1.123はクエリ文字列を示すため、ブラウザは次のような新しいパスであると見なします。?v=1.0ます。したがって、キャッシュからではなく、ファイルからロードします。あなたが望むように。

そして、ブラウザは、ソースは、あなたが呼び出す同じ次回滞在すると仮定します?v=1.123すべきであるその文字列でそれをキャッシュします。そのため、キャッシュに残りますが、移動するまでサーバーは設定さ?v=1.124れます。


4
Steve Souders氏の引用:「人気のあるプロキシによるキャッシュの利点を活用するには、クエリ文字列で改定するのではなく、ファイル名自体を改定してください。」完全な説明はここで見つけることができます:stevesouders.com/blog/2008/08/23/...
ラオス

25
そのブログの投稿は、現在10年前に近づいています。キャッシュプロバイダーとCDNはまだそれに対応していないと思いますか?イカは、クエリ文字列を使用してドキュメントをキャッシュすることができるようです
jeteon

1
多分これは誰かを助けるでしょう:個人的に、私は「自動」バージョンパラメータとしてファイル変更タイムスタンプを使用します。<link rel="stylesheet" href="style.css?v=1487935578" />
oelna 2017

理由は個人的にはわかりませんが、Lara Hogan(Swanson)(Etsyのエンジニアリングマネージャー)はクエリパラメーターを使用してキャッシュを無効にすることを推奨していません。ユーザーとサーバー間のキャッシュプロキシに関係していると思います。
Sam Rueby 2017

36

2つの質問:これはキャッシュを効果的に壊しますか?

はい。スタックオーバーフローでもこの方法を使用していますが、1日あたり数百万のビジターと何十億ものさまざまなクライアントとプロキシのバージョンと構成があるため、これでもキャッシュを壊すには不十分な奇妙なケースがあったことを覚えています。しかし、一般的な仮定はこれが機能することであり、クライアントのキャッシュを解除するのに適した方法です。

paramはこれが動的コンテンツであることを示しているため、paramはブラウザにそのURLからの応答をキャッシュしないようにしますか?

いいえ。パラメータはキャッシュポリシーを変更しません。サーバーによって送信されたキャッシュヘッダーは引き続き適用され、サーバーが送信しない場合は、ブラウザーのデフォルトが適用されます。


1
@spender現在、参照を見つけることができません。恐らく、Jeff Atwoodがそれについて話す長いブログ記事またはSOの回答がありました(IIRC)
Pekka

2
@spenderキャッシュ時に一部のプロキシサーバー(古いサーバー、または構成できるサーバー)がクエリ文字列を無視することを読みました。
MrWhite 2014

2
@spender-同じことを聞いたことがあります。ファイル名を変更するか、パスを選択するのが最善の方法です。それはちょうど、例えば、バージョン管理フォルダ名の下に、すべての静的ファイルを移動できるようにする最も簡単かもしれない/static/v22/file.cssあなたは、単一のフォルダ名の変更、例えばで複数のファイルを行うことができますように、 /static/v23/file.css/static/v23/mystuff.js
ブラッド・パークス

22

実際のファイル名にバージョン番号を入れる方が安全です。これにより、複数のバージョンを同時に存在させることができるため、新しいバージョンをロールアウトできます。古いバージョンを要求しているキャッシュされたHTMLページがまだ存在する場合は、HTMLで動作するバージョンが取得されます。

インターネット上のどこにでもある最も大きなバージョンのデプロイメントの1つでは、jQueryは実際のファイル名にバージョン番号を使用し、特別なサーバー側ロジックなしで複数のバージョンを安全に共存させることができます(各バージョンは異なるファイルです)。

これは、新しいページと新しいリンクファイルをデプロイするときに(必要な処理です)、キャッシュを無効にし、それ以降、それらのバージョンを効果的にキャッシュできます(これも必要です)。


私はそれに同意しますが、Sinatraにすべてのcssおよびjsリクエストに?v = <%= VERSION%>を追加させるよりも、すべてのファイルを個別に制御するよりもはるかに簡単です。最終的には、すべてのファイルを前処理して圧縮し、実際にバージョン番号をファイル名に追加するsinatra-assetpackに切り替えます。これにより、ファイルを個別に制御しやすくなります。
Brad Herman

1
ファイル名にバージョン番号を入れることは、10000%を確実にしたい場合、最も安全な解決策であることに同意しますが、「複数のバージョンを同時に存在させる」という議論には従いません。クエリパラメータを持つURLは、異なるクエリパラメータを持つ同じURLとは異なります。これらは、クライアントによって2つの異なるリソースとして扱われる必要があります。そうでない場合、クライアントは壊れています。
ペッカ

2
@Pekka-バージョン番号により、複数のバージョンを同時に存在させることができますが、クエリパラメータを正しい実際のファイルにマップするには、サーバーの協力が必要です。私はそれがOPがここで行っていることではないと思います。ファイル名の変更がはるかに単純で、サーバーの協力を必要としないときに、その複雑さを要求する理由はほとんどありません。もちろん、どちらも機能します。
jfriend00

11

他の人が言ったように、クエリパラメータによるキャッシュの無効化は、通常、悪い考え(tm)と見なされ、長い間使用されてきました。ファイル名にバージョンを反映することをお勧めします。Html5ボイラープレート、とりわけクエリ文字列の使用を推奨しません。

とはいえ、出典を引用した私が見た推奨事項のうち、スティーブ・スーダースによる2008年の記事からすべてが彼らの知恵を利用しているようです。彼の結論は当時のプロキシーの振る舞いに基づいており、最近では関連性があるかもしれないしそうでないかもしれません。それでも、最新の情報がない場合は、ファイル名を変更するのが安全です。


9

次の場合を除き、クライアントがリソースをダウンロードした後、他のすべての応答はクライアントキャッシュから提供されます。

  1. vパラメータが更新されます。
  2. クライアントはキャッシュをクリアします

6

通常、これで問題ありませんが、要求パラメーターを無視するように構成された中間キャッシュ(プロキシ)がある場合、これが機能しない可能性があります。

たとえば、Akamai CDNを介して静的コンテンツを提供している場合、リクエストパラメータを無視するように構成して、この方法を使用したキャッシュの無効化を防ぐことができます。


5

これは、キャッシュをどの程度堅牢にするかによって大きく異なります。たとえば、Squidプロキシサーバー(およびおそらく他のサーバー)は、クエリ文字列で提供されるURLをデフォルトでキャッシュしないように設定されてます-少なくとも、その記事が書かれたときはそうでした。不要なキャッシュミスの原因となる特定のユースケースを問題にしない場合は、クエリパラメータを使用してください。しかし、この問題を回避するファイル名ベースのキャッシュ無効化スキームを設定するのは非常に簡単です。


5
Steve Soudersの記事で引用されたSquidプロキシは、デフォルトのキャッシュポリシーを変更しました。バージョン2.7(2008年5月)およびバージョン3.1(2010年3月)以降、デフォルトの動作は動的コンテンツのキャッシュです。
Josh Rack

5

2つの手法の比較(クエリ文字列とファイル名)はこちら

クエリ文字列としてのバージョンには2つの問題があります。

まず、バストする必要のあるキャッシュを実装しているブラウザであるとは限りません。特定の(おそらく古い)プロキシは、キャッシュ動作に関してクエリ文字列を無視すると言われています。

第2に、複数のフロントエンドサーバーまたは複数のバックエンドサーバー、あるいはその両方がある特定のより複雑な展開シナリオでは、アップグレードは瞬時ではありません。アセットの古いバージョンと新しいバージョンの両方を同時に提供できる必要があります。たとえば、Google App Engineを使用する場合の影響をご覧ください。


4

別の同様のアプローチは、htaccess mod_rewriteを使用して、ファイルを提供するときにパスの一部を無視することです。キャッシュされていないインデックスページは、ファイルへの最新のパスを参照します。

開発の観点からは、バージョン番号にparamsを使用するのと同じくらい簡単ですが、ファイル名アプローチと同じくらい堅牢です。

パスの無視された部分をバージョン番号に使用すると、サーバーはそれを無視して、キャッシュされていないファイルを提供します。

1.2.3/css/styles.csscss/styles.css最初のディレクトリが削除され、htaccessファイルによって無視されるため、同じファイルを提供します

バージョン付きファイルを含む

<?php
  $version = "1.2.3";
?>

<html>
  <head>
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
  </head>
  <body>
    <script src="<?php echo $version ?>/js/main.js"></script>
  </body>
</html>

この方法では、インデックスページのキャッシュを無効にする必要があることに注意してください- <meta>タグを使用して、すべてのブラウザーでキャッシュをオフにしますか?

.htaccessファイル

RewriteEngine On

# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f 
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d 

# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L] 

URLの書き換えが可能な任意のサーバープラットフォームで同じアプローチをとることができます

mod_rewriteから変更された書き換え条件-/#!/以外のクエリ文字列にディレクトリを書き換えます

...インデックスページ/サイトエントリポイントのキャッシュ無効化が必要な場合は、常にJavaSript使用して更新できます。


2
<script type="text/javascript">
// front end cache bust

var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
</script> 

新しいリリースの開発/テスト中は、ブラウザー、サーバー、さらには3G電話会社(モバイル展開を行う場合)が静的コンテンツ(JS、CSS、HTML、imgなど)をキャッシュするため、キャッシュが問題になることがあります。URLにバージョン番号、乱数、またはタイムスタンプを追加することで、これを克服できます。例:JSP:<script src = "js / excel.js?time = <%= new java.util.Date()%>"> </スクリプト>(サーバーページJSP、ASP、PHPの代わりに)純粋なHTMLを実行している場合、サーバーは役に立ちません。ブラウザーでは、JSが実行される前にリンクがロードされるため、リンクを削除してJSでロードする必要があります
Conete Cristian

私はこれがJSファイルを同期して順番にロードするとは思いません。
ステルスラビ

0
 <script>
    var storedSrcElements = [
         "js/exampleFile.js",
         "js/sampleFile.js",
         "css/style.css"
          ];

    var head= document.getElementsByTagName('head')[0];
    var script;
    var link;
    var versionNumberNew = 4.6;

    for(i=0;i<storedSrcElements.length;i++){
     script= document.createElement('script');
     script.type= 'text/javascript';
     script.src= storedSrcElements[i] + "?" + versionNumberNew;
     head.appendChild(script);
    }     


     </script> 


       ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.