웹 브라우저가 이미지를 캐시하지 않도록 강제하는 방법


124

배경

저는 두 개의 pro-bono 웹 사이트에 대해 매우 간단한 CGI 기반 (Perl) 콘텐츠 관리 도구를 작성하고 사용하고 있습니다. 웹 사이트 관리자에게 이벤트에 대한 HTML 양식을 제공하여 필드 (날짜, 장소, 제목, 설명, 링크 등)를 채우고 저장합니다. 이 양식에서 관리자는 이벤트와 관련된 이미지를 업로드 할 수 있습니다. 양식을 표시하는 HTML 페이지에서 업로드 된 사진 (HTML img 태그)의 미리보기도 표시합니다.

문제

관리자가 사진을 변경하려고 할 때 문제가 발생합니다. 그는 "찾아보기"버튼을 누르고 새 사진을 선택한 다음 확인을 누르면됩니다. 그리고 이것은 잘 작동합니다.

이미지가 업로드되면 내 백엔드 CGI가 업로드를 처리하고 양식을 올바르게 다시로드합니다.

문제는 표시된 이미지 가 새로 고쳐 지지 않는다는 것 입니다. 데이터베이스가 올바른 이미지를 보유하더라도 이전 이미지는 계속 표시됩니다. 웹 브라우저에서 이미지가 캐시된다는 사실로 범위를 좁혔습니다. 관리자가 Firefox / Explorer / Safari에서 RELOAD 버튼을 누르면 모든 것이 정상적으로 새로 고쳐지고 새 이미지가 나타납니다.

내 솔루션-작동하지 않음

아주 먼 과거의 날짜로 HTTP Expires 명령을 작성하여 캐시를 제어하려고합니다.

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

나는 관리 측에 있고 페이지가 항상 만료되기 때문에 페이지를로드하는 데 조금 더 오래 걸리는지 신경 쓰지 않는다는 것을 기억하십시오.

그러나 이것도 작동하지 않습니다.

노트

이미지를 업로드 할 때 파일 이름은 데이터베이스에 보관되지 않습니다. Image.jpg 로 이름이 변경되었습니다 (사용할 때 간단히 표시). 기존 이미지를 새 이미지로 바꿀 때 이름도 변경되지 않습니다. 이미지 파일의 내용 만 변경됩니다.

웹 서버는 호스팅 서비스 / ISP에서 제공합니다. Apache를 사용합니다.

질문

웹 브라우저가 이미지가 아닌이 페이지의 항목을 캐시하지 않도록 강제하는 방법이 있습니까?

저는 데이터베이스와 함께 실제로 "파일 이름을 저장"하는 옵션을 사용하고 있습니다. 이렇게하면 이미지가 변경되면 IMG 태그의 src도 변경됩니다. 그러나 이것은 사이트 전체에서 많은 변경이 필요하며 더 나은 솔루션이 있다면 오히려 그렇게하지 않습니다. 또한 업로드 된 새 이미지의 이름이 같은 경우에도 여전히 작동하지 않습니다 (예 : 이미지가 약간 포토샵되고 다시 업로드 된 경우).


이 질문은 내가 글을 썼을 때 한동안 거기에 넣은 "지금 쓸모없는 문맥"을 모두 제거하기 위해 재 작업 될 수 있다고 생각합니다. 제목이 옳은 것 같고, 최고의 답변이기도합니다. 그러나이 질문은 원래 많은 종류의 답변을위한 많은 공간을 제공하기 위해 작성되었으며 그러한 간단한 해결책을 기대할 수 없었습니다. 따라서 질문은 답을 얻기 위해 거기에 오는 사람들에게 약간 복잡합니다.
Philibert Perusse

답변:


186

Armin Ronacher는 올바른 아이디어를 가지고 있습니다. 문제는 임의의 문자열이 충돌 할 수 있다는 것입니다. 다음을 사용합니다.

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

여기서 "1222259157.415"는 서버의 현재 시간입니다.
Javascript performance.now()또는 Python으로 시간 생성time.time()


32
한 가지 중요한 추가 사항은 브라우저가 어떤 작업도 수행 하도록 강제 할 수 없다는 것입니다. 당신이 할 수있는 모든 것은 친절한 제안을하는 것입니다. 실제로 이러한 제안을 따르는 것은 브라우저와 사용자에게 달려 있습니다. 브라우저는 이것을 무시하거나 사용자가 기본값을 무시할 수 있습니다.
Joel Coehoorn

