บทความ › เซิร์ฟเวอร์และ DevOps
ย้ายเว็บแล้ว ทำไมบางคนยังเห็นเว็บเก่า เรื่อง DNS cache และ TTL จากเคสจริง
เปลี่ยน DNS ชี้ไป เซิร์ฟเวอร์ ใหม่แล้ว แต่ Google DNS ยังตอบ IP เก่าอยู่สองชั่วโมง ทำไมถึงเป็นแบบนั้น ดูได้ยังไงว่าใครเห็นอะไร และย้ายโดเมนครั้งหน้าให้ไม่ต้องรอ
- DNS ไม่ได้ "กระจาย" ไปไหน แค่คนที่ถามไว้ก่อนยังจำคำตอบเก่าอยู่จนกว่า TTL จะหมด
- เวลารอสูงสุดเท่ากับ TTL ของ record เก่า ไม่ใช่ 48 ชั่วโมง ลด TTL เหลือ 300 วินาที ล่วงหน้าหนึ่งวัน แล้วค่อยย้าย
- เช็กทีละชั้นด้วย dig หรือ nslookup ถาม nameserver ตัวจริง และถาม 8.8.8.8 แล้วเทียบกัน
- ขอใบรับรอง TLS หลังจาก DNS สาธารณะเห็น IP ใหม่แล้วเท่านั้น ไม่ใช่หลังจากเครื่องตัวเองเห็น
วันที่ 16 ตุลาคม เราย้ายโดเมนหนึ่ง (mykaset.com) จากโฮสต์เดิมมาขึ้น เซิร์ฟเวอร์ ของเราเอง แก้ A record ที่ DigitalOcean เสร็จ ถาม ns1.digitalocean.com ตรง ๆ ได้ IP ใหม่ทันที แต่ถาม 8.8.8.8 ของ Google ยังได้ IP เก่าอยู่ ประมาณ 2 ชั่วโมง 20 นาที คนที่ใช้ Google DNS จึงยังเห็นเว็บเก่า ขณะที่คนอื่นเห็นเว็บใหม่แล้ว บทความนี้อธิบายว่าทำไม ดูได้ยังไงว่าแต่ละชั้นเห็นอะไร และย้ายครั้งหน้าให้ไม่ต้องรอ
คำสั่งทดสอบบน Windows 11 และ Ubuntu 24.04 บน Ubuntu dig อยู่ในแพ็กเกจ dnsutils (sudo apt install dnsutils) บน Windows ใช้ nslookup หรือ Resolve-DnsName ใน PowerShell
คำถามหนึ่งครั้งเดินทางผ่านอะไรบ้าง
พิมพ์ชื่อเว็บในเบราว์เซอร์ คำถาม “ชื่อนี้ IP อะไร” จะวิ่งผ่านชั้นเหล่านี้ และ ทุกชั้นมี cache ของตัวเอง
| ชั้น | ใครดูแล | จำคำตอบไว้ที่ไหน |
|---|---|---|
| เบราว์เซอร์ | Chrome, Firefox | cache ในตัวเบราว์เซอร์ |
| ระบบปฏิบัติการ | Windows, Linux | DNS client cache |
| resolver | ISP, Google 8.8.8.8, Cloudflare 1.1.1.1 | cache ของ resolver ใช้ร่วมกันกับผู้ใช้หลายล้านคน |
| authoritative nameserver | ผู้ให้บริการ DNS ของโดเมน เช่น DigitalOcean | ไม่มี cache นี่คือต้นทาง |
ถ้าชั้นไหนมีคำตอบอยู่แล้วและยังไม่หมดอายุ มันจะตอบเลยโดยไม่ถามชั้นถัดไป เว็บเร็วก็เพราะอย่างนี้ และเว็บเก่าค้างก็เพราะอย่างนี้เหมือนกัน
TTL คือวันหมดอายุของคำตอบ
ทุก record มีตัวเลข TTL (time to live) เป็นวินาที บอกคนที่ได้คำตอบไปว่าจำได้นานแค่ไหน ตัวอย่างจากเว็บนี้เอง ถาม nameserver ตัวจริง
# Ubuntu 24.04
dig +noall +answer 6amdev.com @ns1.digitalocean.com
# 6amdev.com. 3600 IN A 157.x.x.x
3600 คือ TTL เต็ม ถาม resolver ที่เคยตอบคำถามนี้ไปแล้ว เลขจะน้อยกว่าเพราะนับถอยหลังอยู่
dig +noall +answer 6amdev.com @8.8.8.8
# 6amdev.com. 2299 IN A 157.x.x.x ← เหลืออีก 2299 วินาที ค่อยถามต้นทางใหม่
จุดสำคัญ: TTL ที่คุมเวลารอ คือ TTL ของ record เก่า ที่คนอื่นถามไว้ก่อนเราแก้ ไม่ใช่ค่าที่เราเพิ่งตั้ง resolver ที่จำค่าเก่าไว้จะไม่รู้เลยว่าเราแก้แล้ว จนกว่าตัวนับของมันจะถึงศูนย์
ตัวอย่างตัวเลข: TTL 3600
- A record เดิม TTL 3600 เราแก้เป็น IP ใหม่ตอน 10:00
- resolver ก. เพิ่งถามตอน 09:59 มันจะตอบ IP เก่าถึง 10:59 แล้วค่อยถามใหม่ได้ IP ใหม่
- resolver ข. ถามตอน 09:20 มันจะเปลี่ยนตอน 10:20
- resolver ค. ไม่เคยถามชื่อนี้ ถามตอน 10:01 ได้ IP ใหม่ทันที
ดังนั้นในกรณีเลวร้ายสุด คำตอบเก่าตัวสุดท้ายหมดอายุ ไม่เกิน 60 นาที หลังแก้ ไม่ใช่ 48 ชั่วโมง ตัวเลข 48 ชั่วโมงที่ได้ยินบ่อยมาจากการเปลี่ยน nameserver ที่ระดับ registrar ซึ่ง TTL ของ NS record มักยาวเป็นวัน และจาก resolver บางแห่งที่ไม่ยอมเคารพ TTL
คนต่างคนเห็นคนละเว็บ ไม่ใช่เพราะ DNS “ยังกระจายไม่ถึง” แต่เพราะแต่ละคนใช้ resolver คนละตัว และแต่ละตัวถามต้นทางครั้งล่าสุดคนละเวลา
เคส mykaset.com เราไม่ได้ลด TTL ก่อน และ record เดิมที่โฮสต์เก่าตั้งไว้ยาว 8.8.8.8 เลยค้างค่าเก่าถึง 2 ชั่วโมงกว่า ขณะที่ resolver อื่นที่ไม่เคยถามชื่อนี้ได้ค่าใหม่ทันที
ทำไมต้องลด TTL ก่อนย้าย ไม่ใช่ตอนย้าย
ถ้าแก้ TTL จาก 3600 เป็น 300 แล้วเปลี่ยน IP ทันทีในนาทีเดียวกัน ไม่ช่วยอะไรเลย เพราะคนที่จำค่าเก่าอยู่ จำพร้อม TTL 3600 ตัวเก่า วิธีที่ถูกคือ
- หนึ่งวันก่อนย้าย แก้ TTL ของ record ที่จะเปลี่ยนเหลือ 300 วินาที (IP ยังเป็นตัวเก่า)
- รอให้ค่าเก่าหมดอายุทั่วโลก (รอเท่า TTL เดิม ถ้าเดิม 86400 ก็ต้องรอหนึ่งวันจริง ๆ)
- วันย้าย เปลี่ยน IP ทุกคนเห็นค่าใหม่ภายใน 5 นาที
- เว็บใหม่นิ่งแล้วค่อยปรับ TTL กลับเป็น 3600 เพื่อลดภาระ nameserver
เช็กว่าแต่ละชั้นเห็นอะไร
ถามต้นทางก่อน ถ้าต้นทางยังไม่ใช่ค่าใหม่ แปลว่าเรายังแก้ไม่สำเร็จ ไม่ใช่เรื่อง cache
# Ubuntu 24.04: ต้นทาง กับ resolver สาธารณะ
dig +short NS mykaset.com # หาว่า nameserver คือใคร
dig +short mykaset.com @ns1.digitalocean.com # ต้นทาง
dig +short mykaset.com @8.8.8.8 # Google
dig +short mykaset.com @1.1.1.1 # Cloudflare
dig +short mykaset.com # resolver ที่เครื่องเราใช้จริง
# Windows 11 (PowerShell)
nslookup mykaset.com ns1.digitalocean.com
nslookup mykaset.com 8.8.8.8
Resolve-DnsName mykaset.com -Type A -Server 8.8.8.8 # แสดงคอลัมน์ TTL ที่เหลือด้วย
ถ้าต้นทางถูกแล้ว แต่เครื่องเรายังเห็นเก่า ล้างชั้นที่ใกล้ตัวที่สุดก่อน
ipconfig /flushdns # Windows: ล้าง cache ของ OS
sudo resolvectl flush-caches # Ubuntu 24.04 (systemd-resolved)
Chrome มี cache ของตัวเองอีกชั้น เปิด chrome://net-internals/#dns แล้วกด Clear host cache ถ้าทำครบแล้วยังเห็นเก่า ตัวที่ค้างคือ resolver ซึ่งเราไม่ได้เป็นเจ้าของ ทางเลือกคือรอ เปลี่ยน DNS ของเครื่องไปใช้ตัวอื่นชั่วคราว หรือถ้าตัวที่ค้างคือ Google ใช้เครื่องมือล้างของเขาที่ dns.google/cache ใส่ชื่อโดเมนและชนิด record แล้วกด Flush ครั้งนั้นเรากดล้างแล้ว 8.8.8.8 ตอบค่าใหม่ทันที Cloudflare ก็มีหน้า Purge Cache แบบเดียวกันที่ one.one.one.one/purge-cache ล้างได้เฉพาะ resolver ของเขา คนที่ใช้ DNS ของ ISP ยังต้องรอเหมือนเดิม
certbot ก็ดู DNS สาธารณะ ไม่ใช่เครื่องเรา
ตอนขอใบรับรองด้วย HTTP-01 Let’s Encrypt จะ resolve ชื่อโดเมนจากหลายจุดทั่วโลก (เครื่องหลัก + remote perspectives หลายแห่ง และต้องผ่านเกินกึ่งหนึ่ง) แล้วมาดึงไฟล์ท้าทายที่ http://โดเมน/.well-known/acme-challenge/ ถ้าจุดไหนยังได้ IP เก่า มันจะไปเคาะโฮสต์เก่า ไม่เจอไฟล์ แล้ว challenge ล้ม ซ้ำได้ไม่กี่ครั้งก็ติด rate limit
สคริปต์เพิ่มเว็บของเราจึงเช็กก่อน certbot ทุกครั้ง โดยถาม nameserver ตัวจริง ไม่ใช่ resolver ของเครื่อง
# Ubuntu 24.04: หยุดถ้าต้นทางยังไม่ชี้มาที่เครื่องนี้
MY_IP=$(curl -s https://api.ipify.org)
NS=$(dig +short NS "$DOMAIN" | head -1)
DNS_IP=$(dig +short "$DOMAIN" @"$NS" | tail -1)
[ "$DNS_IP" = "$MY_IP" ] || { echo "DNS ยังชี้ไป $DNS_IP ไม่ใช่ $MY_IP รอก่อน"; exit 1; }
ผ่านด่านนี้แล้วยังควรเช็ก @8.8.8.8 และ @1.1.1.1 อีกครั้งก่อนกดขอจริง เพราะ Let’s Encrypt ก็ไปทาง DNS สาธารณะเหมือนกัน หรือเลี่ยงไปใช้ DNS-01 ที่ไม่ต้องให้ใครมาเคาะ เซิร์ฟเวอร์ ก็ได้
เช็กลิสต์ย้ายโดเมน
- หนึ่งวันก่อน: ลด TTL ของ A/AAAA และ CNAME ที่จะเปลี่ยนเหลือ 300
- เซิร์ฟเวอร์ ใหม่พร้อม ทดสอบผ่านไฟล์ hosts หรือ
curl --resolve mykaset.com:80:IP_ใหม่ http://mykaset.com/ - วันย้าย: เปลี่ยน IP ที่ nameserver แล้ว
dig @ns1...ยืนยันว่าต้นทางถูก - รอจน
@8.8.8.8และ@1.1.1.1ตอบค่าใหม่ ค้างก็กด flush ที่ dns.google/cache - ค่อยรัน
certbot(HTTP-01) หรือใช้ DNS-01 - ปล่อยโฮสต์เก่าไว้อีกอย่างน้อยเท่า TTL เดิม อย่าปิดวันนั้น
- นิ่งแล้ว ปรับ TTL กลับ 3600
ข้อผิดพลาดที่เจอบ่อย
- ดูจากเครื่องตัวเองแล้วสรุปว่าเสร็จ เครื่องเราอาจเห็นใหม่แล้วแต่ลูกค้ายังไม่เห็น หรือกลับกัน ถามต้นทางกับ resolver สาธารณะเสมอ
- ปิดโฮสต์เก่าทันที คนที่ยังได้ IP เก่าจะเจอ error แทนที่จะเจอเว็บเก่า
- ลืมลบไฟล์ hosts หลังทดสอบ แล้วงงว่าทำไมเห็นไม่เหมือนคนอื่น
- ลด TTL พร้อมเปลี่ยน IP ในครั้งเดียว ซึ่งไม่ช่วยอะไรตามที่อธิบายข้างบน
แหล่งอ้างอิง
คำถามที่พบบ่อย
ย้ายเว็บแล้วเห็นเว็บเก่า ต้องรอกี่ชั่วโมง
อย่างมากเท่ากับ TTL ของ record เก่า นับจากตอนที่แก้ เช่น TTL 3600 รอไม่เกิน 60 นาที ถ้าอยากรู้ว่าเหลืออีกเท่าไร ถาม resolver ที่ยังตอบค่าเก่าด้วย dig แล้วดูเลข TTL ที่นับถอยหลังในคำตอบ
ทำไมเพื่อนเห็นเว็บใหม่แล้ว แต่เรายังเห็นเว็บเก่า
คนละ resolver กัน resolver ของเพื่อนอาจไม่เคยถามชื่อนี้มาก่อนเลยได้ค่าใหม่ทันที ส่วนของเราถามไว้ก่อนย้ายและยังจำค่าเก่าอยู่ ลองล้าง cache ที่เครื่องก่อน ถ้ายังไม่ได้ก็เป็นที่ resolver ต้องรอหรือเปลี่ยนไปใช้ resolver อื่นชั่วคราว
ไฟล์ hosts ช่วยอะไรได้บ้าง
ใส่ชื่อโดเมนกับ IP ใหม่ในไฟล์ hosts ของเครื่องตัวเอง เบราว์เซอร์จะข้าม DNS ไปเลย ใช้ทดสอบเว็บใหม่ก่อนย้ายได้ดี แต่อย่าลืมลบออก ไม่อย่างนั้นจะเห็นไม่เหมือนคนอื่นไปอีกนาน
คำสั่งและค่าตั้งในบทความทดสอบบนระบบตัวอย่าง ลองบนเครื่องทดสอบก่อนใช้กับระบบจริงเสมอ