การป้องกันแคชผ่านพารามิเตอร์


123

เราต้องการแคชหน้าอกในการผลิต แต่ไม่ต้องเสียเวลามากมายในการหาระบบเพื่อทำเช่นนั้น ความคิดของฉันคือการใช้ param ต่อท้ายไฟล์ css และ js ด้วยหมายเลขเวอร์ชันปัจจุบัน:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

คำถามสองข้อ: สิ่งนี้จะทำลายแคชได้อย่างมีประสิทธิภาพหรือไม่? พารามิเตอร์จะทำให้เบราว์เซอร์ไม่แคชการตอบสนองจาก url นั้นเนื่องจากพารามิเตอร์ระบุว่านี่เป็นเนื้อหาแบบไดนามิกหรือไม่

คำตอบ:


115

พระรามบ่งชี้สตริงแบบสอบถามและเบราว์เซอร์จึงจะคิดว่ามันเป็นเส้นทางใหม่จากการพูด?v=1.123 ?v=1.0จึงทำให้โหลดจากไฟล์ไม่ใช่จากแคช ตามที่เธอต้องการ.

และเบราว์เซอร์จะถือว่าแหล่งที่มาจะยังคงเหมือนเดิมในครั้งต่อไปที่คุณโทร?v=1.123และควรแคชด้วยสตริงนั้น ดังนั้นมันจะคงอยู่จนกว่า แต่เซิร์ฟเวอร์ของคุณมีการตั้งค่าจนกว่าคุณจะย้ายไป?v=1.124หรืออื่น ๆ


4
อ้างถึง Steve Souders: "เพื่อให้ได้ประโยชน์จากการแคชโดยผู้รับมอบฉันทะที่เป็นที่นิยมหลีกเลี่ยงการแก้ไขด้วย Querystring และเปลี่ยนชื่อไฟล์แทน" อ่านคำอธิบายฉบับเต็มได้ที่นี่: stevesouders.com/blog/2008/08/23/…
lao

25
โพสต์บล็อกนั้นใกล้จะครบรอบหนึ่งทศวรรษแล้ว คุณคิดว่าผู้ให้บริการแคชและ CDN ยังไม่รองรับหรือไม่? ปลาหมึกดูเหมือนว่าจะสามารถไปยังเอกสารแคชกับสตริงแบบสอบถามในขณะนี้
jeteon

1
บางทีนี่อาจช่วยใครบางคน: โดยส่วนตัวแล้วฉันใช้การประทับเวลาการแก้ไขไฟล์เป็นพารามิเตอร์เวอร์ชัน 'อัตโนมัติ' เช่น <link rel="stylesheet" href="style.css?v=1487935578" />
oelna

ฉันไม่เข้าใจเป็นการส่วนตัวว่าทำไม แต่ Lara Hogan (Swanson) (ผู้จัดการฝ่ายวิศวกรรมของ Etsy) ไม่แนะนำให้ใช้พารามิเตอร์การค้นหาเพื่อป้องกันแคช ฉันคิดว่ามันเกี่ยวข้องกับพร็อกซีแคชระหว่างผู้ใช้และเซิร์ฟเวอร์
Sam Rueby

36

คำถามสองข้อ: สิ่งนี้จะทำลายแคชได้อย่างมีประสิทธิภาพหรือไม่?

ใช่. แม้แต่ Stack Overflow ก็ใช้วิธีนี้แม้ว่าฉันจะจำได้ว่าพวกเขา (มีผู้เยี่ยมชมหลายล้านคนต่อวันและ zillions ของไคลเอนต์และเวอร์ชันและการกำหนดค่าพร็อกซีที่แตกต่างกัน) ก็มีบางกรณีที่แปลกประหลาดซึ่งแม้จะไม่เพียงพอที่จะทำลายแคช แต่ข้อสันนิษฐานทั่วไปคือวิธีนี้ใช้ได้ผลและเป็นวิธีที่เหมาะสมในการทำลายแคชบนไคลเอนต์

พารามิเตอร์จะทำให้เบราว์เซอร์ไม่แคชการตอบสนองจาก url นั้นเนื่องจากพารามิเตอร์ระบุว่านี่เป็นเนื้อหาแบบไดนามิกหรือไม่

ไม่พารามิเตอร์จะไม่เปลี่ยนนโยบายการแคช ส่วนหัวแคชที่เซิร์ฟเวอร์ส่งยังคงมีผลและหากไม่ส่งใด ๆ จะเป็นค่าเริ่มต้นของเบราว์เซอร์


