브라우저가 캐시 된 CSS / JS 파일을 강제로 다시로드하는 방법?


993

일부 브라우저 (특히 Firefox 및 Opera)는 브라우저 세션 간에도 .css.js 파일 의 캐시 된 사본을 사용하는 데 매우 열심입니다 . 이 파일 중 하나를 업데이트 할 때 문제가 발생하지만 사용자 브라우저는 캐시 된 사본을 계속 사용합니다.

문제는 파일이 변경되었을 때 사용자 브라우저가 파일을 다시로드하도록하는 가장 우아한 방법은 무엇입니까?

이상적으로 솔루션은 브라우저가 페이지를 방문 할 때마다 파일을 다시로드하도록 강요하지 않습니다. 나는 자신의 해결책을 답변으로 게시 할 것이지만, 더 나은 해결책이 있다면 귀하의 투표가 결정되도록 할 것입니다.

업데이트 :

여기에서 잠시 동안 토론을 한 후 John Millikinda5id 의 제안이 유용하다는 것을 알았습니다 . 이것에 대한 용어 인 auto-versioning이 있습니다.

내 원래 솔루션과 John의 제안이 결합 된 새로운 답변을 아래에 게시했습니다.

SCdF 가 제안한 또 다른 아이디어 는 파일에 가짜 쿼리 문자열을 추가하는 것입니다. (거짓 쿼리 문자열로 타임 스탬프를 자동으로 사용하는 일부 Python 코드는 pi 에 의해 제출되었습니다 .) 그러나 브라우저가 쿼리 문자열로 파일을 캐시하는지 여부에 대한 논의가 있습니다. (브라우저는 파일을 캐시하고 나중에 방문 할 때 파일을 사용하기를 원합니다. 파일이 변경된 경우에만 파일을 다시 가져 오기를 원합니다.)

가짜 쿼리 문자열로 어떤 일이 발생하는지 명확하지 않기 때문에 그 대답을 받아들이지 않습니다.


내 .htaccess 에이 파일이 있으며 캐시 된 파일에 아무런 문제가 없습니다 ExpiresActive On ExpiresDefault "modification".
Frank Conijn

2
파일의 URL에 버전 정보를 추가하는 것이 가장 좋은 방법이라는 데 동의합니다. 모든 사람에게 항상 작동합니다. 그러나 사용하지 않는 경우 브라우저에서 가끔 하나의 CSS 또는 JS 파일을 다시로드하면됩니다. 자신의 탭에서 파일을 열고 SHIFT-reload (또는 CTRL-F5)를 누르십시오! JS를 사용하여 (숨겨진) iframe에 파일을로드하고로드 될 때까지 기다린 다음을 호출하여 효과적으로 동일한 작업을 수행 할 수 있습니다 iframe.contentWindow.location.reload(true). stackoverflow.com/a/22429796/999120의 방법 (4)를 참조하십시오 -이미지에 관한 것이지만 동일하게 적용됩니다.
Doin

2
이 질문이 제기 된 방식과 그 이후로 업데이트 된 방식에 정말 감사드립니다. 그것은 대답에서 기대해야 할 것을 완전히 설명했습니다. 나는 지금부터 내 질문 에이 접근법을 따르려고합니다. 건배!
rd22

답변:


455

업데이트 : John Millikinda5id의 제안 사항을 통합하도록 다시 작성되었습니다 . 이 솔루션은 PHP로 작성되었지만 다른 언어에도 쉽게 적용 할 수 있습니다.

업데이트 2 : Nick Johnson의 의견 을 포함하여 원래 .htaccess정규식이와 같은 파일에 문제를 일으킬 수 있음을 알려줍니다 json-1.3.js. 해결책은 끝에 정확히 10 자리가있는 경우에만 다시 작성하는 것입니다. (10 자리 숫자는 2001 년 9 월 9 일부터 11/20/2286까지의 모든 타임 스탬프를 포함하기 때문에)

먼저 .htaccess에서 다음과 같은 다시 쓰기 규칙을 사용합니다.

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

이제 다음 PHP 함수를 작성합니다.

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

이제 CSS를 포함 할 때마다 다음과 같이 변경하십시오.

<link rel="stylesheet" href="/css/base.css" type="text/css" />

이에:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

이런 식으로 링크 태그를 다시 수정할 필요가 없으며 사용자는 항상 최신 CSS를 볼 수 있습니다. 브라우저는 CSS 파일을 캐시 할 수 있지만 CSS를 변경하면 브라우저는이를 새로운 URL로 인식하므로 캐시 된 사본을 사용하지 않습니다.

이미지, 파비콘 및 JavaScript에서도 작동합니다. 기본적으로 동적으로 생성되지 않은 모든 것.


16
파일 이름 변경 (base.1221534296.css) 대신 버전 관리 (base.css? v = 1221534296)에 매개 변수를 사용한다는 점을 제외하면 내 정적 콘텐츠 서버는 정확히 동일합니다. 그래도 당신의 방법이 조금 더 효율적일 것 같습니다. 매우 시원합니다.
Jens Roland

4
@Kip : 매우 매끄러운 솔루션. URL 재 작성은 단순히 URL을 수정하는 것보다 훨씬 더 많은 것을 제공합니다.
James P.

