画像をキャッシュしないようにWebブラウザーを強制する方法


124

バックグラウンド

私は2つの無料ウェブサイト用の非常にシンプルなCGIベースの(Perl)コンテンツ管理ツールを作成して使用しています。Webサイト管理者に、フィールド(日付、場所、タイトル、説明、リンクなど)に入力して保存するイベントのHTMLフォームを提供します。そのフォームでは、管理者がイベントに関連する画像をアップロードできるようにします。フォームを表示するHTMLページで、アップロードされた画像のプレビュー(HTML imgタグ)も表示しています。

問題

問題は、管理者が画像を変更したい場合に発生します。彼は「ブラウズ」ボタンを押し、新しい写真を選んで「OK」を押すだけです。そして、これはうまくいきます。

画像がアップロードされると、バックエンドのCGIがアップロードを処理し、フォームを適切に再読み込みします。

問題は、表示される画像が更新されないことです。データベースに正しい画像が保持されていても、古い画像は表示されます。画像をWebブラウザーでキャッシュしているという事実に絞り込みました。管理者がFirefox / Explorer / SafariのRELOADボタンを押すと、すべてが正常に更新され、新しい画像が表示されます。

私の解決策-機能しない

過去の非常に古い日付のHTTP Expires命令を記述して、キャッシュを制御しようとしています。

Expires: Mon, 15 Sep 2003 1:00:00 GMT

私は管理者側にいることを忘れないでください。ページは常に期限切れであるため、ページのロードに少し時間がかかるかどうかは特に気にしません。

しかし、これも機能しません。

ノート

画像をアップロードするとき、そのファイル名はデータベースに保持されません。これは、Image.jpgに名前が変更されます(使用するときに単にわかりやすくするため)。既存の画像を新しい画像に置き換える場合、名前も変更されません。画像ファイルの内容のみが変更されます。

ウェブサーバーはホスティングサービス/ ISPによって提供されます。Apacheを使用します。

質問

画像ではなく、このページの内容をキャッシュしないようにWebブラウザに強制する方法はありますか?

実際にデータベースに「ファイル名を保存する」オプションを調整しています。このように、画像が変更されると、IMGタグのsrcも変更されます。ただし、これにはサイト全体に多くの変更が必要であり、より良い解決策がある場合は、むしろ変更を行いません。また、アップロードされた新しい画像に同じ名前が付いている場合、これは機能しません(たとえば、画像が少しフォトショップされて再アップロードされたとします)。


この質問は、私が書いたときにしばらく置いていた「今では役に立たないコンテキスト」をすべて削除するためのやり直しになると思います。タイトルは正解だと思います。しかし、この質問は元々、多くの種類の回答に十分な余地を与えるために書かれており、そのような単純な解決策を期待することはできませんでした。したがって、その質問は、答えを得るためにそこにやって来る人々にとって少し複雑です。
Philibert Perusse

回答:


186

アーミン・ロナッチャーは正しい考えを持っています。問題は、ランダムな文字列が衝突する可能性があることです。私は使うだろう:

<img src="picture.jpg?1222259157.415" alt="">

ここで、「1222259157.415」はサーバーの現在の時刻です。
JavaScriptをperformance.now()使用して、またはPythonを使用して時間を生成するtime.time()


32
重要な追加機能の1つは、ブラウザに何かを強制することは決してできないということです。できることは友好的な提案をすることだけです。これらの提案に実際に従うかどうかは、ブラウザとユーザー次第です。ブラウザはこれを自由に無視できます。または、ユーザーがデフォルトを上書きできます。
Joel Coehoorn、2008

8
ジョエル、あなた自身の答えにそれを追加した方がいいでしょう。
エポックウルフ、2008

1
考えてみて、ここでパーティーに参加するには遅すぎます。ただし、イメージの変更を制御できるため、クエリ文字列を追加するのではなく、更新時にイメージの名前を変更する方がよいでしょう。たとえば、picture.jpg?1222259157の代わりに、1222259157.jpgを使用します。このようにして更新されますが、再訪問時に再キャッシュされます。
ダンジャ