1
@spender ฉันไม่พบข้อมูลอ้างอิงตอนนี้ฉันกลัวว่ามีบทความบล็อกยาวหรือคำตอบ SO ที่ Jeff Atwood พูดถึงเรื่องนี้ (IIRC)
Pekka

2
@spender ฉันได้อ่านพบว่าพร็อกซีเซิร์ฟเวอร์บางตัว (ทั้งเก่าหรือสามารถกำหนดค่าให้) ละเว้นสตริงการสืบค้นเมื่อแคช
MrWhite

2
@spender - ฉันเคยได้ยินเหมือนกันและฉันคิดว่าการเปลี่ยนชื่อไฟล์หรือเส้นทางเป็นตัวเลือกที่ดีที่สุด อาจเป็นเรื่องง่ายที่สุดที่จะปล่อยให้ย้ายไฟล์คงที่ทั้งหมดของคุณภายใต้ชื่อโฟลเดอร์ที่เป็นเวอร์ชันเช่น/static/v22/file.cssคุณสามารถทำหลายไฟล์ด้วยการเปลี่ยนชื่อโฟลเดอร์เดียวเช่น /static/v23/file.cssและ/static/v23/mystuff.js
Brad Parks

22

การใส่หมายเลขเวอร์ชันในชื่อไฟล์จริงจะปลอดภัยกว่า สิ่งนี้ช่วยให้มีหลายเวอร์ชันพร้อมกันเพื่อให้คุณสามารถเปิดตัวเวอร์ชันใหม่ได้และหากมีหน้า HTML ที่แคชไว้ซึ่งกำลังขอเวอร์ชันเก่าอยู่ก็จะได้เวอร์ชันที่ใช้งานได้กับ HTML

หมายเหตุหนึ่งในการปรับใช้เวอร์ชันที่ใหญ่ที่สุดที่ใดก็ได้บนอินเทอร์เน็ต jQuery ใช้หมายเลขเวอร์ชันในชื่อไฟล์จริงและอนุญาตให้หลายเวอร์ชันอยู่ร่วมกันได้อย่างปลอดภัยโดยไม่ต้องใช้ตรรกะพิเศษทางฝั่งเซิร์ฟเวอร์ (แต่ละเวอร์ชันเป็นเพียงไฟล์ที่แตกต่างกัน)

สิ่งนี้จะหยุดแคชหนึ่งครั้งเมื่อคุณปรับใช้เพจใหม่และไฟล์ที่เชื่อมโยงใหม่ (ซึ่งเป็นสิ่งที่คุณต้องการ) และจากนั้นเวอร์ชันเหล่านั้นสามารถแคชได้อย่างมีประสิทธิภาพ (ซึ่งคุณต้องการด้วย)


ฉันเห็นด้วยกับสิ่งนั้น แต่มันง่ายกว่ามากที่จะให้ Sinatra ต่อท้าย? v = <% = VERSION%> กับคำขอ css และ js ทั้งหมดเมื่อเทียบกับการต้องควบคุมทุกไฟล์ทีละไฟล์ ในที่สุดเราจะเปลี่ยนไปใช้ sinatra-assetpack ซึ่งจะประมวลผลล่วงหน้าและบีบอัดไฟล์ทั้งหมดและต่อท้ายเวอร์ชัน # เข้ากับชื่อไฟล์ซึ่งจะช่วยให้เราควบคุมทีละไฟล์ได้ง่ายขึ้นมาก
Brad Herman

1
ฉันยอมรับว่าการใส่หมายเลขเวอร์ชันในชื่อไฟล์เป็นวิธีแก้ปัญหาที่ปลอดภัยที่สุดหากคุณต้องการแน่ใจว่า 10,000% แต่ฉันไม่ได้ทำตามอาร์กิวเมนต์ "มีหลายเวอร์ชันพร้อมกัน" URL ที่มีพารามิเตอร์การค้นหาแตกต่างจาก URL เดียวกันโดยมีพารามิเตอร์การค้นหาที่แตกต่างกัน ลูกค้าควรถือว่าเป็นทรัพยากรสองอย่างที่แตกต่างกัน หากไม่เป็นเช่นนั้นลูกค้าจะเสีย
Pekka

2
@Pekka - สิ่งที่หมายเลขเวอร์ชันสามารถอนุญาตให้มีหลายเวอร์ชันพร้อมกันได้ แต่ต้องใช้ความร่วมมือของเซิร์ฟเวอร์เพื่อแมปพารามิเตอร์การสืบค้นกับไฟล์จริงที่ถูกต้อง ฉันไม่คิดว่านั่นคือสิ่งที่ OP กำลังทำอยู่ที่นี่และมีเหตุผลเพียงเล็กน้อยที่ต้องใช้ความซับซ้อนนั้นในการแก้ไขชื่อไฟล์นั้นง่ายกว่ามากและไม่จำเป็นต้องร่วมมือกับเซิร์ฟเวอร์ เห็นได้ชัดว่าทั้งสองอย่างสามารถทำงานได้
jfriend00