37
나는 이것이 파일 시스템에 여러 번 액세스한다는 사실을 알고 있습니다-정확히-링크 수 * 요청 수 / 초 ... 당신에게 문제가 될 수도 있고 아닐 수도 있습니다.
Tomáš Fejfar

3
@AlixAxel : 아니요, 매개 변수가 변경되면 브라우저가이를 다시 가져 오지만 일부 공용 프록시는 url 매개 변수가있는 파일을 캐시하지 않으므로 경로에 버전을 포함시키는 것이 가장 좋습니다. 그리고 mod_rewrite를 오버 헤드가 WPO의 다른 모든 성능 병목에 비해 미미하다
옌스 롤랜드

8
첫 번째 file_exists점검이 정말로 필요한가요? filemtime실패하면 false를 반환하므로 filemtime 값을 변수에 할당하고 파일 이름을 바꾸기 전에 false인지 확인하십시오. 불필요한 파일 작업 하나를 줄여서 실제로 추가 할 수 있습니다.
Gavin

184

간단한 클라이언트 측 기술

일반적으로 캐싱은 좋습니다. 따라서 웹 사이트를 개발할 때 스스로 문제를 해결하는지 또는 프로덕션 환경에서 캐시를 제어하려고하는지에 따라 몇 가지 기술이 있습니다.

웹 사이트를 방문하는 일반 방문자에게는 사이트를 개발할 때와 같은 경험이 없습니다. 평균 방문자는 사이트를 자주 방문하지 않기 때문에 (Google 또는 hi5 네트워크가 아닌 경우 한 달에 몇 번만 가능) 파일을 캐시에 보관할 가능성이 적으므로 충분할 수 있습니다. 브라우저에 새 버전을 강제 적용하려면 요청에 항상 쿼리 문자열을 추가하고 주요 변경을 수행 할 때 버전 번호를 늘릴 수 있습니다.

<script src="/myJavascript.js?version=4"></script>

이렇게하면 모든 사람이 새 파일을 얻을 수 있습니다. 브라우저가 파일의 URL을보고 캐시에 사본이 있는지 여부를 판별하기 때문에 작동합니다. 서버가 쿼리 문자열을 사용하여 설정되지 않은 경우 무시되지만 이름은 브라우저에 새 파일처럼 보입니다.

반면에 웹 사이트를 개발하는 경우 개발 버전에 변경 사항을 저장할 때마다 버전 번호를 변경하지 않으려 고합니다. 지루할 것입니다.

따라서 사이트를 개발하는 동안 쿼리 문자열 매개 변수를 자동으로 생성하는 것이 좋습니다.

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

요청에 쿼리 문자열을 추가하는 것이 리소스를 버전 관리하는 좋은 방법이지만 간단한 웹 사이트에서는 필요하지 않을 수 있습니다. 캐싱은 좋은 것임을 기억하십시오.

브라우저가 파일을 캐시에 보관하는 데 반드시 신경을 쓸 필요는 없다는 점도 주목할 가치가 있습니다. 브라우저에는 이런 종류의 정책이 있으며 일반적으로 HTTP 사양에 명시된 규칙에 따라 재생됩니다. 브라우저가 서버에 요청하면 응답의 일부는 EXPIRES 헤더입니다. 브라우저에 캐시에 보관해야하는 기간을 알려주는 날짜입니다. 다음에 브라우저가 동일한 파일에 대한 요청을 처리 할 때, 캐시에 사본이 있고 EXPIRES 날짜를 찾아서 사용해야하는지 여부를 결정합니다.

믿거 나 말거나, 실제로 브라우저 캐시를 영구적으로 만드는 서버입니다. 서버 설정을 조정하고 EXPIRES 헤더를 변경할 수 있지만 위에서 작성한 작은 기술은 아마도 훨씬 간단한 방법 일 것입니다. 캐싱이 양호하기 때문에 일반적으로 해당 날짜를 미래까지 ( "Fu-future Expires Header") 설정하고 위에서 설명한 기술을 사용하여 변경을 수행하려고합니다.

HTTP에 대한 자세한 정보 나 이러한 요청을 작성하는 방법에 관심이 있다면 Steve Souders의 "고성능 웹 사이트"를 참조하십시오. 주제에 대한 아주 좋은 소개입니다.


3
Javascript로 쿼리 문자열을 생성하는 빠른 트릭은 활발한 개발 중에 효과적입니다. 나는 PHP와 같은 일을했다.
Alan Turing

2
이것이 원래 포스터의 원하는 결과를 얻는 가장 쉬운 방법입니다. 페이지를로드 할 때마다 .css 또는 .js 파일을 강제로 다시로드하려는 경우 mod_rewrite 메소드가 제대로 작동합니다. 이 방법은 실제로 파일을 변경하고 실제로 다시로드하기를 원할 때까지 캐싱을 허용합니다.
scott80109

@ keparo, 모든 페이지에 충분한 수의 jquery가 있습니다.이를 수동으로 변경하려면 한 달이 걸립니다. 각 페이지를 코딩하지 않고 모든 문제를 해결할 수 있다면
크래커

1
다음을 사용할 때 CSS에서 작동하지 않는 것 같습니다.<link href='myCss.css?dev=14141'...>
Noumenon