43
キャッシュを完全に防止するサーバー時間を追加するのではなく、「?」の後にファイルの最終変更時間を追加してください。このようにして、画像は次に変更されるまで通常どおりキャッシュされます。
Doin

3
Doinによる最後のコメントは賛成されなければなりません!スマートキャッシングは確かに重要です。なぜ同じファイルを何度もダウンロードする必要があるのでしょうか。
f.arcarese 2014年

46

簡単な修正:ランダムなクエリ文字列を画像に添付します。

<img src="foo.cgi?random=323527528432525.24234" alt="">

HTTP RFCの内容:

Cache-Control: no-cache

しかし、それはうまくいきません:)


1
Cache-Controlを与える方法:通常のhtml <img>タグにno-cache?
P Satish Patro、

応答ヘッダーに含める必要がありますか?
P Satish Patro、

22

PHPのファイル変更時刻関数を使用します。次に例を示します。

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

画像を変更すると、変更されたタイムスタンプが異なるため、キャッシュされた画像ではなく新しい画像が使用されます。


素晴らしいヒント。変更された時間が変更された場合(上書きまたは変更された場合)、キャッシュとイメージの両方がクライアントに再送信されます。
Vasilis Lourdas 2013年

他のオプションについてはこちらをご覧くださいstackoverflow.com/questions/56588850/...
drooh

絶対にそれを釘付けにした。素晴らしいチップメイト。
Khurram Shaikh

パーフェクト!追加のヒント:<img src = "images / image.png?<?php echo filemtime( 'images / image.jpg')?>">
Rica Gurgel

16

私は使うだろう:

<img src="picture.jpg?20130910043254">

ここで、 "20130910043254"はファイルの変更時刻です。

画像をアップロードするとき、そのファイル名はデータベースに保持されません。これは、Image.jpgに名前が変更されます(使用するときに単にわかりやすくするため)。既存の画像を新しい画像に置き換える場合、名前も変更されません。画像ファイルの内容のみが変更されます。

単純な解決策には2つのタイプがあると思います。1)最初に思いつくもの(簡単な解決策、思いつくのは簡単なので)、2)考え直した後で終わるもの(簡単にできるため)使用する)。どうやら、物事を考え直すことを選択した場合、あなたは常に利益をもたらすとは限りません。しかし、2番目のオプションはかなり過小評価されていると思います。なぜphpそんなに人気があるか考えてください;)


1
+1私はそれが他の答えよりもはるかに良いアイデアだと思います。ラス変更時間を使用すると、サーバーは画像に何も変更されていないときに同じブラウザーに同じ画像を複数回渡そうとしないからです。リクエストがあるたびに画像の最終書き込み時間を抽出するためのオーバーヘッドは少しありますが、大きな画像の場合は、毎回画像を返す必要があり、画像が変更されると、ユーザーは新しい画像を取得します。この素敵な小さな追加をありがとう。
サミュエル

さて、データベースへの画像へのパスと共に変更時間を保存するかもしれません。したがって、オーバーヘッドはそれほど重要ではない可能性があります。一方、これらが私たちが話しているソースコードの一部である画像である場合、変更時間もキャッシュする可能性があります。画像の変更時間を含むスクリプトを生成する(例:)images.php。このスクリプトはコミットごとに再生成する必要があり、ファイルの変更時刻を決定するオーバーヘッドを排除します。
x-yuri 2014

@ X-ヨリこれは、私はむしろ、filemtimeのランニングよりも、データベースでこの更新されたフィールドを保存することを選択しています理由ですstackoverflow.com/questions/56588850/...
drooh

9

使用クラス= "NO-CACHEを"

サンプルhtml:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

結果:src = "images / img1.jpg" => src = "images / img1.jpg?a = 0.08749723793963926"


シンプルでエレガントで、サーバー側のレンダリングを必要としません。いいね!
Ahi Tuna

7

画像を提供するためのプロキシスクリプトを作成することもできます。これが好きなもの:

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

脚本:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

実際には、キャッシュなしのヘッダーか、画像srcの乱数のいずれかで十分ですが、防弾対策が必要なので、