11

ดังที่คนอื่น ๆ กล่าวไว้การป้องกันแคชด้วยพารามิเตอร์การค้นหามักถือเป็นความคิดที่ไม่ดี (tm) และมีมานานแล้ว แสดงเวอร์ชันในชื่อไฟล์จะดีกว่า Html5 Boilerplate ไม่แนะนำให้ใช้สตริงเคียวรีและอื่น ๆ

ที่กล่าวว่าจากคำแนะนำที่ฉันได้เห็นซึ่งอ้างถึงแหล่งที่มาทั้งหมดดูเหมือนจะใช้ภูมิปัญญาของพวกเขาจากบทความปี 2008ของ Steve Souders ข้อสรุปของเขาขึ้นอยู่กับพฤติกรรมของผู้รับมอบฉันทะในเวลานั้นและอาจเกี่ยวข้องหรือไม่เกี่ยวข้องในปัจจุบัน แต่ในกรณีที่ไม่มีข้อมูลที่เป็นปัจจุบันมากขึ้นการเปลี่ยนชื่อไฟล์เป็นตัวเลือกที่ปลอดภัย


9

แคชจะหยุดการทำงานหนึ่งครั้งหลังจากที่ไคลเอนต์ดาวน์โหลดทรัพยากรแล้วการตอบกลับอื่น ๆ ทั้งหมดจะได้รับจากแคชไคลเอนต์เว้นแต่:

  1. พารามิเตอร์ v ถูกอัพเดต
  2. ไคลเอนต์ล้างแคช

6

โดยทั่วไปสิ่งนี้ควรจะใช้ได้ แต่เป็นไปได้ที่สิ่งนี้จะไม่ทำงานหากมีแคชกลาง (พร็อกซี) ที่กำหนดค่าให้ละเว้นพารามิเตอร์คำขอ

ตัวอย่างเช่นหากคุณให้บริการเนื้อหาแบบคงที่ผ่าน Akamai CDN สามารถกำหนดค่าให้ละเว้นพารามิเตอร์คำขอเพื่อป้องกันไม่ให้แคชหยุดทำงานโดยใช้วิธีนี้


5

มากขึ้นอยู่กับว่าคุณต้องการให้แคชของคุณมีประสิทธิภาพเพียงใด ตัวอย่างเช่นพร็อกซีเซิร์ฟเวอร์ Squid (และอาจเป็นอื่น ๆ ) เริ่มต้นที่จะไม่แคช URL ที่แสดงด้วยสตริงการสืบค้นอย่างน้อยก็เกิดขึ้นเมื่อมีการเขียนบทความนั้น หากคุณไม่ทราบว่ากรณีการใช้งานบางอย่างที่ทำให้เกิดการพลาดแคชโดยไม่จำเป็นให้ดำเนินการต่อด้วยพารามิเตอร์การสืบค้น แต่การตั้งค่ารูปแบบการป้องกันแคชตามชื่อไฟล์นั้นง่ายมากซึ่งจะช่วยหลีกเลี่ยงปัญหานี้


5
พร็อกซี Squid ที่อ้างถึงในบทความ Steve Souders ได้เปลี่ยนนโยบายการแคชเริ่มต้น ตั้งแต่เวอร์ชัน 2.7 (พฤษภาคม 2008) และเวอร์ชัน 3.1 (มีนาคม 2010) ลักษณะการทำงานเริ่มต้นคือการแคชเนื้อหาแบบไดนามิก
Josh Rack

5

พบการเปรียบเทียบ 2 เทคนิค (สตริงการสืบค้นเทียบกับชื่อไฟล์) ที่นี่ :

เวอร์ชันเป็นสตริงการสืบค้นมีปัญหาสองประการ

ประการแรกอาจไม่ใช่เบราว์เซอร์ที่ใช้แคชที่เราจำเป็นต้องหยุดเสมอไป มีการกล่าวกันว่าพร็อกซีบางตัว (อาจเก่ากว่า) ไม่สนใจข้อความค้นหาที่เกี่ยวกับพฤติกรรมการแคช