3
이것은 실용적인 해결책이 아닙니다. 많은 수의 브라우저는 쿼리 문자열이있는 것을 캐시하는 것을 거부합니다. 정적 콘텐츠에 대한 참조에 쿼리 문자열이있는 경우 Google, GTMetrix 및 이와 유사한 도구가 플래그를 발생시키는 이유입니다. 확실히 개발을위한 알맞은 솔루션이지만 생산을위한 솔루션은 아닙니다. 또한 브라우저는 서버가 아닌 캐싱을 제어합니다. 서버는 새로 고쳐야 할 때 간단하게 제안합니다. 브라우저는 서버를 듣지 못하며 종종 듣지 않습니다. 모바일 장치가 대표적인 예입니다.
네이트 I I

113

아파치 용 Google의 mod_pagespeed 플러그인은 자동 버전 관리를 수행합니다. 정말 매끄 럽습니다.

웹 서버에서 나가는 도중에 HTML을 구문 분석하고 (PHP, 레일, 파이썬, 정적 HTML 등 작동) ID 코드를 포함하도록 CSS, JS, 이미지 파일에 대한 링크를 다시 씁니다. 캐시 제어 기능이 매우 긴 수정 된 URL에서 파일을 제공합니다. 파일이 변경되면 URL이 자동으로 변경되어 브라우저가 다시 가져와야합니다. 기본적으로 코드를 변경하지 않고 작동합니다. 나가는 길에 코드를 축소 할 수도 있습니다.


1
훌륭하지만 여전히 베타 버전입니다. 엔터프라이즈 서비스에 사용할 수 있습니까?
이상현

26
브라우저 문제인 경우에는 잘못된 것입니다 (소스와 자동 확인). 우리 (개발자) 줘 실제 뇌는-닦아 새로 고침을 : <CTRL> + F5
T4NK3R

25
mod_pagespeed는 기능적으로 html / css / js에 대한 완전 자동 빌드 / 컴파일 단계와 동일합니다. 빌드 시스템이 본질적으로 잘못되었다고 생각하거나 완전히 자동화 된 것에 문제가 있다고 생각하는 심각한 개발자를 찾기가 어려울 것이라고 생각합니다. 깔끔한 빌드의 비유는 mod_pagespeed의 캐시를 지우는 것입니다 : code.google.com/p/modpagespeed/wiki/… ?
Leopd

3
@ T4NK3R mod_pagespeed는 캐시 관리를 위해 소스와 아무 것도 할 필요가 없으며 단순히 축소와 같은 일에 도움 이 될 있다고 언급했습니다 . 그것이 "잘못"인지 아닌지에 관해서, 그것은 완전히 주관적입니다. 당신에게 잘못되었을 수도 있지만, 이것이 본능적으로 나쁘다 는 것을 의미하지는 않습니다 .
Madbreaks


93

버전을 수동으로 변경하는 대신 실제 CSS 파일의 MD5 해시를 사용하는 것이 좋습니다.

따라서 귀하의 URL은

http://mysite.com/css/[md5_hash_here]/style.css

여전히 다시 쓰기 규칙을 사용하여 해시를 제거 할 수 있지만 이점은 URL이 동일하면 파일이 변경되지 않기 때문에 캐시 정책을 "영원히 캐시"로 설정할 수 있다는 것입니다.

그런 다음 파일의 해시를 계산하고 태그를 업데이트하는 간단한 셸 스크립트를 작성할 수 있습니다 (포함 할 별도의 파일로 옮길 수 있습니다).

CSS가 변경 될 때마다 해당 스크립트를 실행하면됩니다. 브라우저는 파일이 변경 될 때만 파일을 다시로드합니다. 수정 한 후 실행 취소하면 방문자가 다시 다운로드하지 못하도록하기 위해 어떤 버전으로 돌아 가야할지 고민 할 필요가 없습니다.


1
불행히도 나는 그것을 구현하는 방법을 모른다. 조언하시기 바랍니다 ... 자세한 내용은 ...
마이클 펠프스

쉘, 루비 등의 구현은 훌륭 할 것입니다.
Peter

3
아주 좋은 해결책입니다. 그러나 매 페이지 방문마다 모든 파일 요청 (css, js, images, html..etc)에서 파일의 해시를 계산하는 데 리소스를 소비한다고 생각합니다.
DeepBlue

이 꿀꺽, 툴툴 거리는 소리 또는 웹팩, 각 솔루션의 구현 다릅니다 함께 번들 JS 또는 CSS를 사용하지만, 빌드 단계는 현대 번들 애플 리케이션을위한 일반적이고 제안으로 파일을 해싱 사람들을위한 표준 솔루션입니다
브랜든 소렌 Culley

@ DeepBlue-answer는 "CSS가 변경 될 때마다 해당 스크립트를 실행합니다" 라고 말합니다 . 모든 페이지 방문이 아닙니다. OTOH 대답은 주요 세부 사항을 생략합니다-변경된 해시가 어떻게 URL의 일부가됩니까? 모르겠어요 ...
ToolmakerSteve

70

왜이 솔루션을 구현하기 위해 많은 고통을 겪고 있는지 확실하지 않습니다.

파일의 수정 된 타임 스탬프를 가져 와서 파일에 쿼리 문자열로 추가하면됩니다.