Pragmaが応答ヘッダーではないことを除いて、あなたのものは良い解決策です。
Piskvorが建物を去る

1
@Piskvor:w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32はそうではないようです。
Grizly 2013年

6

私は新しいコーダーですが、ブラウザがWebカメラビューをキャッシュして保持しないようにするために思いついたのは次のとおりです。

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

どのブラウザーで何が機能するかはわかりませんが、一部のブラウザーでは機能します。IE:Webページが更新されたとき、およびWebサイトが(更新なしで)再訪問されたときに機能します。Chrome:Webページが更新された場合にのみ機能します(再訪問後も)。SAFARIとiPad:機能しません。履歴とWebデータをクリアする必要があります。

SAFARI / iPadに関するアイデアはありますか?


4

画像をアップロードするとき、そのファイル名はデータベースに保持されません。これは、Image.jpgに名前が変更されます(使用するときに単にわかりやすくするため)。

これを変更すれば、問題は修正されました。上記で提案したソリューションと同様に、タイムスタンプを使用します:Image- <timestamp> .jpg

おそらく、画像のファイル名を同じにすることで回避している問題はすべて克服できますが、それらが何であるかは言いません。


4

私はウェブ上のすべての回答をチェックしましたが、最良の回答は次のようでした(実際にはそうではありません)。

<img src="image.png?cache=none">

最初は。

ただし、cache = noneパラメータ(静的な「none」という単語)を追加しても何も影響はなく、ブラウザは引き続きキャッシュからロードします。

この問題の解決策は:

<img src="image.png?nocache=<?php echo time(); ?>">

基本的には、UNIXタイムスタンプを追加してパラメーターを動的にしてキャッシュを使用しないようにしましたが、うまくいきました。

しかし、私の問題は少し異なりました:オンザフライで生成されたPHPチャート画像をロードし、$ _ GETパラメーターでページを制御していました。URLのGETパラメータが同じ場合はキャッシュから画像を読み取り、GETパラメータが変更された場合はキャッシュしないようにしたいと考えました。

この問題を解決するには、$ _ GETをハッシュする必要がありましたが、これが配列なので、解決策は次のとおりです。

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

編集

上記のソリューションは問題なく機能しますが、ファイルが変更されるまでキャッシュバージョンを提供したい場合があります。(上記の解決策では、その画像のキャッシュを完全に無効にします)したがって、画像ファイルの使用に変更があるまで、ブラウザからキャッシュされた画像を提供するには:

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime()はファイル変更時刻を取得します。


2
filemtimeので、解決策も優れているmd5多くの処理能力を要します。
oriadam

2
Chromiumベースのアプリで画像のキャッシュを停止しようとする日々。時間エコー付きの?nocacheは問題を解決しました。ありがとうございました!
ウッディ

2

あなたの問題は、Expires:ヘッダーにもかかわらず、ブラウザーがキャッシュをチェックするのではなく、更新前の画像のメモリ内コピーを再利用していることです。

私はストアのようなサイトの管理バックエンドで製品の画像をアップロードする非常に類似した状況があり、私の場合、最良のオプションは、他の人のURL変更技術を使用せずに、JavaScriptを使用して画像を強制的に更新することでした。すでにここで言及しています。代わりlocation.reload(true)に、IFRAMEのウィンドウで呼び出される非表示のIFRAMEに画像のURLを挿入し、ページ上の画像を置き換えました。これにより、現在のページだけでなく、それ以降にアクセスするページでも画像が強制的に更新されます。クライアントやサーバーでURLクエリ文字列やフラグメント識別子のパラメーターを覚えておく必要はありません。

これを行うためのコードをここの私の回答に投稿しました


2

タイムスタンプを追加 <img src="picture.jpg?t=<?php echo time();?>">

常に最後にファイルにランダムな番号を付け、キャッシュを停止します


このソリューションを実装すると、リクエストごとにサーバーにストレスがかかり、ブラウザがキャッシュされる最初のリクエスト時に生成された時間を含むphpページをキャッシュできるため、必ずしも機能しないことに注意してください。これは、ページに動的クエリ文字列が含まれている場合にのみ確実に機能します。
ドミトリー

1

