ผมเคยนั่งแก้สคริปต์ตอนดึก ๆ อยู่หลายคืนแบบไม่น่าให้อภัย ทั้งที่มันควรจะแค่ “ดึงไฟล์มาแล้วไปต่อ” เท่านั้น ปรากฏว่าเกือบทุกครั้ง ตัวปัญหาไม่ใช่ cURL พัง แต่เป็น cURL ทำตามที่สั่งเป๊ะ ๆ ไม่ใช่สิ่งที่ผมต้องการจริง ๆ สรุปแล้วช่องว่างระหว่าง “curl -O ใช้ได้” กับ “curl -O ใช้งานได้เสถียรในโปรดักชัน” มันกว้างกว่าที่คิดมาก
คู่มือนี้ถูกเขียนขึ้นมาเพื่อปิดช่องว่างนั้น cURL ติดตั้งมาพร้อมกับ macOS, Linux ส่วนใหญ่ และ Windows 10 ขึ้นไปอยู่แล้ว เท่ากับว่ามีโอกาสสูงมากที่เครื่องคุณมีมันอยู่แล้วตอนนี้ แต่พอเจอปัญหาอย่าง redirect แบบเงียบ ๆ ที่ล้มเหลว, 403 ที่เดาไม่ออก, หรือจาก “ดาวน์โหลดไฟล์เดียว” ไปเป็น “ดาวน์โหลด 500 ไฟล์โดยไม่ทำให้เทอร์มินัลพัง” มันก็มีจุดที่ติดค้างได้ง่ายมาก ผมจะพาไล่ทีละส่วน ทั้ง flags ที่ควรรู้ คำสั่งแบบเป็นขั้นตอนที่ผมใช้จริง ข้อผิดพลาดที่คนพลาดบ่อยที่สุด และจุดที่ cURL ไปต่อไม่ไหวแล้ว — รวมถึงสิ่งที่ควรใช้แทน
cURL คืออะไร และทำไมคุณควรรู้จักมัน?
cURL คือเครื่องมือบรรทัดคำสั่งแบบฟรีและโอเพนซอร์ส สำหรับรับส่งข้อมูลไปยังหรือจากเซิร์ฟเวอร์ผ่าน URL มันรองรับ HTTP, HTTPS, FTP, SFTP และโปรโตคอลอื่น ๆ อีกยาวเป็นหางว่าว จึงโผล่ให้เห็นได้ตั้งแต่ในสคริปต์ bash, Dockerfile ไปจนถึง CI pipeline เบื้องหลัง คำสั่ง curl ที่คุณพิมพ์ในเทอร์มินัลจะใช้ libcurl ซึ่งเป็นไลบรารีภาษา C สำหรับงานรับส่งข้อมูลที่หลายแอปพลิเคชันและหลาย language binding ฝังเอาไว้ เช่น ส่วนขยาย cURL ของ PHP เป็นตัวอย่างหนึ่ง ขณะที่ Requests ของ Python เป็น HTTP client อีกตัวหนึ่งที่สร้างบน urllib3 ไม่ได้ใช้ libcurl
เวอร์ชันเสถียรล่าสุด ณ ตอนเขียนคือ curl 8.21.0 ซึ่งออกในเดือนมิถุนายน 2026 — แต่อย่าเพิ่งคิดว่า OS ของคุณจะติดตั้งบิลด์นี้มาด้วยเลย เวอร์ชันที่มากับดิสโทรมักจะตามหลัง upstream อยู่หลายเดือน หรือบางทีก็นานกว่านั้น ดังนั้นควรรัน curl --version ก่อนจะสรุปว่ามี flag อย่าง --parallel ให้ใช้แน่ ๆ
ทำไมต้องดาวน์โหลดไฟล์ด้วย cURL? กรณีใช้งานยอดนิยม
มีคนถามผมบ่อยเหมือนกันว่า ทำไมต้องใช้เครื่องมือบรรทัดคำสั่ง ทั้งที่เบราว์เซอร์ก็ดาวน์โหลดไฟล์ได้อยู่แล้ว คำตอบจริง ๆ คือ: เบราว์เซอร์ดีมาก จนกระทั่งคุณต้องการทำให้มันอัตโนมัติ
| กรณีใช้งาน | จุดเด่นของ cURL |
|---|---|
| ดาวน์โหลดไฟล์ไบนารีใน CI/CD pipeline | สั่งผ่านสคริปต์ได้ ไม่ต้องใช้ GUI |
| ดึง API response หรือไฟล์ export ของข้อมูล | รองรับ header แบบกำหนดเอง, auth, และการส่งต่อเอาต์พุต |
| ดาวน์โหลดไฟล์ขนาดใหญ่ต่อจากจุดที่ค้างผ่าน SSH | มีระบบ resume ในตัว (-C -) |
| ทำงานดาวน์โหลดซ้ำตามรอบอัตโนมัติ (cron job) | เบา และต่อกับ shell script ได้ดี |
| ดึงไฟล์ที่ต้องยืนยันตัวตนก่อนเข้าถึง | มีตัวเลือก auth ที่ยืดหยุ่น (basic, token, cookies, .netrc) |
การดาวน์โหลดผ่านเบราว์เซอร์คือการกดแบบครั้งเดียวจบ ส่วน cURL เปลี่ยนการทำงานแบบเดิมให้กลายเป็นสิ่งที่คุณตั้งเวลาได้ ต่อเข้ากับ pipeline ได้ ลองใหม่เมื่อพลาดได้ และรันเหมือนกันได้เป็นร้อยเซิร์ฟเวอร์พร้อมกัน นั่นแหละเสน่ห์ของมัน — ไม่ได้หวือหวากว่า แค่ทำซ้ำได้