PHP에서는 다음과 같이합니다.

<link href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">

filemtime은 파일 수정 타임 스탬프를 반환하는 PHP 함수입니다.


당신은 그냥 사용할 수 있습니다 mycss.css?1234567890.
Gavin

3
<link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>GET 변수 (제안 된 형식으로)를 사용하여 URL을 캐싱하는 것과 관련하여이 스레드의 일부 인수가 올바른 경우를 대비 하여 매우 우아 합니다.
luke_mclachlan

내 마지막 의견에 더하여, 나는 워드 프레스가 ?ver=그렇게 알고있는 사람을 사용 하는 것을 보았습니다 !
luke_mclachlan

훌륭한 솔루션. 또한 필자는 filemtime이 FQDN (정규화 된 도메인 이름)에서 작동하지 않는다는 것을 알았으므로 href 부분에는 FQDN을 사용하고 filemtime 부분에는 $ _SERVER [ "DOCUMENT_ROOT"]를 사용했습니다. 예 : <link rel = "stylesheet"href = "http : //theurl/mycss.css? v = <? php echo filemtime ($ _ SERVER ["DOCUMENT_ROOT "]. '/mycss.css')?>"/>
rrtx2000

큰 감사합니다. 간단하고 좋습니다. 여기 파이썬이 있습니다 : progpath = os.path.dirname (sys.argv [0]) def versionize (file) : timestamp = os.path.getmtime ( '% s /../ web / % s'% (progpath , file)) return '% s? v = % s'% (file, timestamp) print <link href = "% s"rel = "stylesheet" ''type = "text / css"/> '\ % versionize ( 'css / main.css')
dlink

52

당신은 그냥 둘 수 ?foo=12341234이 원하는대로 당신으로 변경, 당신의 CSS / JS 가져 오기의 끝에서. SO html 소스를 살펴보십시오.

그 아이디어는? 어쨌든 요청에서 매개 변수는 삭제 / 무시되며 새 버전을 출시 할 때 해당 번호를 변경할 수 있습니다.


참고 : 이것이 캐싱에 어떻게 영향을 미치는지에 관한 몇 가지 주장이 있습니다. 나는 그것의 일반적인 요지가 매개 변수 있거나없는 GET 요청을 캐싱 할 수 있어야 하므로 위의 솔루션이 작동해야한다고 생각합니다.

그러나 웹 서버는 사양의 해당 부분과 사용자가 사용하는 브라우저를 준수할지 여부를 결정해야합니다. 어쨌든 새로운 버전을 요청할 수 있기 때문입니다.


무의미한 말. query-string (일명 GET 매개 변수)은 URL의 일부입니다. 캐시 될 수 있으며 캐시됩니다. 이것은 좋은 해결책입니다.
troelskn

9
@troelskn : HTTP 1.1 사양은 달리 명시하지 않습니다 (쿼리 매개 변수가있는 GET 및 HEAD 요청과 관련하여). 참조 w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
마이클 존슨

4
모든 주요 브라우저에서 쿼리 문자열 유형의 버전 관리를 시도했으며 파일, 사양을 캐시하지 않습니다. 그러나 프록시 문자열을 캐싱하면 파일을 캐시하지 않을 가능성이 있기 때문에 쿼리 문자열을 남용하지 않고 style.TIMESTAMP.css 형식을 사용하는 것이 좋습니다.
Tomas Andrle

34
어떤 이유로 든 Stackoverflow 자체가 쿼리 문자열 방법을 사용한다는 점에 주목할 가치가 있습니다.
Jason

2
? = parameter를 사용하면 매개 변수가 변경 될 때 브라우저가 캐시 된 파일을 다시 가져 오지 않는지 확인했습니다. 유일한 방법은 Kip의 답변에 따라 서버 끝에서 파일 이름 자체를 프로그래밍 방식으로 변경하는 것입니다.
arunskrish

41

이것을 "자동 버전 관리"라고 들었습니다. 가장 일반적인 방법은 정적 파일의 mtime을 URL 어딘가에 포함시키고 다시 쓰기 핸들러 또는 URL conf를 사용하여 제거하는 것입니다.

또한보십시오:


3
감사합니다. 이것이 제 아이디어가 논의 된 또 하나의 사례라고 생각합니다. 그 아이디어가 무엇인지 몰랐기 때문에 Google 검색에서 찾지 못했습니다.
Kip

27

30 개 정도의 기존 답변은 2008 년경 웹 사이트에 대한 훌륭한 조언입니다. 그러나 최신 단일 페이지 애플리케이션 (SPA)과 관련하여 몇 가지 기본 가정을 다시 생각해야 할 때가 있습니다. 특히 웹 서버가 최신 버전의 단일 서버에만 서비스를 제공하는 것이 바람직하다는 생각입니다. 파일.

브라우저에 M 버전 의 SPA가로드 된 사용자라고 가정 하십시오.

  1. CD 파이프 라인 은 응용 프로그램 의 새로운 버전 N 을 서버에 배포 합니다.
  2. SPA 내에서 탐색하면 XHR을 서버로 전송하여 /some.template
    • (브라우저가 페이지를 새로 고치지 않았으므로 여전히 M 버전을 실행하고 있습니다 )
  3. 서버는 다음과 같은 내용으로 응답 합니다. 템플릿의 /some.template버전 M 또는 N 을 반환 하시겠습니까?

