web-dev-qa-db-ja.com

ブラウザが「If-None-Match」ヘッダーを送信しないのはなぜですか?

動的にロードされた画像をPHPでダウンロード(できればキャッシュ)しようとしています。送受信されるヘッダーは次のとおりです。

リクエスト:

GET /url:resource/Pomegranate/resources/images/logo.png HTTP/1.1
Host: pome.local
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: PHPSESSID=fb8ghv9ti6v5s3ekkmvtacr9u5

応答:

HTTP/1.1 200 OK
Date: Tue, 09 Apr 2013 11:00:36 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.14 ZendServer/5.0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Disposition: inline; filename="logo"
ETag: "1355829295"
Last-Modified: Tue, 18 Dec 2012 14:44:55 Asia/Tehran
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: image/png

URLをリロードすると、まったく同じヘッダーが送受信されます。私の質問は、結果のリクエストでIf-None-Matchヘッダーを表示するために、応答で何を送信する必要があるかということです。

注:これらのヘッダーは、確かではありませんが、少し前までは問題なく機能していたと思いますが、ブラウザはIf-None-Matchヘッダーを送信しないように変更されていると思います(以前はそのヘッダーを表示していました)。 ChromeとFirefoxでテストしていますが、どちらもヘッダーの送信に失敗します。

15
Mehran

応答ヘッダーにはCache-Control: no-store, no-cacheが含まれます。これらはキャッシュを防ぎます。

これらの値を削除します(must-revalidate, post-check=0, pre-check=0は保持できる/保持する必要があると思います。変更があったかどうかをサーバーに確認するようにブラウザーに指示します)。

そして、私はLast-Modifiedだけに固執します(リソースへの変更がこの基準だけを使用して検出できる場合)– ETagは処理がより複雑です(特にそれを処理したい場合)あなたのPHPスクリプトを自分で)、そしてGoogle PageSpeed/YSlowはこれにも反対するようアドバイスします。

17
CBroe

同じ問題、同様の解決策

私が開発しているサイトにアクセスしたときに、Google Chromeが_If-None-Match_ヘッダーを送信しない理由を特定しようとしています。(Chrome46.0.2490.71 m、私はそうではありませんがバージョンの関連性を確認してください。)

これは、最終的に引用されたOP(Accepted Answerに関するコメント)とは異なりますが、非常に似ていますが、同じ問題に対処します。

ブラウザは、後続のリクエストで「必要なときに」_If-None-Match_ヘッダーを送信しません(つまり、サーバー側ロジックは、PHPなどを介して、ETagまたは最初の応答の_Last-Modified_ヘッダー)。

前提条件

Chromeのロックを赤に変える自己署名TLS証明書を使用すると、Chromeのキャッシュ動作が変わります。この種の問題のトラブルシューティングを試みる前に、 https://stackoverflow.com/a/1910229 で説明されているように、自己署名証明書を有効な信頼されたルートストアにインストールし、ブラウザを完全に再起動します。 。

1番目のエピファニー:If-None-MatchがサーバーからのETagを必要とする場合、最初に

Chrome(およびおそらく他のほとんどまたはすべてのブラウザ)はサーバーまで_If-None-Match_ヘッダーを送信しない)ということにすぐに気付きました。 すでに送信済み前のリクエストへの応答としてETagヘッダー。論理的には、これは完全に理にかなっています。結局のところ、Chrome値が与えられていないときに_If-None-Match_を送信しますか?

これにより、サーバー側のロジック(特に、ユーザーエージェントに応答をキャッシュさせたいときにヘッダーがどのように送信されるか)を調べて、Chromeの非常に応答としてETagヘッダーが送信されない理由を特定しました。リソースの最初のリクエスト。アプリケーションロジックにETagヘッダーを含めるように計算された努力をしました。

私はたまたまPHPを使用しているので、@ Mehran(OP)のコメントが飛び出しました(必要なキャッシュ関連のヘッダーを送信する前にheader_remove()を呼び出すと問題が解決すると彼/彼女は言います)。

率直に言って、私はこの解決策に懐疑的でした。なぜなら、a)PHPはデフォルトで独自のヘッダーを送信しない(そして私の構成では送信しない)と確信していたからです)。 b)PHPでカスタムキャッシュヘッダーを設定する直前にvar_dump(headers_list());を呼び出したとき、ヘッダーセットは、意図的にすぐ上に設定したものだけでした。

_header('Content-type: application/javascript; charset=utf-8');
_

そこで、失うものは何もないので、カスタムヘッダーを送信する直前にheader_remove();を呼び出してみました。そして驚いたことに、PHPは突然ETagヘッダーを送信し始めました!

2番目のエピファニー:応答をgzipすると、ハッシュが変更されます

それから私はレンガの袋のように私を襲った:PHPで_Content-type_ヘッダーを指定することによって、私はNGINX(私が使用しているWebサーバー)に応答を一度GZIPするように伝えていましたPHPそれをNGINXに返します!明確にするために、私が指定していた_Content-type_は、gzipするNGINXのタイプのリストにありました。

完全を期すために、私のNGINX GZIP設定は次のとおりです。PHPはphp-fpmを介してNGINXに接続されています:

_gzip            on;
gzip_min_length 1;
gzip_proxied    expired no-cache no-store private auth;
gzip_types      text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

gzip_vary on;
_

「gzippable」コンテンツタイプが指定されている場合、NGINXがPHPで送信したETagを削除する理由を考え、今や明らかな答えを思いついた:NGINXが応答本文を変更するためthat PHP NGINXがgzipで圧縮すると返されます!これは完全に理にかなっています。生成に使用された応答と一致しない場合はETagを送信しても意味がありません。NGINXが処理するのはかなり滑らかです。このシナリオはとてもインテリジェントです。