Flags สำคัญของ cURL สำหรับการดาวน์โหลดไฟล์
ผมมักกลับมาใช้ flags ชุดเดิมราวสิบกว่าตัวอยู่เสมอสำหรับงาน 90% ที่ทำอยู่ นี่คือ cheat sheet ที่ผมหวังว่าใครสักคนจะยื่นให้ผมตั้งแต่เมื่อหลายปีก่อน แยกตามหน้าที่จริง ๆ ของมัน
Flags สำหรับกำหนดเอาต์พุตและการบันทึกไฟล์
-O(--remote-name) บันทึกไฟล์โดยใช้ชื่อจากท้าย URL เป็นชื่อไฟล์ สะดวกดี แต่ก็อาจเขียนทับไฟล์เดิมที่ชื่อซ้ำกันได้แบบไม่เตือน-o <filename>(--output) ให้คุณกำหนดชื่อไฟล์ปลายทางเองได้ชัดเจน:curl -o report.pdf https://example.com/downloads/file.pdf-J(--remote-header-name) ใช้ชื่อไฟล์จาก headerContent-Dispositionของเซิร์ฟเวอร์แทนชื่อจาก URL ตัวนี้สะดวกมากสำหรับดาวน์โหลดจาก API แต่ควรมองชื่อไฟล์จากฝั่งเซิร์ฟเวอร์ว่าไม่น่าเชื่อถือ — ควรดาวน์โหลดลงโฟลเดอร์เฉพาะ ไม่ใช่โฟลเดอร์บ้านของคุณ ตามคำแนะนำด้านความปลอดภัยของ curl เอง
Flags ด้านพฤติกรรมที่ทุกการดาวน์โหลดควรมี
-L(--location) สั่งให้ curl ตาม HTTP redirect ถ้าไม่มีตัวนี้ การตอบกลับแบบ 3xx จะถูกบันทึกเป็นหน้า HTML redirect ขนาดจิ๋วแทนไฟล์จริง ซึ่งเป็นความผิดพลาดที่ผมเจอบ่อยที่สุดแบบ “ทำไมดาวน์โหลดแล้วพัง”-C -(--continue-at -) ใช้ดาวน์โหลดต่อจากจุดที่หยุดค้างไว้-s/-Sทำงานแบบเงียบ แต่ยังแสดง error อยู่ — เหมาะกับสคริปต์ที่ไม่อยากให้ progress bar ไปรก log--limit-rate 1Mจำกัดความเร็วการใช้แบนด์วิดท์ (มีประโยชน์บนเครือข่ายร่วมใช้ หรือเวลาที่ไม่อยากกินเน็ต metered มากเกินไป)--connect-timeout 10และ--max-time 300ป้องกันการเชื่อมต่อค้างจนสคริปต์หยุดคาอยู่ตลอด--retry 3และ--retry-delay 5ลองใหม่อัตโนมัติเมื่อเจอความล้มเหลวชั่วคราว — ตาม curl man page ควรจับคู่กับ--retry-all-errorsเฉพาะตอนที่การส่งคำขอเดิมซ้ำปลอดภัยจริง ๆ เท่านั้น
Flags สำหรับแสดงความคืบหน้าและดีบัก
-#แสดง progress bar แบบง่าย แทนตารางสถิติเริ่มต้น-vแสดงรายละเอียด verbose รวมถึง header ของ request/response แบบครบ ๆ — เป็นตัวที่ผมใช้ประจำเวลาอะไรเริ่มงอแง-I(--head) ดึงเฉพาะ response headers เป็นเช็กก่อนเริ่มดาวน์โหลดก้อนใหญ่ได้ดีมาก-wให้พิมพ์เอาต์พุตแบบกำหนดเองหลังจบการรับส่ง เช่นcurl -o /dev/null -s -w "%{http_code}\n" <url>เพื่อเช็กแค่ status code
ก่อนเริ่มต้น
- ระดับความยาก: มือใหม่ถึงระดับกลาง (ส่วน batch และ auth จะยกระดับขึ้นนิดหน่อย)
- เวลาที่ใช้: ประมาณ 15-20 นาทีสำหรับไล่คำสั่งหลักให้ครบ
- สิ่งที่ต้องมี: เทอร์มินัล (macOS Terminal, Linux shell หรือ Windows PowerShell/WSL), ติดตั้ง curl แล้ว (เช็กด้วย
curl --version) และ URL สำหรับทดสอบ — ผมจะใช้ GitHub release asset แบบสาธารณะเป็นตัวอย่าง เพราะเสถียรและเข้าถึงได้ฟรี
วิธีดาวน์โหลดไฟล์ด้วย cURL แบบทีละขั้นตอน
ขั้นที่ 1: ดาวน์โหลดไฟล์เดี่ยว
พื้นฐานที่สุด: curl -O <url> จะบันทึกไฟล์ด้วยชื่อเดิมจากปลาย URL ส่วน curl -o myfile.zip <url> ให้คุณเปลี่ยนชื่อไฟล์ตอนดาวน์โหลดได้
curl -LO https://github.com/curl/curl/releases/download/curl-8_21_0/curl-8.21.0.tar.gz
ตอนนี้ผมใส่ -L เป็นค่าเริ่มต้นเสมอ ไม่มีข้อยกเว้น — ผมเคยเจ็บมาแล้วหลายครั้งจาก redirect ที่เงียบ ๆ ทำให้สิ่งที่คิดว่าเป็น “ไฟล์ดาวน์โหลด” กลายเป็น HTML ขนาด 400 ไบต์ คุณควรเห็นตัวนับความคืบหน้าไหลขึ้นในเทอร์มินัล แล้วไฟล์จะไปอยู่ในไดเรกทอรีปัจจุบันของคุณ
เมื่อคำสั่งสำเร็จ progress meter จะไปถึง 100% และ curl-8.21.0.tar.gz จะปรากฏในโฟลเดอร์ปัจจุบัน ตรวจสอบไฟล์ก่อนนำไปใช้:
ls -lh curl-8.21.0.tar.gz
ขั้นที่ 2: ดาวน์โหลดแล้วเปลี่ยนชื่อไฟล์
ใช้ -o เมื่อคุณต้องการชื่อไฟล์ปลายทางเฉพาะ แทนชื่อที่ URL ลงท้ายมาให้:
curl -L -o curl-latest.tar.gz -S https://github.com/curl/curl/releases/download/curl-8_21_0/curl-8.21.0.tar.gz
-S ในที่นี้ช่วยเปิดการแสดง error กลับมา เผื่อว่าคุณจะใส่ -s ไว้ในสคริปต์ส่วนอื่นด้วย ชุดคำสั่ง -L -o <name> -S นี้แทบจะเป็นคำสั่งดาวน์โหลดไฟล์เดี่ยวประจำของผมเลย
ขั้นที่ 3: ดาวน์โหลดต่อจากไฟล์ที่หยุดค้าง
ถ้าดาวน์โหลดไฟล์ใหญ่แล้วหลุดกลางทาง (Wi‑Fi แย่, VPN กระตุก หรืออะไรก็ตาม) อย่าเริ่มใหม่ทั้งหมด ให้รัน:
curl -C - -LO https://example.com/large-file.iso
ข้อควรระวังคือ วิธีนี้จะใช้ได้ก็ต่อเมื่อเซิร์ฟเวอร์รองรับ byte-range request เท่านั้น Accept-Ranges: bytes เป็นสัญญาณบวกที่มีประโยชน์ แต่การไม่มีมันไม่ได้แปลว่าระบบไม่รองรับ range แน่ ๆ วิธีเช็กที่เชื่อถือได้คือดูการตอบกลับจริงจาก range request: ถ้า resume ได้ปกติ มักจะตอบกลับ 206 Partial Content พร้อม Content-Range ที่ถูกต้อง ลองรันคำสั่ง resume แล้วตรวจสถานะด้วย -v หรือ -D -; ถ้าเซิร์ฟเวอร์ไม่สนใจ range หรือปฏิเสธ offset ให้เริ่มใหม่อย่างตั้งใจ แทนที่จะเดาว่าไฟล์บางส่วนปลอดภัย