/some.template버전 MN 사이 에서 변경된 형식 (또는 파일의 이름이 바뀌거나 다른 이름) 이 있는 경우 템플릿의 버전 N 이 파서 의 이전 버전 M 을 실행하는 브라우저로 전송 되지 않을 것입니다 . †

두 가지 조건이 충족되면 웹 앱에서이 문제가 발생합니다.

  • 초기 페이지로드 후 언젠가 리소스가 비동기 적으로 요청 됨
  • 앱 로직은 리소스 컨텐츠에 대한 사항 (향후 버전에서 변경 될 수 있음)을 가정합니다.

앱이 여러 버전을 병렬로 제공해야하는 경우 캐싱 및 "다시로드"문제를 해결하는 것이 간단 해집니다.

  1. 버전 DIRS에 모든 사이트의 파일을 설치합니다 /v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. 브라우저가 파일을 영원히 캐시 할 수 있도록 HTTP 헤더 설정
    • (또는 더 나은 방법으로 CDN에 모든 것을 넣습니다)
  3. 버전이 지정된 디렉토리 중 하나에서 해당 파일을 가리 키도록 모든 <script><link>태그 등을 업데이트하십시오 .

마지막 단계는 서버 측 또는 클라이언트 측 코드의 모든 URL에 대해 URL 빌더를 호출해야하므로 까다로울 수 있습니다. 또는 <base>태그를 영리하게 사용 하고 한 곳에서 현재 버전을 변경할 수 있습니다.

†이 문제를 해결하는 한 가지 방법은 새 버전이 출시 될 때 브라우저가 모든 것을 다시로드하도록하는 것입니다. 그러나 진행중인 작업을 완료하기 위해 v-current와 v-previous의 두 가지 버전을 병렬로 지원하는 것이 가장 쉬운 방법 일 수 있습니다.


마이클-당신의 의견은 매우 관련이 있습니다. SPA에 대한 해결책을 찾으려고 정확하게 여기에 있습니다. 나는 몇 가지 조언을 얻었지만 스스로 해결책을 찾아야했습니다. 결국, 나는 내가 생각해 낸 것에 정말로 만족했기 때문에 블로그 게시물 과이 질문에 대한 답변 (코드 포함)을 작성했습니다. 포인터 주셔서 감사합니다
statler

좋은 의견. 사람들이 캐시 버스 팅 및 HTTP 캐싱에 대해 SPA의 새로운 문제를 해결하지 않고 웹 사이트 캐싱 문제에 대한 실제 솔루션으로 계속 말하는 동안 이해할 수 없습니다.
David Casillas 2016 년

1
탁월한 반응과 이상적인 전략! base태그 언급에 대한 보너스 포인트 ! 오래된 코드를 지원하는 것에 관해서는 : 이것이 항상 가능한 것은 아니며 항상 좋은 생각이 아닙니다. 새로운 버전의 코드는 다른 앱 조각 변경을 지원하거나 긴급 수정, 취약성 패치 등이 포함될 수 있습니다. 아직이 전략을 직접 구현하지는 않았지만 obsolete다음 번에 비동기식 호출 을 수행 할 때 배포에서 이전 버전에 태그를 지정 하고 다시로드하도록 강제 (또는 WebSockets를 통해 모든 세션을 강제로 인증 취소 해야 함)해야한다는 느낌이 항상 느껴졌습니다. ).
Jonny Asmar

단일 페이지 응용 프로그램과 관련하여 잘 생각 된 답변을 볼 수 있습니다.
네이트 I I

자세한 정보를 검색하려면 "청록색 배포"입니다.

15

foo.css? version = 1을 사용하지 마십시오! 브라우저는 GET 변수로 URL을 캐시해서는 안됩니다. http://www.thinkvitamin.com/features/webapps/serving-javascript-fast 에 따르면 IE와 Firefox는 이것을 무시하지만 Opera와 Safari는 그렇지 않습니다! 대신 foo.v1234.css를 사용하고 다시 쓰기 규칙을 사용하여 버전 번호를 제거하십시오.


1
우선 모든 브라우저는 캐시하지 않으며 HTTP 기능입니다. 왜 http가 URI의 구조에 관심이 있습니까? HTTP 캐시가 URI의 의미를 이해하여 쿼리 문자열로 항목을 캐시하지 않도록 명시하는 스펙에 대한 위반 참조가 있습니까?
AnthonyWJones

13
객체 캐싱 기능이 포함 된 웹 브라우저 (브라우저의 캐시 디렉토리 확인). HTTP는 캐시 제어를 제안하는 서버에서 클라이언트 (프록시, 브라우저, 스파이더 등)에 대한 지시문을 포함하는 프로토콜입니다.
tzot

13

Laravel (PHP)에서는 파일 수정 타임 스탬프를 사용하여 다음과 같이 명확하고 우아한 방식으로 수행 할 수 있습니다.

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>

CSS와 유사

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">

html 출력 예 ( Unix 타임 스탬프filemtime 로 반환 시간 )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">

이 명령의 출력은 무엇입니까 (html)? 그리고 내가처럼 V 전용 버전을 갱신해야하는 경우 = 3, V = 4 등 -?로드 CSS를 매번 사용자에게 브라우저를 강요하지하는 일은 웹 사이트를 입력
게디 미나스