8
Joel, 당신은 당신 자신의 대답에 그것을 추가하는 것이 더 나았을 것입니다.
epochwolf

1
그냥 생각하고 파티에 오기에는 너무 늦었지만 이미지 변경을 제어 할 수 있으므로 쿼리 문자열을 추가하는 대신 이미지가 업데이트 될 때 이름을 바꾸는 것이 좋습니다. 예를 들어 picture.jpg? 1222259157 대신 1222259157.jpg입니다. 이렇게하면 업데이트되고 다시 방문 할 때 다시 캐시됩니다.
danjah

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로 이름이 변경되었습니다 (사용할 때 간단히 표시). 기존 이미지를 새 이미지로 바꿀 때 이름도 변경되지 않습니다. 이미지 파일의 내용 만 변경됩니다.

나는 두 가지 유형의 간단한 해결책이 있다고 생각합니다. 1) 먼저 떠오르는 것 (생각하기 쉽기 때문에 간단한 해결책), 2) 생각한 후에 끝나는 것 (쉽기 때문에 사용하다). 분명히 생각하기로 선택하면 항상 이익을 얻는 것은 아닙니다. 그러나 두 번째 옵션은 다소 과소 평가 된 것 같습니다. 왜 php그렇게 인기가 있는지 생각해보십시오 .)


1
+1 las 수정 시간을 사용하면 이미지가 변경되지 않은 경우 서버가 동일한 이미지를 동일한 브라우저에 여러 번 제공하지 않기 때문에 다른 답변보다 훨씬 좋은 생각이라고 생각합니다. 요청이있을 때마다 이미지의 마지막 쓰기 시간을 추출하는 데 약간의 오버 헤드가 있지만 큰 이미지의 경우 매번 이미지를 반환하는 것보다 낫고 이미지가 변경 될 때 사용자가 새 이미지를 갖게됩니다. 이 멋진 작은 추가에 감사드립니다.
Samuel

글쎄, 데이터베이스의 이미지 경로와 함께 수정 시간을 저장할 수 있습니다. 따라서 오버 헤드는 훨씬 덜 중요 할 수 있습니다. 반면에 이것이 우리가 이야기하는 소스 코드의 일부인 이미지라면 수정 시간도 캐시 할 수 있습니다. 이미지 수정 시간이 포함 된 스크립트 생성 (예 :) images.php. 이 스크립트는 각 커밋을 다시 생성해야하며 파일 수정 시간을 결정하는 오버 헤드를 제거합니다.
x-yuri 2014

@ x-yori 이것이 내가 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() });
    });

자바 스크립트 :

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

6

저는 새로운 Coder이지만 브라우저가 내 웹캠 뷰를 캐싱하고 유지하는 것을 막기 위해 다음과 같이 생각해 냈습니다.

<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 : 웹 페이지를 새로 고칠 때와 웹 사이트를 다시 방문 할 때 (새로 고침없이) 작동합니다. CHROME : 웹 페이지를 새로 고침 할 때만 작동합니다 (다시 방문한 후에도). SAFARI 및 iPad : 작동하지 않습니다. 기록 및 웹 데이터를 삭제해야합니다.

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(); ?>">

기본적으로 유닉스 타임 스탬프를 추가하여 매개 변수를 동적으로 만들고 캐시를 사용하지 않는 경우 작동했습니다.

그러나 내 문제는 약간 달랐습니다. 즉석에서 생성 된 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가 문제를 해결했습니다. 감사합니다!
Woody

2

문제는 Expires:헤더 에도 불구하고 브라우저가 캐시를 확인하지 않고 업데이트되기 전의 이미지의 메모리 내 사본을 재사용하고 있다는 것입니다.

나는 상점과 같은 사이트의 관리자 백엔드에서 제품 이미지를 업로드하는 것과 매우 유사한 상황을 겪었고, 내 경우에는 다른 사람들의 URL 수정 기술을 사용하지 않고 자바 스크립트를 사용하여 이미지 새로 고침을 강제하는 것이 가장 좋은 옵션이라고 결정했습니다. 여기에서 이미 언급했습니다. 대신 location.reload(true)IFRAME의 창에서 호출되는 숨겨진 IFRAME에 이미지 URL을 넣은 다음 페이지에서 내 이미지를 대체했습니다. 이렇게하면 내가있는 페이지뿐만 아니라 나중에 방문하는 페이지에서도 이미지가 새로 고침됩니다. 클라이언트 나 서버가 URL 쿼리 문자열이나 조각 식별자 매개 변수를 기억하지 않아도됩니다.

