Memahami The Vary Header
Diterbitkan: 2022-03-10Header HTTP Vary dikirim dalam miliaran respons HTTP setiap hari. Tetapi penggunaannya tidak pernah memenuhi visi aslinya, dan banyak pengembang salah paham tentang apa yang dilakukannya atau bahkan tidak menyadari bahwa server web mereka yang mengirimkannya. Dengan datangnya Petunjuk Klien, Varian, dan spesifikasi Kunci, respons yang bervariasi mendapatkan awal yang baru.
Apa itu Bervariasi?
Kisah Vary dimulai dengan ide bagus tentang bagaimana web seharusnya bekerja. Pada prinsipnya, URL bukan mewakili halaman web, tetapi sumber daya konseptual, seperti laporan mutasi bank Anda. Bayangkan Anda ingin melihat laporan mutasi bank Anda: Anda pergi ke bank.com
dan mengirim permintaan GET
untuk /statement
. Sejauh ini bagus, tetapi Anda tidak mengatakan format apa yang Anda inginkan untuk pernyataan tersebut. Inilah sebabnya mengapa browser Anda juga akan menyertakan sesuatu seperti Accept: text/html
dalam permintaan Anda. Secara teori, setidaknya, ini berarti Anda bisa mengatakan Accept: text/csv
sebagai gantinya dan dapatkan sumber daya yang sama dalam format yang berbeda.
Karena URL yang sama sekarang menghasilkan respons yang berbeda berdasarkan nilai header Accept
, cache apa pun yang menyimpan respons ini perlu mengetahui bahwa header itu penting. Server memberi tahu kami bahwa tajuk Accept
penting seperti ini:
Vary: Accept
Anda dapat membaca ini sebagai, "Tanggapan ini bervariasi berdasarkan nilai header Accept
permintaan Anda."
Ini pada dasarnya tidak berfungsi di web hari ini. Apa yang disebut "negosiasi konten" adalah ide bagus, tetapi gagal. Ini tidak berarti bahwa Vary
tidak berguna. Sebagian halaman yang Anda kunjungi di web memiliki tajuk Vary
sebagai tanggapan — mungkin situs web Anda juga memilikinya, dan Anda tidak mengetahuinya. Jadi, jika header tidak berfungsi untuk negosiasi konten, mengapa masih begitu populer, dan bagaimana browser menanganinya? Mari lihat.
Saya sebelumnya telah menulis tentang Vary sehubungan dengan jaringan pengiriman konten (CDN), cache perantara tersebut (seperti Fastly, CloudFront, dan Akamai) yang dapat Anda tempatkan di antara server Anda dan pengguna. Peramban juga perlu memahami dan menanggapi aturan Vary, dan cara mereka melakukannya berbeda dengan cara Vary diperlakukan oleh CDN. Dalam posting ini, saya akan menjelajahi dunia keruh variasi cache di browser.
Kasus Penggunaan Hari Ini Untuk Memvariasikan Dalam Peramban
Seperti yang kita lihat sebelumnya, penggunaan tradisional Vary adalah untuk melakukan negosiasi konten menggunakan header Accept
, Accept-Language
dan Accept-Encoding
, dan, secara historis, dua yang pertama telah gagal total. Memvariasikan pada Accept-Encoding
untuk memberikan respons terkompresi Gzip- atau Brotli, jika didukung, sebagian besar bekerja dengan cukup baik, tetapi semua browser mendukung Gzip akhir-akhir ini, jadi itu tidak terlalu menarik.
Bagaimana dengan beberapa skenario ini?
- Kami ingin menyajikan gambar yang lebarnya sama persis dengan layar pengguna. Jika pengguna mengubah ukuran browser mereka, kami akan mengunduh gambar baru (bervariasi pada Petunjuk Klien).
- Jika pengguna logout, kami ingin menghindari penggunaan halaman apa pun yang di-cache saat mereka login (menggunakan cookie sebagai
Key
). - Pengguna browser yang mendukung format gambar WebP harus mendapatkan gambar WebP; jika tidak, mereka harus mendapatkan JPEG.
- Saat menggunakan browser di layar berkepadatan tinggi, pengguna harus mendapatkan gambar 2x. Jika mereka memindahkan jendela browser ke layar kepadatan standar dan menyegarkan, mereka akan mendapatkan gambar 1x.
Cache Sepanjang Jalan
Tidak seperti cache tepi, yang bertindak sebagai satu cache raksasa yang dibagikan oleh semua pengguna, browser hanya untuk satu pengguna, tetapi memiliki banyak cache yang berbeda untuk penggunaan yang berbeda dan spesifik:
Beberapa di antaranya cukup baru, dan memahami dengan tepat dari cache mana konten sedang dimuat adalah perhitungan rumit yang tidak didukung dengan baik oleh alat pengembang. Inilah yang dilakukan cache ini:
- cache gambar
Ini adalah cache lingkup halaman yang menyimpan data gambar yang didekodekan, sehingga, misalnya, jika Anda menyertakan gambar yang sama pada halaman beberapa kali, browser hanya perlu mengunduh dan mendekodekannya sekali. - pramuat cache
Ini juga memiliki cakupan halaman dan menyimpan apa pun yang telah dimuat sebelumnya di headerLink
atau<link rel="preload">
, meskipun sumber daya biasanya tidak dapat di-cache. Seperti cache gambar, cache pramuat dihancurkan saat pengguna keluar dari halaman. - API cache pekerja layanan
Ini menyediakan back-end cache dengan antarmuka yang dapat diprogram; jadi, tidak ada yang disimpan di sini kecuali Anda secara khusus meletakkannya di sana melalui kode JavaScript di pekerja layanan. Ini juga hanya akan diperiksa jika Anda melakukannya secara eksplisit di penanganfetch
pekerja layanan. Cache pekerja layanan memiliki cakupan asal, dan, meskipun tidak dijamin persisten, cache ini lebih persisten daripada cache HTTP browser. - Tembolok HTTP
Ini adalah cache utama yang paling dikenal orang. Ini adalah satu-satunya cache yang memperhatikan header cache tingkat HTTP sepertiCache-Control
, dan ini menggabungkannya dengan aturan heuristik browser sendiri untuk menentukan apakah akan men-cache sesuatu dan untuk berapa lama. Ini memiliki cakupan terluas, dibagikan oleh semua situs web; jadi, jika dua situs web yang tidak terkait memuat aset yang sama (misalnya, Google Analytics), mereka mungkin berbagi klik cache yang sama. - Tembolok dorong HTTP/2 (atau “Tembolok dorong H2”)
Ini duduk dengan koneksi, dan menyimpan objek yang telah didorong dari server tetapi belum diminta oleh halaman mana pun yang menggunakan koneksi. Itu dicakup ke halaman menggunakan koneksi tertentu, yang pada dasarnya sama dengan cakupan ke satu Origin, tetapi juga dihancurkan ketika koneksi ditutup.
Dari jumlah tersebut, cache HTTP dan cache pekerja layanan paling baik ditentukan. Mengenai cache gambar dan pramuat, beberapa browser mungkin menerapkannya sebagai "cache memori" tunggal yang terkait dengan render navigasi tertentu, tetapi model mental yang saya jelaskan di sini masih merupakan cara yang tepat untuk memikirkan prosesnya. Lihat catatan spesifikasi pada preload
jika Anda tertarik. Dalam kasus push server H2, diskusi tentang nasib cache ini tetap aktif.
Urutan di mana permintaan memeriksa cache ini sebelum menjelajah ke jaringan adalah penting, karena meminta sesuatu mungkin menariknya dari lapisan luar cache ke lapisan dalam. Misalnya, jika server HTTP/2 Anda mendorong lembar gaya bersama dengan halaman yang membutuhkannya, dan halaman itu juga memuat lembar gaya sebelumnya dengan <link rel="preload">
, maka lembar gaya akan menyentuh tiga cache di browser. Pertama, itu akan duduk di cache push H2, menunggu untuk diminta. Saat browser merender halaman dan membuka tag preload
, browser akan mengeluarkan style sheet dari cache push, melalui cache HTTP (yang mungkin menyimpannya, tergantung pada header Cache-Control
style sheet), dan akan menyimpan itu di cache pramuat.
Memperkenalkan Vary Sebagai Validator
Oke, jadi apa yang terjadi ketika kita mengambil situasi ini dan menambahkan Vary ke dalam campuran?
Tidak seperti cache perantara (seperti CDN), browser biasanya tidak menerapkan kemampuan untuk menyimpan beberapa variasi per URL . Alasan untuk ini adalah bahwa hal-hal yang biasanya kami gunakan untuk Vary
(terutama Accept-Encoding
dan Accept-Language
) tidak sering berubah dalam konteks satu pengguna. Accept-Encoding
mungkin (tetapi mungkin tidak) berubah saat browser diupgrade, dan Accept-Language
kemungkinan besar hanya akan berubah jika Anda mengedit pengaturan lokal bahasa sistem operasi Anda. Ini juga menjadi jauh lebih mudah untuk menerapkan Vary dengan cara ini, meskipun beberapa penulis spesifikasi percaya ini adalah kesalahan.
Tidak ada ruginya sebagian besar waktu bagi browser untuk menyimpan hanya satu variasi, tetapi penting agar kita tidak secara tidak sengaja menggunakan variasi yang tidak valid lagi jika data "bervariasi" memang berubah.
Komprominya adalah memperlakukan Vary
sebagai validator, bukan kunci. Peramban menghitung kunci cache dengan cara biasa (pada dasarnya, menggunakan URL), dan kemudian jika berhasil, mereka memeriksa apakah permintaan tersebut memenuhi aturan Vary yang dimasukkan ke dalam respons yang di-cache. Jika tidak, maka browser akan memperlakukan permintaan tersebut sebagai cache yang terlewat, dan permintaan akan berpindah ke lapisan cache berikutnya atau keluar ke jaringan. Ketika respons baru diterima, itu akan menimpa versi yang di-cache, meskipun secara teknis variasinya berbeda.
Mendemonstrasikan Perilaku yang Bervariasi
Untuk mendemonstrasikan cara Vary
ditangani, saya telah membuat sedikit test suite. Tes memuat berbagai URL yang berbeda, bervariasi pada header yang berbeda, dan mendeteksi apakah permintaan telah mencapai cache atau tidak. Saya awalnya menggunakan ResourceTiming untuk ini, tetapi untuk kompatibilitas yang lebih besar, saya akhirnya beralih ke hanya mengukur berapa lama permintaan selesai (dan sengaja menambahkan penundaan 1 detik ke respons sisi server untuk membuat perbedaannya sangat jelas).
Mari kita lihat masing-masing jenis cache dan bagaimana Vary
harus bekerja dan apakah itu benar-benar berfungsi seperti itu. Untuk setiap pengujian, saya tunjukkan di sini apakah kita harus mengharapkan untuk melihat hasil dari cache ("HIT" versus "LEWATKAN") dan apa yang sebenarnya terjadi.
Pramuat
Pramuat saat ini hanya didukung di Chrome, di mana respons yang dimuat sebelumnya disimpan dalam cache memori hingga dibutuhkan oleh halaman. Respons juga mengisi cache HTTP dalam perjalanannya ke cache pramuat, jika dapat di-cache-HTTP. Karena menentukan header permintaan dengan pramuat tidak mungkin, dan cache pramuat hanya berlangsung selama halaman, pengujian ini sulit, tetapi kita setidaknya dapat melihat bahwa objek dengan header Vary
berhasil dimuat sebelumnya:
API Cache Pekerja Layanan
Pekerja layanan dukungan Chrome dan Firefox, dan dalam mengembangkan spesifikasi pekerja layanan, penulis ingin memperbaiki apa yang mereka lihat sebagai implementasi yang rusak di browser, untuk membuat Vary
di browser berfungsi lebih seperti CDN. Ini berarti bahwa meskipun browser seharusnya hanya menyimpan satu variasi dalam cache HTTP, browser seharusnya menyimpan beberapa variasi dalam Cache API. Firefox (54) melakukan ini dengan benar, sedangkan Chrome menggunakan logika variabel-sebagai-validator yang sama dengan yang digunakan untuk cache HTTP (bug sedang dilacak).
Tembolok HTTP
Cache HTTP utama harus mengamati Vary
dan melakukannya secara konsisten (sebagai validator) di semua browser. Untuk lebih banyak lagi tentang ini, lihat posting Mark Nottingham "State of Browser Caching, Revisited."
Tembolok Dorong HTTP/2
Vary
harus diperhatikan, tetapi dalam praktiknya tidak ada browser yang benar-benar menghormatinya, dan browser akan dengan senang hati mencocokkan dan menggunakan respons yang didorong dengan permintaan yang membawa nilai acak di header tempat responsnya bervariasi.
Kerut “304 (Tidak Dimodifikasi)”
Status respons HTTP "304 (Tidak Dimodifikasi)" sangat menarik. “Pemimpin kami yang terhormat”, Artur Bergman, menunjukkan kepada saya permata ini dalam spesifikasi caching HTTP (penekanan milik saya):
Server yang menghasilkan respons 304 harus menghasilkan salah satu bidang header berikut yang akan dikirim dalam respons 200 (OK) untuk permintaan yang sama:
Cache-Control
,Content-Location
,Date
,ETag
,Expires
, danVary
.
Mengapa respons 304
mengembalikan header Vary
? Plotnya mengental ketika Anda membaca tentang apa yang seharusnya Anda lakukan setelah menerima 304
respons yang berisi tajuk tersebut:
Jika respons tersimpan dipilih untuk pembaruan, cache harus \[…] menggunakan bidang header lain yang disediakan dalam respons 304 (Tidak Dimodifikasi) untuk mengganti semua instance bidang header terkait dalam respons tersimpan.
Tunggu apa? Jadi, jika header Vary
304
berbeda dari yang ada di objek cache yang ada, kita harus memperbarui objek yang di-cache? Tapi itu mungkin berarti itu tidak lagi sesuai dengan permintaan yang kami buat!
Dalam skenario itu, pada pandangan pertama, 304
tampaknya memberi tahu Anda secara bersamaan bahwa Anda dapat dan tidak dapat menggunakan versi yang di-cache. Tentu saja, jika server benar-benar tidak ingin Anda menggunakan versi cache, itu akan mengirim 200
, bukan 304
; jadi, versi yang di-cache pasti harus digunakan — tetapi setelah menerapkan pembaruan padanya, itu mungkin tidak digunakan lagi untuk permintaan di masa mendatang yang identik dengan yang benar-benar mengisi cache di tempat pertama.
(Catatan tambahan: Di Fastly, kami tidak menghormati kekhasan spesifikasi ini. Jadi, jika kami menerima 304
dari server asal Anda, kami akan terus menggunakan objek cache yang tidak dimodifikasi, selain mengatur ulang TTL.)
Browser tampaknya menghargai ini, tetapi dengan kekhasan. Mereka memperbarui tidak hanya tajuk respons tetapi tajuk permintaan yang berpasangan dengan mereka, untuk menjamin bahwa, pasca-pembaruan, respons yang di-cache cocok dengan permintaan saat ini. Ini sepertinya masuk akal. Spesifikasinya tidak menyebutkan hal ini, jadi vendor browser bebas melakukan apa yang mereka suka; untungnya, semua browser menunjukkan perilaku yang sama.
Petunjuk Klien
Fitur Client Hints Google adalah salah satu hal baru yang paling signifikan terjadi pada Vary di browser dalam waktu yang lama. Tidak seperti Accept-Encoding
dan Accept-Language
, Client Hints menjelaskan nilai-nilai yang mungkin berubah secara teratur saat pengguna bergerak di sekitar situs web Anda, khususnya yang berikut:
-
DPR
Rasio piksel perangkat, kerapatan piksel layar (dapat bervariasi jika pengguna memiliki beberapa layar) -
Save-Data
Apakah pengguna telah mengaktifkan mode hemat data -
Viewport-Width
Lebar piksel dari viewport saat ini -
Width
Lebar sumber daya yang diinginkan dalam piksel fisik
Nilai-nilai ini tidak hanya dapat berubah untuk satu pengguna, tetapi rentang nilai untuk yang terkait lebar juga besar. Jadi, kami benar-benar dapat menggunakan Vary
dengan header ini, tetapi kami berisiko mengurangi efisiensi cache kami atau bahkan membuat caching tidak efektif.
Proposal Header Kunci
Petunjuk Klien dan tajuk yang sangat terperinci lainnya cocok untuk proposal yang sedang dikerjakan Mark, bernama Key. Mari kita lihat beberapa contoh:
Key: Viewport-Width;div=50
Ini mengatakan bahwa responsnya bervariasi berdasarkan nilai header permintaan Viewport-Width
Pandang, tetapi dibulatkan ke bawah ke kelipatan 50 piksel terdekat!
Key: cookie;param=sessionAuth;param=flags
Menambahkan header ini ke dalam respons berarti kita memvariasikan dua cookie tertentu: sessionAuth
dan flags
. Jika belum berubah, kami dapat menggunakan kembali respons ini untuk permintaan di masa mendatang.
Jadi, perbedaan utama antara Key
dan Vary
adalah:
-
Key
memungkinkan memvariasikan subbidang dalam header, yang tiba-tiba memungkinkan untuk memvariasikan cookie, karena Anda dapat memvariasikan hanya pada satu cookie — ini akan sangat besar; - nilai individu dapat dimasukkan ke dalam rentang , untuk meningkatkan kemungkinan cache hit, sangat berguna untuk memvariasikan hal-hal seperti lebar viewport.
- semua variasi dengan URL yang sama harus memiliki kunci yang sama. Jadi, jika cache menerima respons baru untuk URL yang beberapa variannya sudah ada, dan nilai header
Key
respons baru tidak cocok dengan nilai pada varian yang ada, maka semua varian harus dikeluarkan dari cache.
Pada saat penulisan, tidak ada browser atau CDN yang mendukung Key
, meskipun di beberapa CDN Anda mungkin bisa mendapatkan efek yang sama dengan membagi header yang masuk menjadi beberapa header pribadi dan memvariasikannya (lihat posting kami, “Mengoptimalkan Variasi Dengan Fastly"), jadi browser adalah area utama di mana Key
dapat membuat dampak.
Persyaratan untuk semua variasi untuk memiliki resep kunci yang sama agak membatasi, dan saya ingin melihat semacam opsi "keluar lebih awal" dalam spesifikasi. Ini akan memungkinkan Anda untuk melakukan hal-hal seperti, "Bervariasi pada status otentikasi, dan jika masuk, juga bervariasi pada preferensi."
Usulan Varian
Key
adalah mekanisme umum yang bagus, tetapi beberapa header memiliki aturan yang lebih kompleks untuk nilainya, dan memahami semantik nilai tersebut dapat membantu kita menemukan cara otomatis untuk mengurangi variasi cache. Misalnya, bayangkan dua permintaan datang dengan nilai Accept-Language
berbeda, en-gb
dan en-us
, tetapi meskipun situs web Anda memiliki dukungan untuk variasi bahasa, Anda hanya memiliki satu "Bahasa Inggris". Jika kami menjawab permintaan untuk bahasa Inggris AS dan respons itu di-cache di CDN, maka itu tidak dapat digunakan kembali untuk permintaan Inggris Inggris, karena nilai Accept-Language
akan berbeda dan cache tidak cukup pintar untuk mengetahui lebih baik .
Masukkan, dengan keriuhan yang cukup besar, proposal Varian. Ini akan memungkinkan server untuk menggambarkan varian mana yang mereka dukung, memungkinkan cache untuk membuat keputusan yang lebih cerdas tentang variasi mana yang benar-benar berbeda dan mana yang secara efektif sama.
Saat ini, Variants adalah draf yang sangat awal, dan karena dirancang untuk membantu dengan Accept-Encoding
dan Accept-Language
, kegunaannya agak terbatas pada cache bersama, seperti CDN, daripada cache browser. Tapi itu berpasangan dengan baik dengan Key
dan melengkapi gambar untuk kontrol variasi cache yang lebih baik.
Kesimpulan
Ada banyak hal yang dapat diambil di sini, dan meskipun mungkin menarik untuk memahami cara kerja browser di bawah tenda, ada juga beberapa hal sederhana yang dapat Anda saring darinya:
- Sebagian besar browser memperlakukan
Vary
sebagai validator. Jika Anda ingin beberapa variasi terpisah di-cache, temukan cara untuk menggunakan URL yang berbeda. - Browser mengabaikan
Vary
untuk sumber daya yang didorong menggunakan HTTP/2 server push, jadi jangan memvariasikan apa pun yang Anda push. - Peramban memiliki banyak sekali cache, dan bekerja dengan cara yang berbeda. Sebaiknya coba pahami bagaimana keputusan caching Anda memengaruhi kinerja di masing-masing keputusan, terutama dalam konteks
Vary
. -
Vary
tidak berguna seperti yang seharusnya, danKey
yang dipasangkan dengan Petunjuk Klien mulai mengubahnya. Ikuti bersama dengan dukungan browser untuk mengetahui kapan Anda dapat mulai menggunakannya.
Maju dan jadilah variabel.