filemtime : "이 함수는 파일의 데이터 블록이 쓰여지는 시간, 즉 파일의 내용이 변경된 시간을 반환합니다." src : php.net/manual/en/function.filemtime.php
Kamil Kiełczewski

11

RewriteRule은 끝에 점 표기법 버전이 포함 된 js 또는 css 파일에 대한 작은 업데이트가 필요합니다. 예 : json-1.3.js.

도트 부정 클래스 [^.]를 정규식에 .number로 추가했습니다. 무시됩니다.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]

2
입력 주셔서 감사합니다! 이 글을 썼기 때문에 나는 이것으로도 화상을 입었습니다. 내 해결책은 파일 이름의 마지막 부분에 정확히 10 자리가 포함되어있는 경우에만 다시 작성하는 것이 었습니다. (10 자리 숫자는 2001 년 9 월 9 일부터 11/20/2286까지 모든 타임 스탬프에 적용됩니다.)이 정규식을 포함하도록 답변을 업데이트했습니다.^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip

정규식을 이해하지만 [^.]여기에서 어떤 문제를 해결하고 있는지 이해하지 못합니다 . 또한 \d문자 클래스 내부에 글을 쓰면 아무런 이점이 없습니다 \d+. 같은 일을 할 것입니다. 게시 된대로 패턴은 문자 수, 문자 그대로, 점이 아닌 점, 하나 이상의 자릿수, 점, css또는 또는 js파일 이름의 끝과 일치합니다 . 샘플 입력과 일치하지 않습니다 : regex101.com/r/RPGC62/1
mickmackusa

10

ASP.NET 4.5 이상에서는 스크립트 번들링을 사용할 수 있습니다 .

요청 http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81은 AllMyScript 번들에 대한 것이며 쿼리 문자열 쌍 v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81을 포함합니다. 쿼리 문자열 v에는 캐싱에 사용되는 고유 식별자 인 값 토큰이 있습니다. 번들이 변경되지 않는 한 ASP.NET 응용 프로그램은이 토큰을 사용하여 AllMyScripts 번들을 요청합니다. 번들의 파일이 변경되면 ASP.NET 최적화 프레임 워크는 새 토큰을 생성하여 번들에 대한 브라우저 요청이 최신 번들을 얻도록합니다.

축소로 첫 페이지로드시 성능 향상을 포함하여 번들링의 다른 이점이 있습니다.


css 또는 js 파일 만 변경하면 bundle.config에서 변경하지 않고 캐싱 문제를 어떻게 해결할 수 있습니까?
vedankita kumbhar

10

여기 순수한 JavaScript 솔루션이 있습니다

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

위는 사용자가 마지막으로 사이트를 방문한 시간을 보여줍니다. 마지막 방문이 새 코드를 릴리스하기 전인 경우 location.reload(true)서버에서 페이지를 강제로 새로 고칩니다.

나는 보통 이것을 첫 번째 스크립트로 가지고 <head>있으므로 다른 내용이로드되기 전에 평가됩니다. 재 장전이 필요한 경우, 사용자에게 거의 눈에 띄지 않습니다.

로컬 저장소를 사용하여 브라우저에 마지막 방문 타임 스탬프를 저장하고 있지만 이전 버전의 IE를 지원하려는 경우 쿠키를 믹스에 추가 할 수 있습니다.


나는 이와 같은 것을 시도했는데, 이것은 다시로드 된 페이지에서만 작동하지만 사이트에 동일한 CSS / 이미지를 공유하는 여러 페이지가 있으면 다른 페이지는 여전히 오래된 리소스를 사용합니다.
DeepBlue

9

재미있는 소식. "bogus"쿼리 문자열에 아무런 문제가 없었던 사실과 함께 여기에있는 모든 대답을 읽은 후에 (모든 사람이 이것을 사용하기를 꺼려하는 이유가 확실하지 않습니다) 해결책을 생각합니다 (아파치 다시 쓰기 규칙이 필요 없음) 허용 된 답변에서와 같이)은 파일 날짜 시간 대신 CSS 파일 내용의 짧은 HASH를 가짜 쿼리 문자열로 계산하는 것입니다.

결과는 다음과 같습니다.

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

물론 날짜 시간 솔루션은 CSS 파일을 편집하는 경우 작업을 수행하지만 파일 날짜 시간이 아니라 CSS 파일 내용에 관한 것 같습니다. 왜 혼합되어 있습니까?


8

개발을 위해 크롬에는 훌륭한 솔루션이 있다는 것을 알았습니다.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

개발자 도구를 연 상태에서 새로 고침 버튼을 길게 클릭하면 "빈 캐시 및 하드 다시로드"위로 마우스를 가져갑니다.

이것은 내 가장 친한 친구이며 원하는 것을 얻을 수있는 초경량 방법입니다!


Chrome을 개발 환경으로 사용하는 경우 다른 비 침습적 솔루션은 캐시를 비활성화하는 것입니다. 설정 톱니 바퀴에서 '캐시 비활성화'를 선택하여 디스크 캐시를 무효화 할 수 있습니다 (참고 : DevTools가 표시 / 열려 있어야합니다) 이 작동하려면).
Velojet