ประการที่สองในสถานการณ์การปรับใช้ที่ซับซ้อนมากขึ้นซึ่งคุณมีหลายส่วนหน้าและ / หรือเซิร์ฟเวอร์แบ็กเอนด์หลายตัวการอัปเกรดเป็นอะไรก็ได้ แต่จะเกิดขึ้นทันที คุณต้องสามารถให้บริการทั้งเวอร์ชันเก่าและเวอร์ชันใหม่ได้ในเวลาเดียวกัน ดูตัวอย่างว่าสิ่งนี้ส่งผลต่อคุณอย่างไรเมื่อใช้ Google App Engine


4

อีกวิธีหนึ่งที่คล้ายกันคือการใช้htaccess mod_rewriteเพื่อละเว้นบางส่วนของเส้นทางเมื่อให้บริการไฟล์ หน้าดัชนีที่ไม่เคยแคชของคุณอ้างอิงเส้นทางล่าสุดไปยังไฟล์

จากมุมมองของการพัฒนามันง่ายพอ ๆ กับการใช้ params สำหรับหมายเลขเวอร์ชัน แต่ก็แข็งแกร่งพอ ๆ กับวิธีการตั้งชื่อไฟล์

ใช้ส่วนที่ละเว้นของพา ธ สำหรับหมายเลขเวอร์ชันและเซิร์ฟเวอร์จะละเว้นมันและให้บริการไฟล์ที่ไม่ได้แคช

1.2.3/css/styles.cssทำหน้าที่ไฟล์เดียวกันcss/styles.cssเนื่องจากไดเร็กทอรีแรกถูกตัดและละเว้นโดยไฟล์ htaccess

รวมถึงไฟล์เวอร์ชัน

<?php
  $version = "1.2.3";
?>

<html>
  <head>
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
  </head>
  <body>
    <script src="<?php echo $version ?>/js/main.js"></script>
  </body>
</html>

โปรดทราบว่าวิธีนี้หมายความว่าคุณต้องปิดการใช้งานการแคชของหน้าดัชนี - การใช้แท็ก <meta> เพื่อปิดการแคชในเบราว์เซอร์ทั้งหมดหรือไม่?

ไฟล์. htaccess

RewriteEngine On

# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f 
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d 

# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L] 

คุณสามารถใช้แนวทางเดียวกันนี้บนแพลตฟอร์มเซิร์ฟเวอร์ใดก็ได้ที่อนุญาตให้เขียน URL ใหม่ได้

(เงื่อนไขการเขียนใหม่ที่ดัดแปลงมาจากmod_rewrite - เขียนไดเร็กทอรีใหม่เป็นสตริงการสืบค้นยกเว้น / #! / )

... และหากคุณต้องการการป้องกันแคชสำหรับหน้าดัชนี / จุดเข้าไซต์ของคุณคุณสามารถใช้ JavaSriptเพื่อรีเฟรชได้ตลอดเวลา


2
<script type="text/javascript">
// front end cache bust

var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
</script> 

ในระหว่างการพัฒนา / ทดสอบรุ่นใหม่แคชอาจมีปัญหาได้เนื่องจากเบราว์เซอร์เซิร์ฟเวอร์และแม้กระทั่งบางครั้งโทรศัพท์ 3G (หากคุณใช้งานอุปกรณ์เคลื่อนที่) จะแคชเนื้อหาแบบคงที่ (เช่น JS, CSS, HTML, img) คุณสามารถเอาชนะสิ่งนี้ได้โดยการต่อท้ายหมายเลขเวอร์ชันหมายเลขสุ่มหรือการประทับเวลาที่ URL เช่น: JSP: <script src = "js / excel.js? time = <% = new java.util.Date ()%>"> </ สคริปต์> ในกรณีที่คุณใช้ HTML แท้ (แทนที่จะเป็นหน้าเซิร์ฟเวอร์ JSP, ASP, PHP) เซิร์ฟเวอร์จะไม่ช่วยคุณ ในเบราว์เซอร์ลิงก์จะโหลดก่อนที่ JS จะทำงานดังนั้นคุณต้องลบลิงก์และโหลดด้วย JS
Conete Cristian

ฉันไม่คิดว่านี่จะโหลดไฟล์ JS ตามลำดับพร้อมกัน
Stealth Rabbi

0
 <script>
    var storedSrcElements = [
         "js/exampleFile.js",
         "js/sampleFile.js",
         "css/style.css"
          ];

    var head= document.getElementsByTagName('head')[0];
    var script;
    var link;
    var versionNumberNew = 4.6;

    for(i=0;i<storedSrcElements.length;i++){
     script= document.createElement('script');
     script.type= 'text/javascript';
     script.src= storedSrcElements[i] + "?" + versionNumberNew;
     head.appendChild(script);
    }     


     </script> 


       ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.