ETag คือส่วนหัวการตอบสนอง HTTP ที่มีตัวระบุเฉพาะของเวอร์ชันทรัพยากร เซิร์ฟเวอร์สร้าง ETag เป็นแฮชของเนื้อหาหรือหมายเลขเวอร์ชันและส่งคืนให้กับไคลเอ็นต์พร้อมกับข้อมูล ในการร้องขอครั้งต่อไป ไคลเอ็นต์จะส่งตัวระบุนี้อีกครั้งในส่วนหัว If-None-Match เพื่อให้เซิร์ฟเวอร์ตรวจสอบว่าทรัพยากรมีการเปลี่ยนแปลงหรือไม่ ตามข้อมูลจาก MDN Web Docs, 2025 ETag เป็นพื้นฐานของกลไกการร้องขอ GET แบบมีเงื่อนไขใน HTTP การร้องขอแบบมีเงื่อนไข ด้วย ETag ช่วยลดปริมาณข้อมูลที่ถ่ายโอนระหว่างการซิงค์แอปพลิเคชันมือถือได้ถึง 90%
ประเด็นสำคัญ
ETag (Entity Tag) คือส่วนหัว HTTP ในตระกูลส่วนหัวแบบมีเงื่อนไขที่ใช้ตรวจสอบความถูกต้องของทรัพยากรที่ถูกแคชไว้ เซิร์ฟเวอร์คำนวณ ETag เป็นแฮช (MD5, SHA-256) หรือหมายเลขเวอร์ชันของทรัพยากรและส่งคืนในการตอบสนองต่อการร้องขอ GET ไคลเอ็นต์จัดเก็บ ETag พร้อมกับข้อมูลและส่งในส่วนหัว If-None-Match ในการร้องขอครั้งต่อไป หากเนื้อหาของทรัพยากรไม่มีการเปลี่ยนแปลง เซิร์ฟเวอร์จะตอบสนองด้วยสถานะ 304 Not Modified โดยไม่มีเนื้อหาการตอบสนอง
สำหรับแอปพลิเคชันมือถือ ETag มีความสำคัญอย่างยิ่งเนื่องจาก ช่วยลดปริมาณข้อมูลที่ดาวน์โหลด ในการเปิดหรือซิงค์แต่ละครั้ง แอปจะตรวจสอบความสดใหม่ของทรัพยากรด้วยการร้องขอ If-None-Match — แทนที่จะโหลดข้อมูลทั้งหมด แอปจะได้รับ 304 และใช้สำเนาในเครื่อง ตามข้อมูลจาก Google Chrome Team (2024) การใช้ ETag ใน API มือถือช่วยลดขนาดการตอบสนองโดยเฉลี่ย 87% สำหรับรายการและ 94% สำหรับวัตถุแต่ละรายการ
ETag ถูกสร้างขึ้นที่ฝั่งเซิร์ฟเวอร์และสามารถเป็น แบบกำหนดได้ (เหมือนกันสำหรับเนื้อหาที่เหมือนกัน มีประโยชน์สำหรับแคชที่ใช้ร่วมกัน) หรือไม่ซ้ำกันต่อการตอบสนอง (สำหรับการตรวจสอบอย่างเข้มงวด) ใน REST API ที่ออกแบบมาสำหรับการซิงค์บนมือถือ การรวมกันที่พบบ่อยที่สุดคือแฮชเนื้อหาและหมายเลขเวอร์ชันของระเบียนในฐานข้อมูล
ETag แบบเข้ม (strong ETag) คือตัวระบุที่เปลี่ยนแปลงตามการแก้ไขเนื้อหาใด ๆ รวมถึงการเปลี่ยนแปลงเล็กน้อย (ช่องว่าง การจัดรูปแบบ) รูปแบบ: “abc123def” (ในเครื่องหมายคำพูดคู่ ไม่มีคำนำหน้า) ETag แบบเข้มรับประกันว่าทรัพยากรไม่มีการเปลี่ยนแปลงแบบไบต์ต่อไบต์ ซึ่งจำเป็นสำหรับการร้องขอช่วง (Range requests) และการตรวจสอบความสมบูรณ์ของการดาวน์โหลดบางส่วน
ETag แบบอ่อน (weak ETag) คือตัวระบุที่มีคำนำหน้า W/ ตัวอย่างเช่น W/“abc123def” อนุญาตให้ทรัพยากรมีความเท่าเทียมทางความหมายแม้ว่าการแสดงผลแบบไบต์จะแตกต่างกัน ETag แบบอ่อนมีประโยชน์สำหรับเซิร์ฟเวอร์ที่สร้างการตอบสนองแบบไดนามิกด้วยช่องว่างหรือการจัดรูปแบบที่แตกต่างกันแต่มีความหมายเหมือนกัน อย่างไรก็ตาม ETag แบบอ่อนไม่รองรับการร้องขอช่วง
การเปรียบเทียบประเภท ETag:
| คุณลักษณะ | ETag แบบเข้ม | ETag แบบอ่อน |
|---|---|---|
| รูปแบบ | “hash” | W/“hash” |
| ความไว | ไบต์ต่อไบต์ | เชิงความหมาย |
| การร้องขอ Range | รองรับ | ไม่รองรับ |
| การแคช CDN | เหมาะที่สุด | จำกัด |
| การซิงค์ | แม่นยำสูง | อนุญาตให้มีการชนกัน |
Last-Modified คือส่วนหัว HTTP ที่ระบุวันที่และเวลาของการแก้ไขทรัพยากรครั้งล่าสุด ไคลเอ็นต์ส่งกลับในส่วนหัว If-Modified-Since Last-Modified ใช้งานได้ง่ายกว่า (เซิร์ฟเวอร์ต้องการเพียงวันที่) แต่มีข้อจำกัดพื้นฐาน: ความละเอียดหนึ่งวินาที (การเปลี่ยนแปลงสองครั้งในวินาทีเดียวกันไม่สามารถแยกแยะได้) และไม่สามารถระบุได้ว่าเนื้อหาเปลี่ยนแปลงหรือไม่หากการประทับเวลาเหมือนกัน (เช่น หลังจากกู้คืนจากข้อมูลสำรอง)
ETag แก้ปัญหาเหล่านี้: แฮชเนื้อหาเปลี่ยนแปลงตามการแก้ไขใด ๆ โดยไม่ขึ้นกับเวลา ดังนั้น REST API สมัยใหม่จึงใช้ส่วนหัวทั้งสองร่วมกัน: ETag สำหรับการตรวจสอบที่แม่นยำ และ Last-Modified สำหรับการกรองโดยประมาณบน CDN Apache HTTP Server และ Nginx สร้างส่วนหัวทั้งสองสำหรับไฟล์คงที่โดยค่าเริ่มต้น
สำหรับแอปพลิเคชันมือถือที่มีการซิงค์ ETag มีความสำคัญมากกว่าเพราะช่วยตรวจจับข้อขัดแย้งในการแก้ไข หากไคลเอ็นต์ส่งการร้องขอ PUT ด้วย If-Match: “etag” เซิร์ฟเวอร์จะปฏิเสธการร้องขอหากทรัพยากรถูกแก้ไขโดยไคลเอ็นต์อื่น (การล็อกแบบมองโลกในแง่ดี) Last-Modified ไม่สามารถรับประกันความน่าเชื่อถือดังกล่าวได้เนื่องจากความแม่นยำระดับวินาที
มาดูการนำไปใช้ฝั่งไคลเอ็นต์ ของ ETag ในแอปพลิเคชันมือถือโดยใช้ Kotlin กับ Retrofit และ OkHttp ในการร้องขอ GET แต่ละครั้ง ไคลเอ็นต์จะบันทึก ETag จากการตอบสนอง และในการร้องขอครั้งต่อไปจะส่งในส่วนหัว If-None-Match หากเซิร์ฟเวอร์ส่งคืน 304 ข้อมูลจะไม่ถูกดาวน์โหลดซ้ำ
การตั้งค่าไคลเอ็นต์ OkHttp ด้วยการแคช ETag:
class EtagClient {
private val etagCache =
mutableMapOf<String, String>()
private val client = OkHttpClient.Builder().build()
suspend fun fetchWithEtag(
url: String
): Result<String> {
val request = Request.Builder()
.url(url)
.header("If-None-Match",
etagCache[url] ?: "")
.build()
val response = client.newCall(request).await()
return when (response.code) {
304 -> Result.success(
"not_modified")
200 -> {
response.header("ETag")?.let {
etagCache[url] = it
}
Result.success(response.body?.string()
?: "")
}
else -> Result.failure(
Exception("HTTP ${response.code}"))
}
}
}
ไคลเอ็นต์บันทึก ETag หลังจากการตอบสนอง 200 ที่สำเร็จและส่งในส่วนหัว If-None-Match ในการร้องขอครั้งต่อไป เมื่อได้รับการตอบสนอง 304 ไคลเอ็นต์ทราบว่าเวอร์ชันในเครื่องเป็นปัจจุบันและไม่สิ้นเปลืองแบนด์วิดท์ในการดาวน์โหลดซ้ำ รูปแบบนี้ช่วยลดต้นทุนเครือข่ายของแอปพลิเคชันมือถือลง 80–90% สำหรับทรัพยากรที่ถูกร้องขอบ่อย
ETag เป็นกลไกสำคัญ ในการปรับการซิงค์แอปพลิเคชันมือถือกับ REST API ให้เหมาะสมที่สุด ในรูปแบบการซิงค์มาตรฐาน ไคลเอ็นต์จะร้องขอรายการทรัพยากรด้วยการตรวจสอบ ETag ก่อน — หากไม่มีทรัพยากรใดเปลี่ยนแปลง เซิร์ฟเวอร์จะส่งคืน 304 และไคลเอ็นต์จะเสร็จสิ้นการซิงค์ หากมีการเปลี่ยนแปลง เซิร์ฟเวอร์จะส่งคืนเฉพาะทรัพยากรที่ถูกแก้ไข วิธีการนี้เรียกว่าการซิงค์แบบเดลต้าและมีความสำคัญอย่างยิ่งสำหรับอุปกรณ์มือถือที่มีแบนด์วิดท์จำกัด
ในสถานการณ์ การล็อกแบบมองโลกในแง่ดี ETag ใช้เพื่อป้องกันข้อขัดแย้งแบบ Lost Update เมื่อไคลเอ็นต์ส่งการร้องขอ PUT เพื่ออัปเดตทรัพยากร จะรวมส่วนหัว If-Match: “etag” หาก ETag ไม่ตรงกัน (ไคลเอ็นต์อื่นแก้ไขทรัพยากรแล้ว) เซิร์ฟเวอร์จะตอบสนองด้วย 412 Precondition Failed และไคลเอ็นต์ต้องดึงเวอร์ชันปัจจุบันอีกครั้งและลองแก้ไขใหม่ วิธีการนี้รับประกันความสอดคล้องของข้อมูลโดยไม่ต้องล็อกในระดับฐานข้อมูล
สำหรับระบบกระจายที่มีโหมดออฟไลน์ ETag ถูกใช้ ร่วมกับการแก้ไขข้อขัดแย้ง ไคลเอ็นต์ซิงค์โดยรับ ETag ปัจจุบันสำหรับทรัพยากรทั้งหมด เมื่อส่งการเปลี่ยนแปลง เซิร์ฟเวอร์จะตรวจสอบ If-Match — หาก ETag ไม่ตรงกัน ข้อขัดแย้งจะถูกบันทึกและแก้ไขตามกลยุทธ์ที่เลือก (LWW, Merge) ตามรายงาน Postman API Report (2025) 67% ของ REST API ในระบบการผลิตสำหรับแอปพลิเคชันมือถือใช้ ETag เป็นกลไกหลักในการตรวจสอบเวอร์ชัน
คำถามที่พบบ่อย
ETag คือส่วนหัวการตอบสนอง HTTP ที่มีตัวระบุเฉพาะของเวอร์ชันทรัพยากร ไคลเอ็นต์ใช้สำหรับการร้องขอแบบมีเงื่อนไข: หากทรัพยากรไม่มีการเปลี่ยนแปลง เซิร์ฟเวอร์จะส่งคืน 304 Not Modified โดยไม่มีเนื้อหาการตอบสนอง ประหยัดแบนด์วิดท์
ETag ใช้แฮชเนื้อหาเพื่อการเปรียบเทียบที่แม่นยำ Last-Modified ขึ้นอยู่กับวันที่แก้ไขด้วยความแม่นยำระดับวินาที ETag เชื่อถือได้มากกว่าในการตรวจจับการเปลี่ยนแปลงจริงและรองรับการล็อกแบบมองโลกในแง่ดีผ่าน If-Match
ETag แบบเข้ม (ไม่มีคำนำหน้า) แยกแยะทรัพยากรแบบไบต์ต่อไบต์ ETag แบบอ่อน (มีคำนำหน้า W/) อนุญาตให้มีความเท่าเทียมทางความหมาย แบบเข้มจำเป็นสำหรับการร้องขอ Range แบบอ่อนสำหรับเนื้อหาที่สร้างแบบไดนามิก
ETag ลดแบนด์วิดท์ลง 80–90%: ไคลเอ็นต์ตรวจสอบความสดใหม่ของทรัพยากรทั้งหมดผ่าน If-None-Match ดาวน์โหลดเฉพาะทรัพยากรที่เปลี่ยนแปลง หากไม่มี ETag ไคลเอ็นต์จะดาวน์โหลดข้อมูลทั้งหมดในการซิงค์แต่ละครั้ง สิ้นเปลืองแบนด์วิดท์และแบตเตอรี่
เซิร์ฟเวอร์คำนวณ ETag เป็นแฮช (MD5, SHA-256) ของเนื้อหาการตอบสนองหรือใช้หมายเลขเวอร์ชันของระเบียนจากฐานข้อมูล ใน Spring Boot คำอธิบายประกอบ @Cacheable ด้วย etag = true ก็เพียงพอ ใน Express.js มิดเดิลแวร์ etag ถูกเปิดใช้งานโดยค่าเริ่มต้น
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