7

Kip의 완벽한 솔루션에 감사드립니다!

Zend_view_Helper로 사용하도록 확장했습니다. 내 클라이언트는 가상 호스트에서 자신의 페이지를 실행하기 때문에 해당 페이지를 확장했습니다.

다른 사람에게도 도움이되기를 바랍니다.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

건배와 감사합니다.


7

스크립트 노드 (또는 CSS) 요소를 동적으로 작성하는 클라이언트 측 DOM 접근법을 찾지 못했습니다.

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>

6

크롬이 구글 하드 다시로드 뿐만 아니라 캐시 비우기 및 하드 새로 고침 하나를 선택합니다 (에서 모드를 검사)를 클릭하고 새로 고침 버튼을 길게 할 수 option.You을.


그들은, F12 일명 "개발 도구"에 일명 Ctrl + Shift + I, 일명 언급하는 "모드를 검사"로, 명확히하기 ant menu> More Tools> Developer Tools, 일명 right click>을 Inspect Element. 또한 매번 다시로드 할 때마다 다시로드하기 위해 개발 도구 (어딘가의 위치를 ​​잊어 버림)의 어딘가에 묻혀있는 설정이 있습니다.
Jonny Asmar

5

js / css 파일의 spureous 매개 변수로 session-id를 추가하면 "세션 전체 캐싱"을 강제 실행할 수 있습니다.

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

버전 전체 캐싱을 원할 경우 파일 날짜 또는 이와 유사한 것을 인쇄하는 코드를 추가 할 수 있습니다. Java를 사용하는 경우 사용자 정의 태그를 사용하여 우아한 방식으로 링크를 생성 할 수 있습니다.

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>

5

다음 위치에 파일이 있다고 가정하십시오.

/styles/screen.css

