すべてのブラウザーでWebページのキャッシュをどのように制御しますか?


1552

調査の結果、すべてのブラウザーがHTTPキャッシュディレクティブを統一的に尊重しているわけではありません。

セキュリティ上の理由から、我々は我々のアプリケーション内の特定のページには、キャッシュされたくない、これまでの Webブラウザで。これは、少なくとも次のブラウザで機能する必要があります。

  • Internet Explorer 6以降
  • Firefox 1.5以降
  • Safari 3以降
  • Opera 9以降
  • クロム

私たちの要件はセキュリティテストから来ました。ウェブサイトからログアウトした後、戻るボタンを押して、キャッシュされたページを表示できます。


iPad Safariの場合のみ、[これ] [1]は役に立ちますか?[1]:stackoverflow.com/questions/24524248/...
Bakhshi

最も簡単なのは、max-age = 10を使用することです。ページは10秒間キャッシュされるため、これは完璧ではありません。しかし、これは「ヘッダースパゲッティ」ソリューションとしては最も少ないものです。また、リバースプロキシを使用する動的なWebサイトでパフォーマンスが大幅に向上することもあります。(遅いphpスクリプトは10秒に1回呼び出され、リバースプロキシによってキャッシュされます。10秒に1回の方が1人の訪問者に1回よりもはるかに優れています)
Hello World


3
その素晴らしい質問をありがとう。好奇心のために、「セキュリティ上の理由」のために受信者にデータを保存させたくない場合に、データを送信させる状況とはどのようなものか。あなたはすでにそれらを送った!
会計士م

1
@Accountant:彼のシナリオでは、ユーザーはログアウトしていました。そのUser-Agentの次の人間のユーザーがログアウトした人になることを誰が保証できますか?
Fabien Haddadi、

回答:


2579

前書き

上記のすべてのクライアント(およびプロキシ)で機能するヘッダーの正しい最小セット:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

これCache-Controlは、クライアントとプロキシのHTTP 1.1仕様に準拠しています(およびの横にある一部のクライアントでは暗黙的に要求されていますExpires)。これPragmaは、先史時代のクライアントのHTTP 1.0仕様に準拠しています。これExpiresは、クライアントとプロキシのHTTP 1.0および1.1仕様に準拠しています。HTTP 1.1では、Cache-Controlがに優先するExpiresため、結局のところ、HTTP 1.0プロキシのみが対象となります。

のみを使用してHTTPS経由でページを提供するときにIE6とその壊れたキャッシュを気にしないno-store場合は、省略できますCache-Control: no-cache

Cache-Control: no-store, must-revalidate
Pragma: no-cache
Expires: 0

IE6もHTTP 1.0クライアントも気にしない場合(HTTP 1.1は1997年に導入されました)、省略できますPragma

Cache-Control: no-store, must-revalidate
Expires: 0

HTTP 1.0プロキシも気にしない場合は、省略できますExpires

Cache-Control: no-store, must-revalidate

一方、サーバーが有効なDateヘッダーを自動インクルードする場合は、理論的には省略しCache-Controlて、依存Expiresのみにすることもできます。

Date: Wed, 24 Aug 2016 18:32:02 GMT
Expires: 0

ただし、たとえばエンドユーザーがオペレーティングシステムの日付を操作し、クライアントソフトウェアがそれに依存している場合、失敗する可能性があります。

上記のパラメーターが指定されている場合、Cache-Controlなどの他のパラメーターmax-ageは無関係ですCache-ControlLast-Modifiedここで最も他の回答に含まれるヘッダがあるだけで、あなたがあれば面白い実際に欲しいあなたがすべてでそれを指定する必要はありませんので、要求をキャッシュします。

設定方法は?

PHPの使用:

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

JavaサーブレットまたはNode.jsの使用:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setHeader("Expires", "0"); // Proxies.

ASP.NET-MVCの使用

Response.Cache.SetCacheability(HttpCacheability.NoCache);  // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

ASP.NET Web APIの使用:

// `response` is an instance of System.Net.Http.HttpResponseMessage
response.Headers.CacheControl = new CacheControlHeaderValue
{
    NoCache = true,
    NoStore = true,
    MustRevalidate = true
};
response.Headers.Pragma.ParseAdd("no-cache");
// We can't use `response.Content.Headers.Expires` directly
// since it allows only `DateTimeOffset?` values.
response.Content?.Headers.TryAddWithoutValidation("Expires", 0.ToString()); 

ASP.NETの使用:

Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

ASP.NET Core v3の使用

// using Microsoft.Net.Http.Headers
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
Response.Headers[HeaderNames.Expires] = "0";
Response.Headers[HeaderNames.Pragma] = "no-cache";

ASPの使用:

Response.addHeader "Cache-Control", "no-cache, no-store, must-revalidate" ' HTTP 1.1.
Response.addHeader "Pragma", "no-cache" ' HTTP 1.0.
Response.addHeader "Expires", "0" ' Proxies.

