動的にロードされた画像を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でテストしていますが、どちらもヘッダーの送信に失敗します。
応答ヘッダーにはCache-Control: no-store, no-cache
が含まれます。これらはキャッシュを防ぎます。
これらの値を削除します(must-revalidate, post-check=0, pre-check=0
は保持できる/保持する必要があると思います。変更があったかどうかをサーバーに確認するようにブラウザーに指示します)。
そして、私はLast-Modified
だけに固執します(リソースへの変更がこの基準だけを使用して検出できる場合)– ETag
は処理がより複雑です(特にそれを処理したい場合)あなたのPHPスクリプトを自分で)、そしてGoogle PageSpeed/YSlowはこれにも反対するようアドバイスします。
私が開発しているサイトにアクセスしたときに、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 で説明されているように、自己署名証明書を有効な信頼されたルートストアにインストールし、ブラウザを完全に再起動します。 。
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
ヘッダーを送信し始めました!
それから私はレンガの袋のように私を襲った: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つの貴重な議論を引用しています。
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)
応答で証明されています)。
この経験は私にとってかなり有益であり、他の人が将来この価値の分析を見つけてくれることを願っています。
これは2つの理由で私に起こりました:
サーバーがetag応答ヘッダーを送信しませんでした。以下を追加して、etagを返すようにjettyweb.xmlを更新しました。
<init-param>
<param-name>etags</param-name>
<param-value>true</param-value>
</init-param>
私が呼び出したURLはxmlファイル用でした。 HTMLファイルに変更すると、chrome "if-none-match"ヘッダーの送信が開始されました!
私はそれが誰かを助けることを願っています
将来の私のためにこれを投稿しています...
同様の問題が発生し、応答でETag
を送信していましたが、HTTPクライアントは後続のリクエストでIf-None-Match
ヘッダーを送信していませんでした(前日だったので奇妙でした)。
開発にhttp://localhost:9000
を使用していたことが判明しました(If-None-Match
は使用しませんでした)-http://127.0.0.1:9000
Chromeに切り替えることで1 リクエストでIf-None-Match
ヘッダーの送信を自動的に再開しました。
さらに、Devtools > Network > Disable Cache [ ]
がオフになっていることを確認します。
Chrome:バージョン71.0.3578.98(公式ビルド)(64ビット)
1 これが文書化されている場所はどこにも見つかりません-Chromeがこのロジックの原因であると思います。
これは、(グループポリシーを介して)キャッシュサイズを小さすぎるように設定したために発生していました。
シークレットモードでは発生しなかったため、これが当てはまる可能性があることに気づきました。
問題を解決した修正。
適切なEtag
ヘッダーを指定して、If-None-Match
ヘッダーを使用して条件付きGETリクエストを取得しようとしましたが、試したどのブラウザーでも役に立ちませんでした。
多くの試行の後、ブラウザがGET
とPOST
の両方を同じキャッシュ候補として同じパスに処理することに気付きました。したがって、適切なGET
を持つEtag
は、Cache-Control:"no-cache, private"
によって提供されたとしても、X-Requested-With:"XMLHttpRequest"
と同じパスへの即時の「POST」で事実上キャンセルされました。
これが誰かに役立つことを願っています。