programing tip

브라우저가 캐시 된 CSS / JS 파일을 다시로드하도록 강제하는 방법은 무엇입니까?

itbloger 2020. 9. 28. 08:22
반응형

브라우저가 캐시 된 CSS / JS 파일을 다시로드하도록 강제하는 방법은 무엇입니까?


일부 브라우저 (특히 Firefox 및 Opera)는 브라우저 세션간에 .css.js 파일 의 캐시 된 복사본을 사용하는 데 매우 열성적이라는 것을 알게되었습니다 . 이로 인해 이러한 파일 중 하나를 업데이트 할 때 문제가 발생하지만 사용자의 브라우저는 캐시 된 복사본을 계속 사용합니다.

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

이상적으로는이 솔루션은 페이지를 방문 할 때마다 브라우저가 파일을 다시로드하도록 강제하지 않습니다. 나는 내 자신의 솔루션을 답변으로 게시 할 것이지만 누구에게 더 나은 솔루션이 있는지 궁금합니다. 여러분의 투표에 따라 결정하도록하겠습니다.

업데이트 :

잠시 여기에서 토론을 허용 한 후 John Millikinda5id 의 제안이 유용하다는 것을 알게되었습니다 . 자동 버전 관리 라는 용어가 있습니다.

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

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

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


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

업데이트 2 : Nick Johnson의 주석을 통합 하면 원래 .htaccess정규식이 json-1.3.js. 해결책은 끝에 정확히 10 자리가있는 경우에만 다시 쓰는 것입니다. (10 자리 숫자는 2001 년 9 월 9 일부터 2286 년 11 월 20 일까지의 모든 타임 스탬프를 포함하기 때문입니다.)

먼저 .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을 새 URL로 인식하므로 캐시 된 사본을 사용하지 않습니다.

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


간단한 클라이언트 측 기술

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

웹 사이트의 일반 방문자는 사이트를 개발할 때와 동일한 경험을 갖지 않습니다. 평균적인 방문자가 사이트를 덜 자주 방문하기 때문에 (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 헤더를 변경할 수 있지만 위에서 작성한 작은 기술은 아마도 훨씬 더 간단한 방법 일 것입니다. 캐싱이 좋으므로 일반적으로 해당 날짜를 먼 미래 ( "미래 만료 헤더")로 설정하고 위에서 설명한 기술을 사용하여 강제로 변경합니다.

HTTP에 대한 자세한 정보 나 이러한 요청이 수행되는 방법에 관심이 있다면 Steve Souders의 "고성능 웹 사이트"가 좋은 책입니다. 주제에 대한 아주 좋은 소개입니다.


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

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


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

따라서 URL은 다음과 같습니다.

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

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

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

CSS가 변경 될 때마다 해당 스크립트를 실행하기 만하면됩니다. 브라우저는 파일이 변경된 경우에만 파일을 다시로드합니다. 편집 한 다음 실행 취소하면 방문자가 다시 다운로드하지 않도록하기 위해 어떤 버전으로 돌아 가야하는지 파악하는 데 어려움이 없습니다.


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

파일의 수정 된 타임 스탬프를 가져 와서 파일에 쿼리 문자열로 추가하는 경우 수행 할 모든 작업

PHP에서는 다음과 같이 할 것입니다.

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

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


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

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


참고 : 이것이 캐싱에 정확히 어떻게 영향을 미치는지에 대한 몇 가지 논쟁이 있습니다. 일반적인 요점은 매개 변수 있거나없는 GET 요청 이 캐싱 가능 해야 하므로 위의 솔루션이 작동해야한다는 것입니다.

그러나 사양의 해당 부분과 사용자가 사용하는 브라우저를 고수할지 여부를 결정하는 것은 웹 서버 모두에게 달려 있습니다. 바로 진행하여 어쨌든 새로운 버전을 요청할 수 있기 때문입니다.


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

또한보십시오:


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

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

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

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

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

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

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

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

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

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


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


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

정규식에 점 부정 클래스 [^.]를 추가했습니다. 그래서 .number. 무시됩니다.

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

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

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

축소를 통해 처음 페이지를로드 할 때 성능 향상을 포함하여 번들링에 대한 다른 이점이 있습니다.


다음은 순수한 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를 지원하려는 경우 믹스에 쿠키를 추가 할 수 있습니다.


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'))}}">

흥미로운 게시물입니다. 여기에서 "가짜"쿼리 문자열에 문제가 없다는 사실과 결합 된 모든 답변을 읽은 후 (왜 모든 사람이 이것을 사용하기를 꺼려하는지 잘 모르겠습니다) 솔루션을 추측합니다 (아파치 재 작성 규칙이 필요하지 않음). 수락 된 답변에서와 같이) CSS 파일 내용의 짧은 HASH (파일 datetime 대신)를 가짜 쿼리 문자열로 계산하는 것입니다.

