Memahami The Vary Header

Diterbitkan: 2022-03-10
Ringkasan cepat Header 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.

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

Ilustrasi percakapan negosiasi konten antara pengguna dan bank
(Lihat versi besar)

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.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

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:

Ilustrasi cache di browser
(Lihat versi besar)

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 header Link 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 penangan fetch 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 seperti Cache-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.

Aliran PUSH HTTP/2 melalui cache browser
(Lihat versi besar)

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:

Hasil tes untuk link rel=preload di Google Chrome
(Lihat versi besar)

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

Hasil pengujian untuk cache pekerja layanan di Google Chrome
(Lihat versi besar)

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.

Hasil pengujian untuk cache push H2 di Google Chrome
(Lihat versi besar)

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 , dan Vary .

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, dan Key yang dipasangkan dengan Petunjuk Klien mulai mengubahnya. Ikuti bersama dengan dukungan browser untuk mengetahui kapan Anda dapat mulai menggunakannya.

Maju dan jadilah variabel.