버전 정보가 포함 된 쿼리 매개 변수를 URI에 추가 할 수 있습니다 (예 :

/styles/screen.css?v=1234

또는 다음과 같은 버전 정보를 추가 할 수 있습니다.

/v/1234/styles/screen.css

IMHO 두 번째 방법은 상대 URL을 사용하여 이미지를 참조 할 수 있기 때문에 CSS 파일에 더 좋습니다 background-image.

body {
    background-image: url('images/happy.gif');
}

URL은 효과적으로 다음과 같습니다.

/v/1234/styles/images/happy.gif

즉, 사용 된 버전 번호를 업데이트하면 서버는이를 새 리소스로 취급하고 캐시 된 버전을 사용하지 않습니다. Subversion / CVS / etc를 기반으로 버전 번호를 사용하는 경우 개정판은 CSS 파일에서 참조 된 이미지에 대한 변경 사항이 있음을 의미합니다. 즉 예를 첫 번째 계획으로 URL의 보장되지 않으므로 images/happy.gif상대적 /styles/screen.css?v=1235IS를/styles/images/happy.gif 버전 정보가 포함되어 있지 않습니다.

Java 서블릿과 함께이 기술을 사용하여 캐싱 솔루션을 구현 /v/*했으며 기본 리소스 (즉 /styles/screen.css) 를 위임하는 서블릿으로 요청을 처리합니다 . 개발 모드에서 나는 (당신이 Tomcat의 위임 경우이 일반적으로 304 결과 서버와 자원의 신선도를 확인 항상 클라이언트를 말해 헤더를 캐싱 세트 DefaultServlet.css, .js동안 배포 모드 등 파일이 변경되지 않은) "캐시 영원히"라는 헤더를 설정했습니다.


필요한 경우 이름을 바꿀 수있는 폴더를 추가하면 상대 URL 만 사용하는 경우 작동합니다. 그런 다음 기본 폴더 (예 : PHP)에서 올바른 폴더로 리디렉션해야합니다 <?php header( 'Location: folder1/login.phtml' ); ?>.
Gruber

1
두 번째 방법을 사용하면 CSS를 변경하면 상대 URL을 참조하는 모든 이미지의 캐시 된 사본이 무효화되며 이는 바람직하거나 바람직하지 않을 수 있습니다.
TomG

5

CSS / JS URL로 임의의 숫자를 추가 할 수 있습니다.

example.css?randomNo=Math.random()

5

ASP.NET의 경우 고급 옵션 (디버그 / 릴리스 모드, 버전)을 갖춘 다음 솔루션을 가정합니다.

이러한 방식으로 포함 된 Js 또는 Css 파일 :

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix 및 Global.CssPostfix는 Global.asax에서 다음과 같은 방법으로 계산됩니다.

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}

4

나는 최근에 파이썬을 사용하여 이것을 해결했다. 코드는 다음과 같습니다 (다른 언어로 쉽게 채택 할 수 있어야 함).

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

이 코드는 기본적으로 파일에 타임 스탬프를 쿼리 매개 변수로 URL에 추가합니다. 다음 함수의 호출

script("/main.css")

결과

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

물론 html을 다시 변경할 필요가 없으며 CSS 파일을 터치하면 캐시 무효화가 자동으로 트리거됩니다. 매우 잘 작동하며 오버 헤드는 눈에 띄지 않습니다.


os.stat ()가 병목 현상을 일으킬 수 있습니까?
hoju

디스크가 매우 느리고 요청이 너무 많으면 @Richard 통계에 병목 현상이 발생할 수 있습니다. 이 경우 타임 스탬프를 메모리 어딘가에 캐시하고 매번 새로 배포 할 때마다이 캐시를 제거 할 수 있습니다. 그러나 대부분의 사용 사례에서는 이러한 복잡성이 필요하지 않습니다.
pi.

4

git + PHP를 사용하는 경우 다음 코드를 사용하여 git repo가 ​​변경 될 때마다 캐시에서 스크립트를 다시로드 할 수 있습니다.

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;

4

캐싱을 피하려는 개발자 인 경우 크롬 네트워크 탭에 캐시 비활성화 옵션이 있습니다. 그렇지 않으면 두 개의 스크립트 태그를 사용하여 서버 렌더링 프레임 워크없이이를 수행 할 수 있습니다.

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>

4

이 질문은 매우 구식이며, 누군가이 문제를 Google에 표시 할 때 가장 먼저 나타납니다. 이것은 op가 원하는 방식으로 질문에 대한 대답이 아니라 개발 및 테스트 중에이 문제가있는 개발자에 대한 대답입니다. 중복으로 표시 되므로이 주제에 대한 새 질문을 게시 할 수 없습니다.

다른 많은 사람들과 마찬가지로 캐싱을 간단히 제거하고 싶었습니다.

"keep caching consistent with the file" .. 너무 번거 로움 ..

일반적으로 말해서, 대부분의 프로젝트에서 변경되지 않은 파일을 다시로드하더라도 더 많이로드하는 것은 중요하지 않습니다. 우리는 대부분 디스크에서 로딩하는 앱을 개발하는 동안 localhost:portincrease in network traffic문제는 큰 문제가 아닙니다 .

대부분의 소규모 프로젝트는 진행 중입니다. 프로덕션에서는 결코 끝나지 않습니다. 그들에게는 더 이상 필요하지 않습니다 ..

따라서 Chrome 개발자 도구 를 사용하는 경우 아래 이미지와 같이이 캐싱 비활성화 방법을 사용할 수 있습니다. 크롬이 캐시 된 파일을 다시로드하도록하는 방법

그리고 파이어 폭스 캐싱 문제가있는 경우 : 파이어 폭스에서 자산을 다시로드하는 방법

개발 중에 Firefox에서 캐싱을 비활성화하는 방법 앱을 자주 업데이트하고 답변에 설명 된 것과 같은 전용 캐시 동기화 메커니즘을 제공하지 않으면 사용자는 오래된 캐시 무효화 모듈을 사용하므로 개발시에만이 작업을 수행해야합니다. 위.

예,이 정보는 이미 이전 답변에 있지만 여전히 정보를 찾으려면 Google 검색을 수행해야했습니다.

이 답변이 매우 명확하기 때문에 이제는 필요하지 않습니다.


OP는 무언가를 물었고 다른 것을 대답했습니다. 로컬에서는 강제로드에 대한 것이 아니라 프로덕션 환경에서 최종 사용자에게 캐시 등을 비활성화하기 위해 위와 같이 할 것을 요청할 수 없습니다.
Jitendra Pancholi

3

여기에있는 모든 대답은 이름 지정 체계에서 일종의 버전 관리를 제안하는 것 같습니다.

브라우저는 웹 서버 응답, 특히 http 헤더를 읽어 캐시 할 대상과 캐시 할 대상을 잘 알고 있어야합니다.이 자원의 유효 기간은 얼마입니까? 이 리소스를 마지막으로 검색 한 후에 업데이트 되었습니까? 등.

사물이 '올바르게'구성되어있는 경우 애플리케이션 파일을 업데이트하면 브라우저 캐시가 새로 고쳐 져야합니다. 예를 들어 브라우저가 파일을 캐싱하지 않도록 웹 서버를 구성 할 수 있습니다 (잘못된 생각).

작동 방식에 대한 자세한 설명은 여기 https://www.mnot.net/cache_docs/#WORK


3

하드 코드를 다시로드 할 위치 에이 코드를 추가하기 만하면됩니다 (브라우저가 캐시 된 CSS / JS 파일을 다시로드하도록 강제하십시오) .load 내부에서 루프처럼 새로 고치지 않도록하십시오.

 $( window ).load(function() {
   location.reload(true);
});

Chrome에서는 작동하지 않습니다. 여전히 디스크 캐시에서 자산을로드
제이슨 김

3

서버 측 코드를 사용하여 파일의 날짜를 추가하십시오 ... 그러면 파일이 변경 될 때만 캐시되고 다시로드됩니다

ASP.NET에서

<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    

다음과 같이 단순화 할 수 있습니다.

<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>

확장 메소드를 프로젝트에 추가하여 페이지를 확장합니다.

public static class Extension_Methods
{
    public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
    {
        string sFilePath = oPg.Server.MapPath(sRelPath);
        string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
        string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");

        return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
    }
}

2

다음 프로세스를 구현하는 것이 좋습니다.

  • 배포 할 때마다 css / js 파일의 버전을 정하십시오. screen.1233.css (버전 관리 시스템을 사용하는 경우 SVN 버전이 될 수 있음)

  • 로딩 시간을 최적화하기 위해 축소

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.