결과는 다음과 같습니다.

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

물론 datetime 솔루션은 CSS 파일을 편집하는 경우에도 작업을 수행하지만 파일 datetime이 아니라 css 파일 내용에 관한 것이라고 생각합니다.


내 개발을 위해 크롬이 훌륭한 솔루션을 가지고 있음을 발견했습니다.

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

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

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


그의 완벽한 솔루션에 대해 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;
            }
        }
    }

}

건배와 감사합니다.


스크립트 노드 (또는 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>

google chrome에는 Hard Reload , Empty Cache 및 Hard Reload 옵션이 있습니다. Reload 버튼 (Inspect Mode)을 길게 클릭하여 하나를 선택할 수 있습니다.


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

<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>

다음 위치에 파일이 있다고 가정합니다.

/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

This means that if you update the version number used the server will treat this as a new resource and not use a cached version. If you base your version number on the Subversion/CVS/etc. revision this means that changes to images referenced in CSS files will be noticed. That isn't guaranteed with the first scheme, i.e. the URL images/happy.gif relative to /styles/screen.css?v=1235 is /styles/images/happy.gif which doesn't contain any version information.

I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".


You could simply add some random number with the CSS/JS url like

example.css?randomNo=Math.random()

For ASP.NET I suppose next solution with advanced options (debug/release mode, versions):

Js or Css files included by such way:

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

Global.JsPostfix and Global.CssPostfix is calculated by the following way in 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();
    }
    ...
}

I recently solved this using Python. Here the code (should be easy to adopt to other languages):

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) 

This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function

script("/main.css")

will result in

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

The advantage of course is that you do never have to change your html again, touching the CSS file will automatically trigger a cache invalidation. Works very good and the overhead is not noticeable.


If you're using git + PHP, you can reload the script from cache each time there is a change in the git repo, using the following code:

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

It seems all answers here suggest some sort of versioning in the naming scheme, which has its downsides.

Browsers should be well aware of what to cache and what not to cache by reading the webservers response, in particular the http headers - for how long is this resource valid ? was this resource updated since I last retrieved it ? etcetera.

If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browsers caches. You can for example configure your web server to tell the browser to never cache files (which is a bad idea).

A more in-depth explanation of how that works is here https://www.mnot.net/cache_docs/#WORK


Simply add this code, where you want to do hard reload (force browser to reload cached CSS/JS files ) Do this inside the .load so it does not refreshes like a loop

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

Just use server side code to add the date of the file... that way it WILL be cached and only reloaded when the file changes

In 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>    

This can be simplified to:

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

By adding an extension method to your project to extend Page:

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;
    }
}

I suggest implementing the following process:

  • version your css/js files whenever you deploy, something like: screen.1233.css (the number can be your SVN revision if you use a versioning system)

  • minify them to optimize loading times


I put an MD5 hash of the file's contents in its URL. That way I can set a very long expiration date, and don't have to worry about users having old JS or CSS.

I also calculate this once per file at runtime (or on file system changes) so there's nothing funny to do at design time or during the build process.

If you're using ASP.NET MVC then you can check out the code in my other answer here.


Sorry for bringing back a dead thread.

@TomA is right.

Using "querystring" method will not be cached as quoted by Steve Souders below:

...that Squid, a popular proxy, doesn’t cache resources with a querystring.

@TomA suggestion of using style.TIMESTAMP.css is good, but MD5 would be much better as only when the contents were genuinely changed, the MD5 changes as well.

참고URL : https://stackoverflow.com/questions/118884/how-to-force-the-browser-to-reload-cached-css-js-files

반응형