여기 에 내 대답에 몇 가지 코드를 게시했습니다 .


2

타임 스탬프 추가 <img src="picture.jpg?t=<?php echo time();?>">

항상 파일 끝에 임의의 숫자를 부여하고 캐싱을 중지합니다.


이 솔루션을 구현하면 요청 당 서버에 스트레스가 가해지며 브라우저가 캐시되는 초기 요청시 생성 된 시간이 포함 된 PHP 페이지를 캐시 할 수 있기 때문에 반드시 작동하지는 않습니다. 이는 페이지에 동적 쿼리 문자열이있는 경우에만 안정적으로 작동합니다.
Dmitry

1

사용자와 클라이언트 사이에 잘못 작동하는 투명 프록시의 가능성이 있으므로 이미지가 캐시되지 않도록 완전히 보장 할 수있는 유일한 방법은 쿼리 문자열이나 클라이언트의 일부로 타임 스탬프를 태그하는 것과 같은 고유 한 URI를 제공하는 것입니다. 통로.

해당 타임 스탬프가 이미지의 마지막 업데이트 시간과 일치하는 경우 필요할 때 캐시하고 적절한 시간에 새 이미지를 제공 할 수 있습니다.


1

원래 질문은 텍스트 정보와 함께 저장된 이미지에 관한 것이라고 가정합니다. 따라서 src = ... url을 생성 할 때 텍스트 컨텍스트에 액세스 할 수있는 경우 무의미한 랜덤 또는 타임 스탬프 대신 이미지 바이트의 CRC32 저장 / 사용을 고려하십시오. 그러면 이미지가 많은 페이지가 표시되면 업데이트 된 이미지 만 다시로드됩니다. 결국 CRC 저장이 불가능한 경우 런타임에 계산하여 URL에 추가 할 수 있습니다.


또는 CRC 대신 이미지의 마지막 수정 시간을 사용하는 것이 더 좋습니다!
Doin

1

내 관점에서 이미지 캐싱을 비활성화하는 것은 나쁜 생각입니다. 조금도.

여기서 근본적인 문제는-브라우저가 서버 측에서 업데이트되었을 때 이미지를 업데이트하도록 강제하는 방법입니다.

다시 말하지만, 개인적 관점에서 가장 좋은 해결책은 이미지에 대한 직접 액세스를 비활성화하는 것입니다. 대신 서버 측 필터 / 서블릿 / 기타 유사한 도구 / 서비스를 통해 이미지에 액세스합니다.

제 경우에는 이미지를 반환하고 응답으로 ETag를 첨부하는 나머지 서비스입니다. 서비스는 모든 파일의 해시를 유지하며, 파일이 변경되면 해시가 업데이트됩니다. 모든 최신 브라우저에서 완벽하게 작동합니다. 예, 구현하는 데 시간이 걸리지 만 그만한 가치가 있습니다.

유일한 예외는 파비콘입니다. 어떤 이유로 작동하지 않습니다. 브라우저가 서버 측에서 캐시를 업데이트하도록 강제 할 수 없습니다. ETags, Cache Control, Expires, Pragma 헤더, 아무것도 도움이되지 않았습니다.

이 경우 임의 / 버전 매개 변수를 URL에 추가하는 것이 유일한 해결책 인 것 같습니다.


1

이상적으로는 콘텐츠를 동기화하는 옵션과 함께 각 웹 페이지에 버튼 / 키 바인딩 / 메뉴를 추가해야합니다.

이렇게하려면 동기화해야 할 수있는 리소스를 추적하고 xhr을 사용하여 동적 쿼리 문자열로 이미지를 조사하거나 동적 쿼리 문자열을 사용하여 src로 런타임에 이미지를 만듭니다. 그런 다음 브로드 캐스팅 메커니즘을 사용하여 리소스를 사용하는 웹 페이지의 모든 구성 요소를 업데이트하여 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="">

그러나 여전히 효과적이지 않습니다. 이유 : 브라우저 캐시 ... 마지막이지만 가장 효과적인 방법은 Native 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.