Ruby on Rails、またはPython / Flaskの使用:

headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
headers["Pragma"] = "no-cache" # HTTP 1.0.
headers["Expires"] = "0" # Proxies.

Python / Djangoの使用:

response["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response["Pragma"] = "no-cache" # HTTP 1.0.
response["Expires"] = "0" # Proxies.

Python / Pyramidの使用:

request.response.headerlist.extend(
    (
        ('Cache-Control', 'no-cache, no-store, must-revalidate'),
        ('Pragma', 'no-cache'),
        ('Expires', '0')
    )
)

Goの使用:

responseWriter.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
responseWriter.Header().Set("Pragma", "no-cache") // HTTP 1.0.
responseWriter.Header().Set("Expires", "0") // Proxies.

Apache .htaccessファイルの使用:

<IfModule mod_headers.c>
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires 0
</IfModule>

HTML4の使用:

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

HTMLメタタグとHTTP応答ヘッダー

知っておくべき重要なことは、HTMLページがHTTP接続を介して提供され、ヘッダーがHTTP応答ヘッダーとHTML タグの両方に存在する場合、HTTP応答ヘッダーで<meta http-equiv>指定されたものがHTMLメタタグよりも優先されるということです。HTMLメタタグは、ページがローカルディスクファイルシステムからfile://URLを介して表示される場合にのみ使用されます。W3 HTML仕様の章5.2.2も参照してください。プログラムで指定しない場合は、Webサーバーにデフォルト値を含めることができるため、この点に注意してください。

一般に、スターターによる混乱を回避し、ハードHTTP応答ヘッダーに依存するために、HTMLメタタグを指定しない方がよいでしょう。さらに、特にこれらの<meta http-equiv>タグはHTML5では無効です。HTML5仕様にhttp-equivリストされいる値のみが許可されます

実際のHTTP応答ヘッダーの確認

どちらか一方を確認するには、Webブラウザーの開発者ツールセットのHTTPトラフィックモニターでそれらを表示/デバッグできます。Chrome / Firefox23 + / IE9 +でF12を押し、[ネットワーク]または[ネット]タブパネルを開いて、目的のHTTPリクエストをクリックすると、HTTPリクエストとレスポンスに関するすべての詳細が表示されます。以下のスクリーンショットは Chromeからです。

stackoverflow.comにHTTP応答ヘッダーを表示するChrome開発ツールセットHTTPトラフィックモニター

ファイルのダウンロードにもこれらのヘッダーを設定したい

まず、この質問と回答は「ファイルのダウンロード」(PDF、zip、Excelなど)ではなく、「Webページ」(HTMLページ)を対象としています。それらをキャッシュし、URIパスまたはクエリ文字列のどこかにあるファイルバージョン識別子を利用して、変更されたファイルを強制的に再ダウンロードすることをお勧めします。いずれにせよ、これらの非キャッシュヘッダーをファイルダウンロードに適用する場合は、HTTPではなくHTTPSを介してファイルダウンロードを提供するときにIE7 / 8バグに注意してください。詳細については、IEがfoo.jsfをダウンロードできないを参照してくださいIEはこのインターネットサイトを開くことができませんでした。リクエストされたサイトは利用できないか、見つかりません


16
これは完全ではないようです。IE 8でこのソリューションを試したところ、[戻る]ボタンをクリックすると、ブラウザがキャッシュされたバージョンをロードすることがわかりました。
Mike Ottum、2010年

16
テスト方法が間違っていた可能性があります。多分ページはすでにキャッシュにありましたか?ヘッダーが正しくない/上書きされていたのでしょうか?多分あなたは間違った要求を見ていましたか?その他
BalusC、2010年

8
実際、私はこのアプローチが不完全であり、IE8で、または少なくともいくつかの状況で問題を引き起こすことを確認します。具体的には、SSLを介してリソースをフェッチするためにIE8を使用する場合、IE8は2回目のリソースのフェッチを拒否します(使用されるヘッダーに応じて、まったくまたは最初の試行の後)。たとえば、EricLawのブログを参照してください。
ヘイレム

21
これは本質的にバンク・オブ・アメリカがすることであると付け加えたいと思います。それらの応答ヘッダーを見てaspxに変換すると、次のようになります。Response.AppendHeader( "Cache-Control"、 "no-cache、no-store、must-revalidate"); Response.AppendHeader( "Expires"、 "Thu、01 Dec 1994 16:00:00 GMT"); 彼らにとってそれで十分なら、私にとっても十分だと思います。
ジョン

8
@John:その期限切れヘッダーは、まさにHTTP 1.0仕様のサンプル値です。それは機能しますが、そのタイムスタンプを正確に取得するのはやや馬鹿げています。
BalusC 2013

244

(ねえ、みんな:見つけたすべてのヘッダーを不注意にコピーして貼り付けないでください)

まず、[ 戻る]ボタンの履歴はキャッシュではありません

鮮度モデル(セクション4.2)は必ずしも履歴メカニズムに適用されません。つまり、履歴メカニズムは、期限が切れていても以前の表現を表示できます。

以前のHTTP仕様では、この表現はさらに強力で、ブラウザに戻るボタンの履歴のキャッシュディレクティブを無視するように明示的に指示していました。

戻るのは、(ユーザーログインしたときの)時間に戻ることです。以前に開いたURLに移動することはありません。

ただし、実際には、非常に特殊な状況では、キャッシュが[戻る]ボタンに影響を与える可能性があります。

  • ページHTTPS経由で配信する必要あります。そうしないと、このキャッシュ無効化は信頼できません。さらに、HTTPSを使用していない場合、ページは他の多くの方法でログイン盗用に対して脆弱です。
  • 送信する必要がありますCache-Control: no-store, must-revalidate(一部のブラウザは監視しno-store、一部は監視しますmust-revalidate

次のいずれ必要になることありません。

  • <meta>キャッシュヘッダー付き—まったく機能しません。まったく役に立たない。
  • post-check/ pre-checkキャッシュ可能なリソースにのみ適用されるIEのみのディレクティブです。
  • 同じヘッダーを2回または12回に分けて送信する。一部のPHPスニペットでは、実際には以前のヘッダーが置き換えられ、最後のヘッダーのみが送信されます。

必要に応じて、以下を追加できます。

  • no-cacheまたはmax-age=0、これはリソース(URL)を「古く」し、ブラウザに新しいバージョンがあるかどうかをサーバーに確認するよう要求します(no-storeすでにこれはさらに強力であることを意味します)。
  • ExpiresHTTP / 1.0クライアントの過去の日付(実際の HTTP / 1.0のみのクライアントは最近完全に存在しなくなりましたが)。

おまけ:新しいHTTPキャッシングRFC


1
これは、読み込み時間の点で、Webサイトのパフォーマンスに影響を与えますか?no-store、no-cache、must-revalidateはどのようにパフォーマンスに影響しますか?
ラマンガイ2013年

@RamanGhaiキャッシュを無効にすると、一般にパフォーマンスが低下します(前述の3つのオプションすべてがキャッシュを無効にします)。CDNとISPプロキシ(たとえば、モバイルオペレーターによって一般的に使用される)を無効にする可能性があります。(プロキシの問題は別として)新規ユーザーによる最初のロードには影響しませんが、その後のナビゲーションはかなり遅くなる可能性があります。
Kornel 2013年

@porneLあなたは私たちが送信しなければならないことを述べていますCache-Control: must-revalidate。なぜ送信しないCache-Control: no-cacheので、no-cacheすでに暗示must-revalidatew3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1
Pacerier

3
関係@Pacerier no-cachemust-revalidateキャッシュのために真であるが、バック履歴はキャッシュではありません。ブラウザは、must-revalidate履歴の動作を制御するために特別なケースを明示的に示します。
Kornel 2013

@porneL、うーんそれは望ましい振る舞いであると述べているサポートRFCはありますか?
Pacerier 2013

103

@Kornelが述べたように、必要なのはキャッシュを非アクティブ化することではなく、履歴バッファーを非アクティブ化することです。異なるブラウザーには、履歴バッファーを無効にする独自の微妙な方法があります。

Chrome(v28.0.1500.95 m)では、これはによってのみ実行できますCache-Control: no-store

FireFox(v23.0.1)では、次のいずれでも機能します。

  1. Cache-Control: no-store

  2. Cache-Control: no-cache (httpsのみ)

  3. Pragma: no-cache (httpsのみ)

  4. Vary: * (httpsのみ)

Opera(v12.15)では、これはCache-Control: must-revalidate(httpsのみ)でのみ可能です。

Safari(v5.1.7、7534.57.2)では、次のいずれでも機能します。

  1. Cache-Control: no-store
    <body onunload=""> HTMLで

  2. Cache-Control: no-store (httpsのみ)

IE8(v8.0.6001.18702IC)では、次のいずれでも機能します。

  1. Cache-Control: must-revalidate, max-age=0

  2. Cache-Control: no-cache

  3. Cache-Control: no-store

  4. Cache-Control: must-revalidate
    Expires: 0

  5. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT

  6. Pragma: no-cache (httpsのみ)

  7. Vary: * (httpsのみ)

上記を組み合わせると、Chrome 28、FireFox 23、IE8、Safari 5.1.7、およびOpera 12.15で機能するこのソリューションが得られ Cache-Control: no-store, must-revalidateます(httpsのみ)

Operaはプレーンなhttpページの履歴バッファーを非アクティブ化しないため、httpsが必要であることに注意してください。あなたが本当にhttpsを取得できず、Operaを無視する準備ができている場合、あなたができる最善のことはこれです:

Cache-Control: no-store
<body onunload="">

以下は私のテストの生ログを示しています:

HTTP:

  1. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  2. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  3. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    失敗:Safari 5.1.7、Opera 12.15
    成功:Chrome 28、FireFox 23、IE8

  4. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    失敗:Safari 5.1.7、Opera 12.15
    成功:Chrome 28、FireFox 23、IE8

  5. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  6. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  7. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  8. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  9. Cache-Control: no-store
    失敗:Safari 5.1.7、Opera 12.15
    成功:Chrome 28、FireFox 23、IE8

  10. Cache-Control: no-store
    <body onunload="">
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  11. Cache-Control: no-cache
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  12. Vary: *
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15
    成功:なし

  13. Pragma: no-cache
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15
    成功:なし

  14. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  15. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  16. Cache-Control: must-revalidate, max-age=0
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  17. Cache-Control: must-revalidate
    Expires: 0
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  18. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、Opera 12.15
    成功:IE8

  19. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15
    成功:なし

HTTPS:

  1. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15
    成功:なし

  2. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15
    成功:なし

  3. Vary: *
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  4. Pragma: no-cache
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  5. Cache-Control: no-cache
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  6. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  7. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  8. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  9. Cache-Control: must-revalidate
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7
    成功:Opera 12.15

  10. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    <body onunload="">
    失敗:Chrome 28、FireFox 23、IE8、Safari 5.1.7
    成功:Opera 12.15

  11. Cache-Control: must-revalidate, max-age=0
    失敗:Chrome 28、FireFox 23、Safari 5.1.7
    成功:IE8、Opera 12.15

  12. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、Safari 5.1.7
    成功:FireFox 23、IE8、Opera 12.15

  13. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Chrome 28、Safari 5.1.7
    成功:FireFox 23、IE8、Opera 12.15

  14. Cache-Control: no-store
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  15. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  16. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    失敗:Opera 12.15
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7

  17. Cache-Control: private, no-cache
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    失敗:Chrome 28、Safari 5.1.7、Opera 12.15
    成功:FireFox 23、IE8

  18. Cache-Control: must-revalidate
    Expires: 0
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、
    成功:IE8、Opera 12.15

  19. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、
    成功:IE8、Opera 12.15

  20. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、
    成功:IE8、Opera 12.15

  21. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    失敗:Chrome 28、FireFox 23、Safari 5.1.7、
    成功:IE8、Opera 12.15

  22. Cache-Control: private, must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    失敗:Chrome 28、Safari 5.1.7
    成功:FireFox 23、IE8、Opera 12.15

  23. Cache-Control: no-store, must-revalidate
    失敗:なし
    成功:Chrome 28、FireFox 23、IE8、Safari 5.1.7、Opera 12.15


3
これは数年前に投稿されたことは知っていますが、興味深い読み物でした。この問題は、ここ数か月の間私を夢中にさせてきました。身体はキャッシュ制御の扱い方を本当に知っているようです。私は数人の人<body onunload="">がこれを使用しているのを見てきましたが、実際の問題を回避する方法のようです。.htaccessを使用してヘッダーをそのように変更してみましたが、HTTPSを使用している場合は、そのように機能しますか?問題が最も発生するのは主にサファリです。
ジョーダン

4
@ジョーダン、上記のログによると、HTTPSを使用している場合は追加するCache-Control: no-storeとうまくいきます。<body onunload="">HTTPSがない場合にのみ必要です。
Pacerier 2015年

37

私はweb.configルートが便利だと思いました(回答に追加しようとしましたが、受け入れられていないようですので、ここに投稿してください)

<configuration>
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
            <!-- HTTP 1.1. -->
            <add name="Pragma" value="no-cache" />
            <!-- HTTP 1.0. -->
            <add name="Expires" value="0" />
            <!-- Proxies. -->
        </customHeaders>
    </httpProtocol>
</system.webServer>

そして、これは同じことを行うエクスプレス/node.jsの方法です:

app.use(function(req, res, next) {
    res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    next();
});

web.configの場合、動的に/ requirejsを使用して読み込まれることがわかっているスクリプトにのみカスタムヘッダーを適用するように少し変更します。スクリプトがクライアントフォルダーにあると仮定すると、<location path = "client"> ..... </ location>
Ibrahim ben Salah '12

誰が何を疑問に思うかもしれませんweb.conf:これはASP.NETWebアプリケーションのメイン設定と構成ファイルです。これは、ルートディレクトリにあるXMLドキュメントです。(wiki)。
別の

27

このページのすべての回答にまだ問題があることがわかりました。特に、IE8が[戻る]ボタンをクリックしてアクセスしたときに、キャッシュされたバージョンのページの使用を妨げることはないことに気づきました。

多くの調査とテストの結果、私が本当に必要なヘッダーは2つだけであることがわかりました。

Cache-Control:no-store
Vary:*

Varyヘッダーの説明については、http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6をご覧ください。

IE6-8、FF1.5-3.5、Chrome 2-3、Safari 4、およびOpera 9-10では、これらのヘッダーが原因で、ページへのリンクをクリックするか、URLを入力すると、サーバーからページが要求されましたアドレスバーに直接。Jan '10の時点で使用されているすべてのブラウザーの約99%をカバーしています。

IE6、およびOpera 9-10では、戻るボタンを押してもキャッシュされたバージョンがロードされていました。私がテストした他のすべてのブラウザでは、サーバーから新しいバージョンをフェッチしました。これまでのところ、「戻る」ボタンを押したときに、これらのブラウザーがキャッシュされたバージョンのページを返さないヘッダーのセットは見つかりませんでした。

更新: この回答を書いた後、私たちのWebサーバーが自分自身をHTTP 1.0サーバーとして識別していることに気付きました。リストしたヘッダーは、HTTP 1.0サーバーからの応答がブラウザーによってキャッシュされないようにするための正しいヘッダーです。HTTP 1.1サーバーについては、BalusCの回答をご覧ください。


4
これはIE8の戻るボタンで機能します!! 他のすべての提案ですべてを試みた後、「Vary:*」ヘッダーを追加することは、ユーザーが戻るボタンを押したときにIE8にページを強制的にリロードさせる唯一の方法であることは明らかです。そして、これ HTTP / 1.1サーバーで動作します。
coredumperror 2013年

BarlusCによって提案されたヘッダーと、「永続化」属性(Safariに必要)でonPageShowイベントがトリガーされたときにwindow.location.reload()を呼び出すJSスニペットと組み合わせると、テストしたすべてのブラウザーは、ユーザーが[戻る]ボタンを使用したときのサーバー。
coredumperror 2013年

1
@ CoreDumpError、JavaScriptが有効になっていると想定しないでください。
Pacerier 2013

@Pacerier私が2010年に回答を書いたとき、これはSafariとOperaの両方の最新バージョンであり、サーバーがHTTP 1.0サーバーであることを示していました。残念ながら、これを簡単にテストする方法はもうないので、これらのブラウザーの最新バージョンについて決定的なことは言えません。
Chris Vasselli 2013

テストしたブラウザのバージョンは何でしたか?
Pacerier 2013年

21

少し調べた結果、ほとんどのブラウザーをカバーしていると思われる以下のヘッダーのリストが思い付きました。

ASP.NETでは、次のスニペットを使用してこれらを追加しました。

Response.ClearHeaders(); 
Response.AppendHeader("Cache-Control", "no-cache"); //HTTP 1.1
Response.AppendHeader("Cache-Control", "private"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "no-store"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "must-revalidate"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "max-stale=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "post-check=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "pre-check=0"); // HTTP 1.1 
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0 
Response.AppendHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT"); // HTTP 1.0 

発見場所:http : //forums.asp.net/t/1013531.aspx


36
@bart:さらに厄介なのは、1997年の7月26日が月曜日ではなく土曜日だったことです。
CᴏʀʏFeb

5
Cache-Control: no-cacheそしてCache-Control: private衝突-あなたは両方を一緒にしてはいけません:前者はブラウザとプロキシにまったくキャッシュしないように指示し、後者はプロキシにキャッシュしないように指示しますが、ブラウザに独自のプライベートコピーを保持させます。ブラウザーがどの設定に従うかはわかりませんが、ブラウザーとバージョンの間で一貫しているとは考えられません。
キース

事前チェックと事後チェックは使用しないでください。blogs.msdn.com/b/ieinternals/archive/2009/07/20/...
EricLaw

これは私にはうまくいきませんでした-asp.net 4.5を使用すると、コードは実行されますが、必要な結果が生成されません。:私はこれに従う必要がありましたstackoverflow.com/questions/22443932/...
アンディ

8

応答でのプラグマヘッダーの使用は、妻の話です。RFC2616は要求ヘッダーとしてのみ定義しています

http://www.mnot.net/cache_docs/#PRAGMA


4
これは、仕様を超える必要がある理由の良い例です。仕様が常に明確である場合、StackOverflowのようなサイトにはあまり意味がありません。Microsoft からHTTP 1.0サーバーとの下位互換性のために、Internet ExplorerはHTTPプラグマの特別な使用法をサポートしています:no-cacheヘッダー。クライアントが安全な接続(https://)を介してサーバーと通信し、サーバーが応答と共にPragma:no-cacheヘッダーを返す場合、Internet Explorerは応答をキャッシュしません。
マイケルク

@michaelok:参照は有効ですが、大きな点を見逃しています-適切なCache-Control / Expiresを設定すれば、プラグマは必要ありません。
EricLaw 2015

8

免責事項:@BalusCの回答を読むことを強くお勧めします。次のキャッシングチュートリアルを読んだ後:http : //www.mnot.net/cache_docs/(私もお読みになることをお勧めします)、正しいと思います。ただし、歴史的な理由から(そして私が自分でテストしたため)、元の回答を以下に含めます。


私はPHPで「受け入れられた」答えを試しましたが、うまくいきませんでした。次に、少し調査を行い、わずかな変種を見つけてテストしましたが、うまくいきました。ここにあります:

header('Cache-Control: no-store, private, no-cache, must-revalidate');     // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0, max-stale = 0', false);  // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');                  // Date in the past  
header('Expires: 0', false); 
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header ('Pragma: no-cache');

うまくいくはずです。問題は、ヘッダーの同じ部分を2回設定するときfalseに、ヘッダー関数の2番目の引数としてが送信されない場合、ヘッダー関数が単に前のheader()呼び出しを上書きすることでした。したがって、を設定するCache-Control場合、たとえば、1つのheader()関数呼び出しにすべての引数を入れたくない場合は、次のようにする必要があります。

header('Cache-Control: this');
header('Cache-Control: and, this', false);

ここでより完全なドキュメントを参照してください。


14
これは神話でいっぱいです。事前チェックと事後チェックはIEのみで、キャッシュされた応答にのみ関連し、値0は何もしません。max-staleはプロキシー要求ヘッダーであり、サーバー応答ヘッダーではありません。Expiresは単一の値のみを受け入れます。複数あると、このヘッダーは無視されます。
Kornel

1
@porneL、あなたはこれらの神話を正しく扱う競合する回答を提出しますか?
奇妙なことに、2008年

@ Oddthinking、stackoverflow.com / questions / 49547 /…のように見えるのは競合する答えです。
Mike Ottum、2014年

@Pacerierはい、免責事項で述べたように、BalusCの回答を使用します。
Steven Oxley、2013

8

ASP.NET Coreの場合、単純なミドルウェアクラスを作成します。

public class NoCacheMiddleware
{
    private readonly RequestDelegate m_next;

    public NoCacheMiddleware( RequestDelegate next )
    {
        m_next = next;
    }

    public async Task Invoke( HttpContext httpContext )
    {
        httpContext.Response.OnStarting( ( state ) =>
        {
            // ref: http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers
            httpContext.Response.Headers.Append( "Cache-Control", "no-cache, no-store, must-revalidate" );
            httpContext.Response.Headers.Append( "Pragma", "no-cache" );
            httpContext.Response.Headers.Append( "Expires", "0" );
            return Task.FromResult( 0 );
        }, null );

        await m_next.Invoke( httpContext );
    }
}

次にそれを登録します Startup.cs

app.UseMiddleware<NoCacheMiddleware>();

これを後でどこかに追加してください

app.UseStaticFiles();

文字列リテラル「Cache-Controls」、「Pragma」、「Expires」の代わりに、Microsoft.Net.Http.Headers.HeaderNamesの定数を使用することをお勧めします。
Victor Sharovatov

7

これらのディレクティブは、セキュリティリスクを軽減しません。それらは本当にUAが揮発性情報を更新するように強制することを意図しており、UAが情報を保持しないようにすることを意図していません。この同様の質問を参照してください。少なくとも、ルーターやプロキシなどがキャッシュディレクティブも無視しないという保証はありません。

さらに良いことに、コンピュータへの物理的なアクセス、ソフトウェアのインストールなどに関するポリシーは、セキュリティの面でほとんどの企業よりもはるかに優れています。この情報の消費者が公衆のメンバーである場合、あなたが本当にできる唯一のことは、情報がマシンに到達したら、そのマシンは自分の責任ではなく自分の責任であることを理解してもらうことです。


7

IE6にバグがあります

「Cache-Control:no-cache」を使用しても、「Content-Encoding:gzip」のコンテンツは常にキャッシュされます。

http://support.microsoft.com/kb/321722

IE6ユーザーのgzip圧縮を無効にすることができます(「MSIE 6」のユーザーエージェントを確認してください)


6

HTTP 1.1のRFC では、適切な方法は次のHTTPヘッダーを追加することです。

キャッシュ制御:キャッシュなし

古いブラウザは、HTTP 1.1に適切に準拠していない場合、これを無視することがあります。それらのためにあなたはヘッダを試すことができます:

プラグマ:キャッシュなし

これは、HTTP 1.1ブラウザーでも機能するはずです。


1
仕様は、再検証なしで応答を再利用してはならないことを示しています。そもそもレスポンスがキャッシュに格納されていないことを示す公式の方法であるのは、Cache-Control:no-storeです。
AnthonyWJones 2008

6

変更されたhttpヘッダーを1995年の日付に設定すると、通常はうまくいきます。

次に例を示します。

有効期限:1995年11月15日水曜日04:58:08 GMT
最終変更日:水、1995年11月15日04:58:08 GMT
キャッシュ制御:キャッシュなし、再検証が必要

1
以前のLast-Modifiedを設定しても、ヒューリスティックな再検証のためにキャッシュされた応答をより長く使用できるようにすることを除いて、キャッシュに影響はありません。
EricLaw 2015

6

ヘッダー関数PHPドキュメントには、かなり完全な例があります(サードパーティによって寄稿)。

    header('Pragma: public');
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");                  // Date in the past   
    header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
    header('Cache-Control: no-store, no-cache, must-revalidate');     // HTTP/1.1
    header('Cache-Control: pre-check=0, post-check=0, max-age=0', false);    // HTTP/1.1
    header ("Pragma: no-cache");
    header("Expires: 0", false);

11
これは明らかに間違っています。Expires、Cache-control、およびPragmaのheader()への2回目の呼び出しは、以前に設定された値を完全に上書きします。
Kornel

1
@porneL:いいえ、以前の値を上書きしないように2番目のパラメーターとしてfalseを渡すため、以前に設定した値を上書きしません。
ジュリアンパラード2013

1
@JulienPalard私がコメントした後、回答が編集されました。それはまだあまり意味がありません。
Kornel 2013

9より前のIEで作業する場合は、複数のCache-Controlヘッダーを送信しないでください。事前チェックまたは事後チェックを送信しないでください。blogs.msdn.com/b/ieinternals/archive/2009/07/20/...
EricLaw

6

SSLを介したIE6-IE8およびMS Officeファイルでのcache:no-cacheヘッダー(および同様の値)のダウンロードの問題に直面している場合は、cache:private、no-storeヘッダーを使用して、POSTリクエストでファイルを返すことができます。できます。


6

私の場合、これでクロムの問題を修正します

<form id="form1" runat="server" autocomplete="off">

セキュリティ上の理由でユーザーがボタンをクリックして戻るときに、以前のフォームデータのコンテンツをクリアする必要がある場合


Mozilla 19.xブラウザーの問題もコードスニペットで解決されました。autocomplete = "off"。ありがとうございました。
サティア

5

受け入れられた回答は、IIS7 +では機能していないようで、II7で送信されないキャッシュヘッダーに関する多数の質問が原因です。

等々

受け入れられた答えは、どのヘッダーを設定する必要があるかでは正しいが、どのように設定する必要があるかではありません。この方法はIIS7で機能します。

Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "-1");

1行目はに設定さCache-controlno-cache、2行目は他の属性を追加しますno-store, must-revalidate


これは私にはResponse.Cache.SetAllowResponseInBrowserHistory(false); Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
有効

4

Pragma:no-cacheを設定することにより、すべてのブラウザーで最良かつ最も一貫した結果が得られました


4

BalusCが提供する回答のヘッダーは、ブラウザーの戻るボタンを使用したときに、Safari 5(および場合によっては古いバージョンも)がブラウザーのキャッシュからコンテンツを表示することを妨げません。これを防ぐ方法は、本文タグに空のonunloadイベントハンドラー属性を追加することです。

<body onunload=""> 

このハック、Safariのバックフォワードキャッシュを破壊するようです。[戻る]ボタンをクリックしたときに、ブラウザ間のonloadイベントはありますか?


クール、私はそれをテストしました、そしてこれは実際にはSafari(5.1.7)で動作しますがOperaでは動作しません。
Pacerier 2013

4

また、キャッシュを有効にするためにそれを使用している場合はExpiresDefault、念のため、.htaccessファイル内のを必ずリセットしてください。

ExpiresDefault "access plus 0 seconds"

その後、を使用ExpiresByTypeして、キャッシュするファイルに特定の値を設定できます。

ExpiresByType image/x-icon "access plus 3 month"

これは、phpなどの動的ファイルがブラウザによってキャッシュされている場合にも役立ち、その理由を理解できません。確認してくださいExpiresDefault


3

ヘッダーに加えて、httpsを介してページを提供することを検討してください。多くのブラウザはデフォルトでhttpsをキャッシュしません。


3
//In .net MVC
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult FareListInfo(long id)
{
}

// In .net webform
<%@ OutputCache NoStore="true" Duration="0" VaryByParam="*" %>

2

完全にBalusC - > ANSWER あなたはPerlを使用している場合は、HTTPヘッダを追加するCGIを使用することができます。

Perlの使用:

Use CGI;    
sub set_new_query() {
        binmode STDOUT, ":utf8";
        die if defined $query;
        $query = CGI->new();
        print $query->header(
                        -expires       => 'Sat, 26 Jul 1997 05:00:00 GMT',
                        -Pragma        => 'no-cache',
                        -Cache_Control => join(', ', qw(
                                            private
                                            no-cache
                                            no-store
                                            must-revalidate
                                            max-age=0
                                            pre-check=0
                                            post-check=0 
                                           ))
        );
    }

Apache httpd.confを使用する

<FilesMatch "\.(html|htm|js|css|pl)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>

注: HTML METAを使用しようとすると、ブラウザはそれらを無視してページをキャッシュしました。


0

誰かが動的コンテンツのみをキャッシュしないようにしたい場合は、それらの追加ヘッダーをプログラムで追加する必要があることを指摘しておきます。

プロジェクトの構成ファイルを編集してno-cacheヘッダーを追加しましたが、静的コンテンツのキャッシュも無効になりましたが、これは通常は望ましくありません。コード内の応答ヘッダーを変更すると、画像とスタイルファイルが確実にキャッシュされます。

これは非常に明白ですが、言及する価値があります。

そして別の注意。HttpResponseクラスのClearHeadersメソッドの使用には注意してください。無謀に使用すると、あざができることがあります。それが私に与えたように。

ActionFilterAttributeイベントでリダイレクトした後、すべてのヘッダーをクリアすると、すべてのセッションデータとTempDataストレージのデータが失われます。リダイレクトが行われているときにアクションからリダイレクトするか、ヘッダーをクリアしない方が安全です。

ふたたび考えて、ClearHeadersメソッドを使用することはすべてお勧めしません。ヘッダーは個別に削除することをお勧めします。そして、Cache-Controlヘッダーを適切に設定するには、次のコードを使用します。

filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-store, must-revalidate");

0

私は<head><meta>要素に運がありませんでした。HTTPキャッシュ関連のパラメーターを直接(HTMLドキュメントの外で)追加すると、実際に機能します。

web.py web.header呼び出しを使用したPythonのサンプルコードを次に示します。私は意図的に自分の無関係なユーティリティコードを編集しました。

    ウェブをインポート
    インポートシステム
    PERSONAL-UTILITIESのインポート

    myname = "main.py"

    URL =(
        '/'、 'main_class'
    )

    main = web.application(urls、globals())

    render = web.template.render( "templates /"、base = "layout"、cache = False)

    クラスmain_class(オブジェクト):
        def GET(self):
            web.header( "Cache-control"、 "no-cache、no-store、must-revalidate")
            web.header( "プラグマ"、 "キャッシュなし")
            web.header( "有効期限"、 "0")
            render.main_form()を返します

        def POST(self):
            msg = "投稿済み:"
            フォーム= web.input(関数=なし)
            web.header( "Cache-control"、 "no-cache、no-store、must-revalidate")
            web.header( "プラグマ"、 "キャッシュなし")
            web.header( "有効期限"、 "0")
            render.index_laid_out(greeting = msg + form.function)を返します

    __name__ == "__main__"の場合:
        nargs = len(sys.argv)
        #Pythonプログラム名の後に十分な引数があることを確認します
        nargs!= 2の場合:
            LOG-AND-DIE( "%s:コマンドラインエラー、nargs =%s、2"でなければならない、myname、nargs)
        #TCPポート番号が数値であることを確認します
        試してください:
            tcp_port = int(sys.argv [1])
        eとしての例外を除く:
            LOG-AND-DIE( "%s:tcp_port = int(%s)failed(integerではない)"、myname、sys.argv [1])
        # すべては順調です!
        JUST-LOG( "%s:ポート%dで実行中"、myname、tcp_port)
        web.httpserver.runsimple(main.wsgifunc()、( "localhost"、tcp_port))
        main.run()


これは、何年も前からサイトに掲載されてきた回答で、すでに何度も取り上げられていませんか?
Martin Tournoij 16

METAディレクティブは、Internet ExplorerおよびEdge 18以前のバージョンで機能します。最新のブラウザはそれらをサポートしていません。crbug.com/2763
EricLaw

0

キャッシングに関するケーススタディへのリンクを参照してください。

http://securityevaluators.com/knowledge/case_studies/caching/

要約によると、記事によると、Cache-Control: no-storeChrome、Firefox、IE でのみ機能します。IEは他のコントロールを受け入れますが、ChromeとFirefoxは受け入れません。リンクは、キャッシングとドキュメント化の概念実証の履歴が揃った読み物です。


0

私の答えがシンプルで愚かに聞こえるかどうかはわかりませんが、おそらくそれはずっと前からあなたに知られているかもしれませんが、誰かがブラウザの戻るボタンを使用して履歴ページを表示するのを防ぐことが目標の1つであるため、以下を使用できます。

window.location.replace("https://www.example.com/page-not-to-be-viewed-in-browser-history-back-button.html");

もちろん、これをサイト全体に実装することは不可能かもしれませんが、少なくともいくつかの重要なページでは、それを行うことができます。お役に立てれば。


-1

IISでアプリ全体をキャッシュする代わりに、ロケーションブロックを使用して個々のファイルを設定できます

 <location path="index.html">
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Cache-Control" value="no-cache" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.