ขั้นที่ 4: ดาวน์โหลดพร้อมแถบความคืบหน้า หรือแบบเงียบ ๆ
ถ้าอยากให้ดูสะอาดในเทอร์มินัลแบบโต้ตอบ: curl -# -LO <url> ถ้าใช้กับสคริปต์และ cron job ที่อยากได้แค่ error ไม่อยากได้เสียงรบกวน: curl -sS -LO <url> ผมใช้แบบเงียบเกือบตลอด ยกเว้นตอนดีบักด้วยตัวเอง
ขั้นที่ 5: จำกัดความเร็วในการดาวน์โหลด
บนเครือข่ายสำนักงานที่แชร์กันหลายคน (หรือเวลาที่ผมไม่อยากเป็น “คนนั้น” ที่แย่งแบนด์วิดท์ตอนประชุมวิดีโอ) ผมจะจำกัดความเร็วแบบนี้:
curl --limit-rate 1M -LO https://example.com/big-dataset.zip
หน่วยคือ K, M, และ G สำหรับ kilobytes, megabytes, และ gigabytes ต่อวินาที ตามลำดับ
ขั้นที่ 6: บันทึก response headers ไว้คู่กับไฟล์
บางครั้งผมต้องการรู้ว่าเซิร์ฟเวอร์ตอบอะไรมาบ้างแบบเป๊ะ ๆ — content type, cache headers และเรื่องอื่น ๆ — โดยไม่ทำให้เทอร์มินัลรก:
curl -L -D headers.txt -o file.zip https://example.com/file.zip
คำสั่งนี้จะเก็บ response headers ไว้ใน headers.txt ขณะที่ไฟล์จริงจะถูกบันทึกเป็น file.zip เหมาะมากสำหรับดีบักปัญหา content-type ไม่ตรง หรือเช็กว่า CDN แคชข้อมูลตามที่คุณคิดไว้จริงไหม
เคล็ดลับและข้อผิดพลาดที่พบบ่อย
- เคล็ดลับ: ตั้ง
-Lเป็นค่าเริ่มต้นเสมอ ผมนึกไม่ออกจริง ๆ ว่ามันมีข้อเสียอะไร และผมเสียเวลากับการลืมใส่มันมาหลายชั่วโมงแล้ว - เคล็ดลับ: เวลาเขียนสคริปต์ ให้จับคู่
--failกับคำสั่งดาวน์โหลดด้วย เพื่อให้ response ที่ไม่ใช่ 2xx ทำให้สคริปต์จบพร้อม error จริง ๆ แทนที่จะบันทึก error page แบบเงียบ ๆ ราวกับเป็นไฟล์ที่ถูกต้อง - ข้อผิดพลาด: อย่าใช้
-C -คู่กับ--remove-on-errorเพราะ curl ระบุว่าทั้งสองอย่างไม่เข้ากัน เนื่องจากการ resume ต้องอาศัยไฟล์บางส่วนเดิมยังอยู่ - ข้อผิดพลาด:
-Oสามารถเขียนทับไฟล์ได้โดยไม่เตือน ถ้าคุณดาวน์โหลดหลายไฟล์ลงโฟลเดอร์ที่ใช้ร่วมกัน ควรใช้--output-dirเพื่อกันไฟล์ไว้เป็นที่
วิธีดาวน์โหลดหลายไฟล์และทำ batch download ด้วย cURL
ตัวอย่างไฟล์เดี่ยวเป็นส่วนที่ง่าย ส่วน workflow จริงที่ผมสร้างขึ้นมา — เช่น ดึงข้อมูล export ทุกคืน หรือซิงก์ไบนารีข้าม build server — ต้องใช้ concurrency และนี่คือจุดที่บทความส่วนใหญ่จบแบบดื้อ ๆ มีอยู่ 3 วิธีที่ควรรู้ โดยแต่ละวิธีก็ซับซ้อนขึ้นทีละนิด
วิธีที่ 1: ใส่หลาย URL ใน cURL คำสั่งเดียว
วิธีที่ง่ายที่สุดคือแค่ระบุ URL หลายตัว:
curl -LO https://example.com/a.zip -LO https://example.com/b.zip -LO https://example.com/c.zip
วิธีนี้ใช้ได้ แต่จะทำแบบเรียงลำดับ — curl จะดาวน์โหลดไฟล์หนึ่งให้เสร็จก่อนแล้วค่อยเริ่มไฟล์ถัดไป ไหวสำหรับ 3 ไฟล์ แต่ทรมานสำหรับ 300 ไฟล์
วิธีที่ 2: ดาวน์โหลดแบบขนานด้วย --parallel (curl 7.66+)
ตั้งแต่ curl 7.66 เป็นต้นมา คุณสามารถเพิ่ม --parallel (หรือ -Z) เพื่อดึงหลาย URL พร้อมกันได้:
curl --parallel --parallel-max 5 --remote-name-all \
https://example.com/a.zip https://example.com/b.zip https://example.com/c.zip
สิ่งที่ควรรู้คือ ค่า parallel max เริ่มต้นจริง ๆ คือ 50 ซึ่งเป็นจำนวนการเชื่อมต่อพร้อมกันที่มากเกินกว่าจะได้คำขอบคุณจากเซิร์ฟเวอร์ส่วนใหญ่ หรือจากเครือข่ายของคุณเอง ผมมักกำหนด --parallel-max เองแบบระวัง ๆ — โดยทั่วไปอยู่ที่ 4 ถึง 8 — แทนที่จะปล่อยตามค่า default
วิธีที่ 3: ใช้ xargs และ Bash loop เพื่อทำ concurrency จากรายการ URL
ถ้ามีรายการ URL จำนวนมากอยู่ในไฟล์ข้อความ ผมมักใช้ xargs:
cat urls.txt | xargs -n1 -P 8 curl -O -L
หรือถ้าอยากควบคุมงานของแต่ละรอบให้มากขึ้น จะใช้ bash loop ที่รันเป็น background process:
while read -r url; do
curl -O -L "$url" &
done < urls.txt
wait
wait ท้ายสุดสำคัญมาก — ถ้าไม่มีมัน สคริปต์จะออกก่อนที่การดาวน์โหลดพื้นหลังจะเสร็จ
เมื่อไหร่ควรใช้ wget หรือ aria2 แทน
พูดตรง ๆ เลย: cURL ไม่ใช่เครื่องมือที่เหมาะทุกกรณี ถ้าคุณต้องการ mirror โครงสร้างไดเรกทอรีทั้งเว็บไซต์ wget -r จะทำ recursive crawling ได้ในตัว ในแบบที่ cURL ไม่ได้ออกแบบมาเพื่อสิ่งนี้ ถ้าคุณต้องการดาวน์โหลดไฟล์ขนาดใหญ่มากแบบ multi-source และแบ่งส่วนเพื่อเอาความเร็วสูงสุด aria2c จะเร็วกว่าจริง
| เครื่องมือ | เหมาะที่สุดสำหรับ |
|---|---|
| cURL | ความแม่นยำ, การเขียนสคริปต์, ดาวน์โหลดไฟล์เดี่ยวหรือชุดเล็ก, การโต้ตอบกับ API |
| wget | ดาวน์โหลดเว็บไซต์แบบ recursive/mirror, ดึงไฟล์สาธารณะจำนวนมากแบบง่าย ๆ |
| aria2 | ดาวน์โหลดแบบ multi-source/segmented, ดันความเร็วสูงสุดสำหรับไฟล์ใหญ่ |
จุดแข็งของ cURL คือความแม่นยำและการประกอบต่อกันได้ดี — การ pipe, การเขียนสคริปต์, ความยืดหยุ่นของโปรโตคอล — ไม่ใช่การไล่เก็บแบบ brute force
วิธีดาวน์โหลดไฟล์ที่ต้องยืนยันสิทธิ์ด้วย cURL: รูปแบบการทำ auth
บทความ cURL ส่วนใหญ่มักหยุดที่ -u user:pass แล้วก็จบ นั่นเป็นแนวคิดจากยุคอินเทอร์เน็ตก่อนเก่า แต่ในปี 2026 ไฟล์ที่ผมดาวน์โหลดจริงมักมาจาก REST API, dashboard ที่ใช้ session, และระบบ CI — ซึ่งแต่ละอย่างต้องใช้ credential คนละแบบ
Basic Auth
curl -u username:password -O https://legacy-server.example.com/file.zip
ใช้ได้ดีกับเซิร์ฟเวอร์ FTP แบบเก่าหรือ HTTP endpoint ทั่วไป แต่ต้องรู้ไว้ว่ารหัสผ่านจะโผล่ใน shell history และ process list ถ้าไม่ระวัง — ซึ่งผมไม่แนะนำให้ใช้กับอะไรที่ละเอียดอ่อน
Bearer / OAuth Token Auth
นี่คือรูปแบบที่ในคู่มือจำนวนมากแทบไม่ค่อยพูดถึง ทั้งที่ผมใช้งานบ่อยที่สุดตอนนี้:
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
-LO https://api.github.com/repos/curl/curl/releases/assets/12345
นี่เป็นแพตเทิร์นจริงสำหรับดึง GitHub release asset แบบ private — แค่เปลี่ยน token กับ asset ID ของคุณ REST API และ resource ที่ป้องกันด้วย OAuth2 ส่วนใหญ่ในปัจจุบันพูดภาษานี้กันทั้งนั้น
Cookie-Based Session Auth
สำหรับเว็บแอปที่ล็อกอินแล้วสร้าง session ให้บันทึก cookie jar ตอนล็อกอิน และนำกลับมาใช้ตอนดาวน์โหลด:
curl -c cookies.txt -d "user=me&pass=secret" https://example.com/login
curl -b cookies.txt -O https://example.com/protected/file.zip
ไฟล์ .netrc สำหรับงานสคริปต์และ CI
นี่คือวิธีที่ผมชอบที่สุดสำหรับงานที่รันแบบไม่ต้องเฝ้าเอง สร้างไฟล์ ~/.netrc (หรือ _netrc บน Windows):
machine example.com
login myusername
password mypassword
ล็อกสิทธิ์ไฟล์ด้วย chmod 600 ~/.netrc แล้วเรียกใช้งานแบบนี้:
curl --netrc -LO https://example.com/protected-file.zip
ข้อดีคือ credential จะไม่ไปอยู่ใน shell history หรือ source ของสคริปต์ ซึ่งสำคัญจริง ๆ ใน CI/CD ที่มักจะมีการ log สคริปต์แบบเต็มบรรทัด
| วิธี auth | Flag/ตัวเลือก | เหมาะที่สุดสำหรับ |
|---|---|---|
| Basic auth | -u user:pass | FTP แบบเก่า, HTTP ทั่วไป |
| Bearer token | -H "Authorization: Bearer <token>" | REST API, OAuth2 |
| Cookie auth | -b cookies.txt (+ -c เพื่อบันทึก) | เว็บแอปที่ใช้ session |
ไฟล์ .netrc | --netrc หรือ --netrc-file | CI/CD, สภาพแวดล้อมที่รันด้วยสคริปต์ |