あなたとクライアントの間に不適切に動作する透過プロキシの可能性があるため、画像がキャッシュされないことを完全に保証する唯一の方法は、クエリ文字列またはタイムスタンプの一部としてタイムスタンプにタグを付けるなど、一意のURIを与えることです道。

そのタイムスタンプが画像の最終更新時刻に対応している場合は、必要なときにキャッシュして、適切なタイミングで新しい画像を提供できます。


1

元の質問は、テキスト情報が格納された画像に関するものだと思います。したがって、src = ... urlを生成するときにテキストコンテキストにアクセスできる場合は、無意味なランダムスタンプやタイムスタンプではなく、画像バイトのCRC32を保存/使用することを検討してください。次に、画像がたくさんあるページが表示されている場合は、更新された画像のみが再ロードされます。最終的に、CRCの格納が不可能な場合は、実行時に計算してURLに追加できます。


または、CRCではなく、画像の最終変更時刻を使用してください。
Doin

1

私の観点からは、画像キャッシュを無効にすることは悪い考えです。全然。

ここでの根本的な問題は、サーバー側で画像が更新されたときに、ブラウザに画像を強制的に更新する方法です。

繰り返しになりますが、私の個人的な観点からは、画像への直接アクセスを無効にすることが最善の解決策です。代わりに、サーバー側のフィルター/サーブレット/その他の同様のツール/サービスを介して画像にアクセスします。

私の場合、それは画像を返し、応答としてETagを添付するRESTサービスです。サービスはすべてのファイルのハッシュを保持し、ファイルが変更された場合、ハッシュは更新されます。すべての最新のブラウザで完全に動作します。はい、実装には時間がかかりますが、それだけの価値はあります。

唯一の例外-ファビコンです。いくつかの理由で、機能しません。ブラウザにサーバー側からキャッシュを強制的に更新させることができませんでした。ETags、Cache Control、Expires、Pragmaヘッダー、何の助けにもなりません。

この場合、ランダム/バージョンパラメータをURLに追加することが唯一の解決策のようです。


1

理想的には、コンテンツを同期するオプションを使用して、各Webページにボタン/キーバインド/メニューを追加する必要があります。

そのためには、同期が必要なリソースを追跡し、xhrを使用して動的クエリ文字列で画像を調査するか、動的クエリ文字列を使用してsrcで実行時に画像を作成します。次に、ブロードキャストメカニズムを使用して、リソースを使用しているWebページのすべてのコンポーネントに、動的なクエリ文字列をURLに追加してリソースを使用するように更新を通知します。

単純な例は次のようになります:

通常、画像は表示されてキャッシュされますが、ユーザーがボタンを押すと、時間クエリ文字列が追加されたxhrリクエストがリソースに送信されます。時間はプレスごとに異なると想定できるため、クエリに基づいてサーバー側でリソースが動的に生成されているかどうか、または静的であるかどうかを判断できないため、ブラウザーはキャッシュをバイパスしますクエリを無視するリソース。

その結果、すべてのユーザーが常にリソースリクエストで攻撃されるのを回避できると同時に、ユーザーが同期していないと思われる場合にリソースを更新するメカニズムを許可できます。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>


0

一意のファイル名を使用する必要があります。このような

<img src="cars.png?1287361287" alt="">

ただし、この手法では、サーバーの使用率が高くなり、帯域幅が浪費されます。代わりに、バージョン番号または日付を使用する必要があります。例:

<img src="cars.png?2020-02-18" alt="">

しかし、キャッシュから画像を提供しないようにする必要があります。このため、ページがページキャッシュを使用しない場合、 PHPまたはサーバー側で可能です。

<img src="cars.png?<?php echo time();?>" alt="">

ただし、それでも効果はありません。理由:ブラウザキャッシュ...最後ですが、最も効果的な方法は、ネイティブJAVASCRIPTです。この単純なコード、「NO-CACHE」クラスを持つすべての画像検索し、画像をほぼ一意にします。これをスクリプトタグの間に置きます。

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) {
    var img = items[i];
    img.src = img.src + '?' + Date.now();
}

使用法

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" alt="">

このような結果

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