NGINXが、非圧縮であるがETagヘッダーを含む応答本文を圧縮しないほど賢いのかどうかはわかりませんが、ここで起こっているようです。

[〜#〜] update [〜#〜]:私は この点に関するNGINXの動作を説明する解説 を見つけました。これは、この主題に関する2つの貴重な議論を引用しています。

  1. 動作について説明しているNGINXフォーラムスレッド
  2. プロジェクトリポジトリでの接線関連のディスカッション ;コメント_Posted on Jun 15, 2013 by Massive Bird_を参照してください。

この貴重な説明を保存するために、万が一それが消えた場合は、_Massive Bird_の議論への貢献から引用します。

Nginxは、応答をその場でgzipするときに、Etagを削除します。非gzip圧縮された応答は、gzip圧縮された応答に匹敵するバイト単位ではないため、これは仕様によるものです。

ただし、この点でのNGINXの動作は、同じ仕様でわずかに欠陥があると見なされる可能性があります

...また、weak Etags(W /で始まるEtag値)と呼ばれるものがあることを示し、応答が意味的に同等であるかどうかを確認するために使用できることを示しています。その場合、Nginxはそれを台無しにすべきではありません。残念ながら、その小切手はソースツリーに組み込まれませんでした[悲しいことに、引用はスパムでいっぱいになりました]。」

この点に関するNGINXの現在の傾向、具体的には、「弱い」Etagのサポートが追加されているかどうかはわかりません。

だから、解決策は何ですか?

では、ETagを応答に戻すための解決策は何ですか? PHPでgzipを実行して、NGINXが応答がすでに圧縮されていることを確認し、ETagヘッダーをそのままにしてそのまま渡します。

_ob_start('ob_gzhandler');
_

ヘッダーと応答本文を送信する前にこの呼び出しを追加すると、PHPは、すべての応答でETag値の送信を開始しました。はい!

学んだ他の教訓

これが私の研究から集められたいくつかの興味深い一口です。この情報は、PHPまたは別の言語であるかどうかにかかわらず、サーバー側のキャッシュ実装をテストしようとするときにかなり便利です。

Chromeとそのデベロッパーツールの[ネット]パネルの動作は異なりますリクエストの開始方法によって

リクエストが「新しくなった」場合、たとえば_Ctrl+F5_を押すと、Chromeは次のヘッダーを送信します:

_Cache-Control: no-cache
Pragma: no-cache
_

サーバーは_200 OK_に応答します。

リクエストが_F5_のみで行われた場合、Chromeは次のヘッダーを送信します:

_Pragma: no-cache
_

サーバーは_304 Not Modified_に応答します。

最後に、既に表示しているページへのリンクをクリックしてリクエストを行った場合、または Chromeのアドレスバーにフォーカスを置いてEnterキーを押すと、Chrome送信これらのヘッダー:

_Cache-Control: no-cache
Pragma: no-cache
_

サーバーは200 OK (from cache)に応答します。

この動作は最初は少し混乱しますが、どのように機能するかわからない場合は、考えられるすべての要求/応答シナリオを非常に徹底的にテストできるため、理想的な動作です。

おそらく最も紛らわしいのは、Chromeが送信リクエストに_Cache-Control: no-cache_ヘッダーと_Pragma: no-cache_ヘッダーを自動的に挿入することです実際の場合 Chromeは、キャッシュから応答を取得しています(200 OK (from cache)応答で証明されています)。

この経験は私にとってかなり有益であり、他の人が将来この価値の分析を見つけてくれることを願っています。

33
Ben Johnson

これは2つの理由で私に起こりました:

  1. サーバーがetag応答ヘッダーを送信しませんでした。以下を追加して、etagを返すようにjettyweb.xmlを更新しました。

    <init-param>
        <param-name>etags</param-name>
        <param-value>true</param-value>
    </init-param>
    
  2. 私が呼び出したURLはxmlファイル用でした。 HTMLファイルに変更すると、chrome "if-none-match"ヘッダーの送信が開始されました!

私はそれが誰かを助けることを願っています

1
Amir Karo

将来の私のためにこれを投稿しています...

同様の問題が発生し、応答でETagを送信していましたが、HTTPクライアントは後続のリクエストでIf-None-Matchヘッダーを送信していませんでした(前日だったので奇妙でした)。

開発にhttp://localhost:9000を使用していたことが判明しました(If-None-Matchは使用しませんでした)-http://127.0.0.1:9000Chromeに切り替えることで1 リクエストでIf-None-Matchヘッダーの送信を自動的に再開しました。

さらに、Devtools > Network > Disable Cache [ ]がオフになっていることを確認します。

Chrome:バージョン71.0.3578.98(公式ビルド)(64ビット)

1 これが文書化されている場所はどこにも見つかりません-Chromeがこのロジックの原因であると思います。

1
Nick Grealy

これは、(グループポリシーを介して)キャッシュサイズを小さすぎるように設定したために発生していました。

シークレットモードでは発生しなかったため、これが当てはまる可能性があることに気づきました。

問題を解決した修正。

0
Mehrdad

同様の問題

適切なEtagヘッダーを指定して、If-None-Matchヘッダーを使用して条件付きGETリクエストを取得しようとしましたが、試したどのブラウザーでも役に立ちませんでした。

多くの試行の後、ブラウザがGETPOSTの両方を同じキャッシュ候補として同じパスに処理することに気付きました。したがって、適切なGETを持つEtagは、Cache-Control:"no-cache, private"によって提供されたとしても、X-Requested-With:"XMLHttpRequest"と同じパスへの即時の「POST」で事実上キャンセルされました。

これが誰かに役立つことを願っています。

0
PF4Public