การแก้ปัญหา cURL ดาวน์โหลดไม่สำเร็จที่พบบ่อย
นี่คือส่วนที่ผมอยากให้มีตอนเริ่มต้นมากที่สุด เพราะแทบไม่มีใครอธิบายไว้ครบ “ทำไม curl ดาวน์โหลดไม่ได้” เป็นคำค้นหาที่เจอบ่อยและหงุดหงิดมาก และวิธีแก้ส่วนใหญ่ก็เป็นคำสั่งสั้น ๆ แค่บรรทัดเดียว ถ้ารู้สาเหตุแล้ว
| อาการ | สาเหตุที่เป็นไปได้ | วิธีแก้ |
|---|---|---|
curl: (60) SSL certificate problem | ใบรับรอง self-signed หรือหมดอายุ | --cacert <file> หรือ -k (ใช้เฉพาะ dev) |
403 Forbidden / ไฟล์ว่าง | เซิร์ฟเวอร์บล็อก user agent เริ่มต้นของ curl | -A "Mozilla/5.0..." หรือ -H "User-Agent: ..." |
ดาวน์โหลดเริ่มใหม่จาก 0 ตอนใช้ -C - | เซิร์ฟเวอร์ไม่รองรับ Range | เช็กด้วย curl -I <url> และดู Accept-Ranges: bytes |
| บันทึกไฟล์ขนาด 0 ไบต์ | ไม่ได้ตาม redirect | เพิ่ม flag -L |
curl: (28) Operation timed out | เซิร์ฟเวอร์ช้า หรือเน็ตมีปัญหา | --connect-timeout 10 --max-time 300 + --retry 3 |
| บันทึกเป็นหน้า HTML แทนไฟล์จริง | หน้านั้นต้องใช้ JavaScript ในการเรนเดอร์ | curl ใช้ JavaScript ไม่ได้ — ดูส่วนด้านล่าง |
ข้อผิดพลาด SSL Certificate: หมายความว่าอะไร และจะแก้ยังไง
Error 60 หมายความว่า curl ยืนยันใบรับรอง SSL ของเซิร์ฟเวอร์ไม่ได้ — โดยทั่วไปเพราะเป็น self-signed, หมดอายุ, หรือออกโดย CA ที่ curl ไม่ไว้ใจ ถ้าคุณควบคุมเซิร์ฟเวอร์เอง ให้ชี้ curl ไปยัง CA bundle ที่ถูกต้องด้วย --cacert /path/to/ca.pem ส่วน -k (--insecure) จะข้ามการตรวจสอบทั้งหมด ซึ่งพอใช้ได้ในสภาพแวดล้อม dev ในเครื่อง แต่เป็นความคิดที่แย่มากถ้าเกี่ยวข้องกับ production หรือข้อมูลผู้ใช้จริง
403 Forbidden และการดาวน์โหลดที่ว่างเปล่า
เซิร์ฟเวอร์จำนวนไม่น้อยบล็อกคำขอที่ระบุตัวเองว่าเป็น curl/8.21.0 (User-Agent เริ่มต้นของ curl) เพราะคิดว่าเป็นบอทหรือสแครปเปอร์ วิธีแก้ส่วนใหญ่คือแกล้งทำเป็นเบราว์เซอร์:
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -LO https://example.com/file.zip
ถ้าอยากเช็กว่าจริง ๆ แล้วเซิร์ฟเวอร์ตอบอะไรกลับมาก่อนจะดาวน์โหลดเต็ม ผมใช้: curl -o /dev/null -s -w "%{http_code}\n" <url>
Timeout, Retry และการเชื่อมต่อที่ไม่นิ่ง
นี่คือคำสั่งที่ผมอยากสักไว้บนแขน ถ้าผมกล้าพอจะสักจริง
คำสั่งดาวน์โหลดประจำของผม ซึ่งใช้ในสคริปต์โปรดักชันจริง ๆ จะรวม flags ด้านความเสถียรเข้าด้วยกันทั้งหมด:
curl -L -C - --retry 5 --retry-delay 3 --connect-timeout 10 --max-time 600 --fail -O <url>
มันคือการตาม redirect, resume, retry 5 ครั้งเว้น 3 วินาที, timeout การเชื่อมต่อ 10 วินาที, จำกัดเวลารวม 10 นาที, และ fail ทันทีเมื่อ HTTP status ไม่ดี — เรียกว่ารวมทุกอย่างที่ผมเรียนรู้มาด้วยความเจ็บปวดเอาไว้หมดแล้ว
cURL ในระบบอัตโนมัติจริง: CI/CD pipeline, การ pipe และความปลอดภัยของสคริปต์
ส่งเอาต์พุตของ cURL ไปยังเครื่องมืออื่นผ่าน pipe
curl ไม่จำเป็นต้องบันทึกอะไรลงดิสก์เลยด้วยซ้ำ — การส่งต่อไปยังคำสั่งอื่นโดยตรงเป็นหนึ่งในความสามารถที่คนมักมองข้ามมากที่สุด:
curl -sL https://example.com/archive.tar.gz | tar xz
curl -s https://api.example.com/data | jq '.results'
ดาวน์โหลดแล้วแตกไฟล์ หรือดาวน์โหลดแล้ว parse ในบรรทัดเดียว นี่คือแพตเทิร์นที่ผมใช้บ่อยมากสำหรับการดึงข้อมูลแบบครั้งคราว
ใช้ cURL ใน GitHub Actions และ CI/CD
ตัวอย่างขั้นตอน GitHub Actions แบบเรียบง่ายที่ดาวน์โหลด binary พร้อม logic สำหรับ retry และ fail ชัดเจนเมื่อเกิดปัญหา:
- name: Download binary
run: |
curl -L --fail --retry 3 --retry-delay 5 \
-o app-binary "https://example.com/releases/app-binary"
ให้เก็บ token ไว้เป็น CI secret แล้วเรียกผ่าน environment variable — อย่า hardcode ลงในสคริปต์โดยตรง และใช้ --fail (หรือ --fail-with-body ถ้าคุณต้องการเห็น error body สำหรับดีบัก) เพื่อให้การดาวน์โหลดที่เสียจริง ๆ ทำให้ build ล้มจริง แทนที่จะผ่านแบบเงียบ ๆ พร้อมข้อมูลขยะ
คำถามด้านความปลอดภัยของ curl | sh
เรื่องนี้โผล่ในแทบทุกฟอรัมของนักพัฒนาที่ผมเคยอ่านมา และมีเหตุผลชัดเจน: การ pipe curl เข้า sh โดยตรงหมายถึงการรันโค้ดจากระยะไกลที่คุณยังไม่ได้ตรวจดู โดยอิงแค่ความเชื่อว่าเซิร์ฟเวอร์ไม่ได้ถูกเจาะ และการเชื่อมต่อไม่ได้ถูกดัดแปลง นั่นแหละคือความเสี่ยงจริง ๆ — ไม่ใช่ความระแวง แต่เป็นความเสี่ยงด้าน supply chain แบบตรงไปตรงมา
แพตเทิร์นที่ปลอดภัยกว่าคือดาวน์โหลดมาก่อน ตรวจดูสคริปต์ ตรวจสอบ checksum หรือ GPG signature ถ้ามีให้ แล้วค่อยรัน:
curl -sL https://example.com/install.sh -o install.sh
cat install.sh # อ่านจริง ๆ ก่อน
sha256sum install.sh # เทียบกับ checksum ที่เผยแพร่ ถ้ามี
bash install.sh
ตัวติดตั้งที่เป็นที่รู้จักอย่าง rustup และ Homebrew ก็ยังใช้แพตเทิร์น curl | sh อยู่ และโดยทั่วไปก็ยอมรับกันได้ในกรณีเฉพาะนั้น เพราะผู้ดูแลและช่องทางแจกจ่ายมีความน่าเชื่อถือสูง แต่ผมก็ยังอยากเสียเวลาเพิ่มอีกสิบวินาทีเพื่ออ่านสคริปต์ ดีกว่ามาเสียใจทีหลัง
เมื่อ cURL ยังไม่พอ: หน้าเว็บที่เรนเดอร์ด้วย JS, เว็บไซต์กันบอท และข้อมูลแบบมีโครงสร้าง
นี่คือความล้มเหลวแบบหนึ่งที่ทำให้หลายคนงง และส่วนใหญ่ไม่ใช่ความผิดของเขาด้วย: คุณสั่ง curl -O ไปที่หน้าที่ดูเหมือนเว็บธรรมดา แต่แทนที่จะได้เนื้อหาที่คาดไว้ กลับได้ HTML เปล่า ๆ, หน้า challenge ของ Cloudflare, หรืออะไรที่ดูไร้ระเบียบ curl ทำสิ่งที่มันถูกออกแบบมาให้ทำอย่างถูกต้องแล้ว — มันแค่ดึง raw HTTP response แต่ curl ไม่สามารถรัน JavaScript, แก้ CAPTCHA, หรือฝ่าระบบ fingerprinting ป้องกันบอทได้ สิ่งเหล่านั้นไม่ใช่บั๊กของ curl เลย แต่เป็นงานที่อยู่นอกขอบเขตหน้าที่ของมันทั้งหมด
ทำไม cURL ถึงล้มเหลวบนหน้าเว็บยุคใหม่
เว็บแอปแบบ single-page จำนวนมากส่งกลับมาแค่ HTML skeleton ที่แทบว่างเปล่า แล้วให้ JavaScript เรนเดอร์เนื้อหาจริงฝั่ง client หลังโหลดหน้าเสร็จ — ซึ่ง curl ไม่มีทางรันส่วนนี้ได้ นอกจากนี้ ระบบอย่าง Cloudflare และ Akamai ยังเสิร์ฟ challenge page ให้กับอะไรก็ตามที่ไม่ดูเหมือนเบราว์เซอร์จริง และคำขอ curl ซ้ำ ๆ จาก IP เดิมก็อาจถูกจำกัดอัตราหรือถูกจัดว่าเป็นทราฟฟิกจากบอทได้เร็วมาก
ก้าวต่อไป: AI Scraping API สำหรับนักพัฒนา
ถ้าถามผม cURL เหมาะกับราว 80% ของงานดาวน์โหลดไฟล์และข้อมูลทั้งหมดที่มีอยู่ — asset แบบสแตติก, API response, อะไรก็ตามที่เสิร์ฟเป็น resource HTTP ปกติ ส่วนอีก 20% ที่เป็นหน้าหนัก JavaScript หรือมีการป้องกันบอท คือจุดที่ผมเห็นนักพัฒนาเสียเวลาหลายชั่วโมงไปกับการสู้ header และ user-agent ก่อนจะยอมแพ้และเปลี่ยนไปใช้ชั้นเครื่องมือแบบอื่น
นั่นแหละคือช่องว่างที่ทีมของเราออกแบบ Thunderbit มาเพื่อเติมเต็ม ควบคู่ไปกับ Chrome extension ที่หลายคนรู้จักเราอยู่แล้ว ฝั่งนักพัฒนา Thunderbit มี Open API ที่ให้ POST /distill ซึ่งส่งกลับ Markdown ที่สะอาด พร้อมใช้งานกับ LLM จาก URL โดยให้บริการเรนเดอร์หน้าเว็บให้เรียบร้อย และ POST /extract ที่ส่งกลับ structured JSON ตาม schema เมื่อคุณต้องการข้อมูลแบบมีฟิลด์จริง ๆ แทนข้อความที่อ่านง่าย นอกจากนี้ยังมี MCP server ให้เอเจนต์ใน Claude หรือ Cursor เรียก thunderbit_distill และ thunderbit_extract ระหว่างทำงานได้ และมี CLI (npx @thunderbit/thunderbit-cli distill <url>) ที่ให้ประสบการณ์คล้าย curl ในเทอร์มินัลของคุณ คุณสามารถ pipe เอาต์พุต JSON ไปยัง jq ได้ เช่น thunderbit distill <url> --format json | jq -r '.data.markdown'; หรือส่งเอาต์พุต --format markdown ไปยังเครื่องมือจัดการข้อความหรือบันทึกลงไฟล์แทน
ถ้าเทียบกันชัด ๆ ความต่างเห็นได้เลย คำขอ curl ไปยังหน้า product ที่เรนเดอร์ด้วย JS อาจคืนค่าเป็น <div id="root"></div> ที่เกือบว่างเปล่า แต่คำสั่ง thunderbit distill ที่เทียบเท่ากันจะคืนเนื้อหาหน้าเว็บเป็น Markdown ที่สะอาด Distill ใช้ 1 เครดิตต่อ URL และ Extract ใช้ 20 เครดิตต่อ URL ขีดจำกัดของแต่ละ endpoint ในปัจจุบันก็แตกต่างกัน: Batch Distill รองรับได้ถึง 100 URL ต่อหนึ่งงาน ขณะที่ Batch Extract รับได้สูงสุด 50 URL ด้วย schema เดียวกัน ควรตรวจเอกสาร API ล่าสุดก่อนวางขนาดคิวงานในโปรดักชัน
ถ้าคุณยังใหม่กับแนวคิดนี้ บทความอธิบายของเราเรื่อง web scraping คืออะไรจริง ๆ เป็นจุดเริ่มต้นที่ดี และ คู่มือ scraping แบบไม่ต้องเขียนโค้ด จะอธิบายอีกมุมหนึ่งของปัญหาเดียวกันสำหรับคนในทีมที่ไม่อยากแตะเทอร์มินัล สำหรับการเทียบเครื่องมือในตลาดแบบกว้างขึ้น เรายังรวบรวม AI web scraper ที่ดีที่สุด ไว้อ่านประกอบด้วย
สรุปด่วน: cheat sheet สำหรับดาวน์โหลดด้วย cURL
| งาน | คำสั่ง |
|---|---|
| ดาวน์โหลดพื้นฐาน | curl -LO <url> |
| ตั้งชื่อไฟล์เอง | curl -L -o myfile.zip <url> |
| ดาวน์โหลดต่อจากไฟล์ที่ค้าง | curl -C - -LO <url> |
| เงียบ แต่ยังแสดง error | curl -sSL -O <url> |
| ดาวน์โหลดแบบขนาน | curl --parallel --parallel-max 5 -O <url1> -O <url2> |
| ใช้ Bearer token auth | curl -H "Authorization: Bearer <token>" -LO <url> |
| คำสั่งดาวน์โหลดสำหรับสคริปต์ที่ผมใช้บ่อย | curl -LO --retry 5 --retry-delay 3 --max-time 600 --fail <url> |
| pipe ไปยังเครื่องมือแยกข้อมูล | curl -sL <url> | tar xz |
บทสรุปและประเด็นสำคัญ
การดาวน์โหลดไฟล์ด้วย curl เริ่มต้นง่าย — แค่ curl -O ก็แทบจบแล้ว — แต่ทักษะจริงอยู่ในชั้นถัดลงไป: รู้ว่าเมื่อไหร่ต้องเติม -L, เมื่อไหร่ควร resume แทนเริ่มใหม่, auth แบบไหนเข้ากับ workflow ของคุณจริง ๆ, และควรทำอย่างไรเมื่อ 403 หรือ HTML เปล่า ๆ โผล่มาแทนไฟล์ที่หวังไว้ ผมเคยพึ่งทุกแพตเทิร์นที่ว่ามานี้มาแล้วสักครั้งหนึ่ง โดยมากคือหลังจากเรียนรู้บทเรียนแบบเจ็บ ๆ ว่ามันสำคัญแค่ไหน
cURL ยังคงเป็นเครื่องมือหลักของผมสำหรับการดาวน์โหลดไฟล์ตรงไปตรงมาและงาน HTTP ที่เขียนสคริปต์ได้ — มันเร็ว มีอยู่แทบทุกที่ และต่อเข้ากับ shell pipeline อื่น ๆ ได้อย่างสวยงาม แต่เมื่อคุณเจอหน้าเว็บที่เรนเดอร์ด้วย JavaScript หรือกำแพงกันบอท นั่นไม่ใช่ปัญหาที่จะโซ้ยด้วย flags เพิ่มอีกนิด แต่มันคือสัญญาณว่าคุณต้องใช้ชั้นเครื่องมืออีกแบบ และนั่นคือจุดที่ API อย่าง Thunderbit เข้ามารับช่วงต่อ โดยไม่บังคับให้คุณต้องออกจากเทอร์มินัล
บันทึก cheat sheet นี้ไว้ ลองคำสั่ง retry-and-resume ในการดาวน์โหลดครั้งหน้าที่ไม่เสถียร และถ้าคุณเจอกำแพงที่ cURL คืนข้อมูลขยะออกมาแทน คุณก็รู้แล้วว่าก้าวต่อไปหน้าตาเป็นยังไง — หน้า pricing ของ Thunderbit มีรายละเอียดเครดิตล่าสุด ถ้าคุณอยากดูว่าการยกระดับไปใช้แบบนั้นต้องจ่ายเท่าไร และ ช่อง YouTube ของเราก็มีวิดีโอ walkthrough ถ้าคุณอยากดูมากกว่าอ่าน
คำถามที่พบบ่อยเกี่ยวกับการดาวน์โหลดไฟล์ด้วย cURL
จะดาวน์โหลดไฟล์ด้วย cURL และตั้งชื่อเฉพาะได้อย่างไร?
ใช้ -o ตามด้วยชื่อไฟล์ที่ต้องการ: curl -L -o yourname.ext <url> อย่าลืมเพิ่ม -L เพื่อไม่ให้ redirect ทำให้การดาวน์โหลดล้มเหลว
จะ resume การดาวน์โหลด cURL ที่ล้มเหลวได้อย่างไร?
รัน curl -C - -LO <url> วิธีนี้จะใช้ได้ก็ต่อเมื่อเซิร์ฟเวอร์รองรับ range request — ตรวจสอบก่อนด้วย curl -I <url> แล้วมองหา Accept-Ranges: bytes ใน response
cURL ดาวน์โหลดไฟล์ที่ต้องล็อกอินได้ไหม?
ได้ และมี 4 วิธีหลัก: basic auth (-u user:pass), bearer token (-H "Authorization: Bearer <token>"), session แบบ cookie (-b cookies.txt), หรือไฟล์ .netrc สำหรับสภาพแวดล้อมที่ใช้สคริปต์ ดูหัวข้อ authentication ด้านบนสำหรับรายละเอียดครบ ๆ และดูว่าแต่ละแบบเหมาะกับอะไร
ความต่างระหว่าง cURL กับ wget สำหรับดาวน์โหลดไฟล์คืออะไร?
cURL รองรับโปรโตคอลได้มากกว่า และโดยทั่วไปเหมาะกับการเขียนสคริปต์, การ pipe, และการดาวน์โหลดไฟล์เดี่ยวหรือชุดเล็กแบบแม่นยำกว่า ส่วน wget ถูกออกแบบมาสำหรับการ crawl แบบ recursive และ mirror ทั้งไดเรกทอรีของเว็บ จึงเหมาะกว่าสำหรับการดึงไฟล์สแตติกจำนวนมาก
ทำไม cURL ถึงดาวน์โหลดหน้า HTML แทนที่จะเป็นไฟล์จริง?
มีผู้ต้องสงสัยหลักสองราย: คุณลืมใส่ -L แล้วเซิร์ฟเวอร์พาไปยังที่อื่น หรือหน้านั้นต้องใช้ JavaScript เพื่อเรนเดอร์เนื้อหาจริง ซึ่ง curl ทำไม่ได้อยู่แล้ว ในกรณีหลัง คุณจะต้องใช้เครื่องมือที่เรนเดอร์หน้าเว็บได้ แทนที่จะเพิ่ม flags ให้ curl ต่อไป


