ผมเริ่มใช้ Cloudflare Tunnel ตอนที่อยากเข้า dashboard จากนอกบ้านแต่ ISP ไม่ให้ public IP — ไม่ต้องยุ่งกับ port forwarding อีกเลย
Series: เข้าเครื่องจากนอกบ้าน (3 ส่วน)
- Remote Access: Expose Services with Cloudflare Tunnel (1/3) — ติดตั้ง cloudflared สร้าง tunnel ทดสอบ expose localhost (บทความนี้)
- Remote Access: Auto-start After Reboot with systemd (2/3) — systemd คืออะไร ประยุกต์กับ cloudflared
- Remote Access: Restrict Access with Cloudflare Access (3/3) — Email OTP ผ่าน Zero Trust
แพลตฟอร์ม:
cloudflaredติดตั้งและรัน tunnel ได้บน Linux, macOS และ Windows — ชุดบทความนี้เขียนสำหรับ Linux server ที่บ้าน เป็นหลัก (Ubuntu/Debian) คำสั่งและ path ในขั้นตอนถัดไปอ้างอิง Linux
ทำไมต้อง Cloudflare Tunnel?
ถ้าคุณรัน service อยู่ที่บ้าน แล้วอยากให้คนอื่น (หรือตัวเอง) เข้าถึงได้จากอินเทอร์เน็ต ปกติต้องทำ 2 อย่าง — ก่อนอื่นขออธิบายคำเหล่านี้ก่อน
ISP คืออะไร? (และ CG-NAT)
ISP (Internet Service Provider) คือ ผู้ให้บริการอินเทอร์เน็ต — บริษัทหรือหน่วยงานที่คุณจ่ายค่าบริการเพื่อให้บ้านหรือออฟฟิส ต่อเน็ตได้ (เช่น ไฟเบอร์บ้าน, เน็ตมือถือ 4G/5G)
ISP กำหนดนโยบายว่าบ้านคุณได้ public IP จริง หรือไม่ — สรุปสั้น ๆ: Router จัดการเน็ตในบ้าน ISP จัดการว่าบ้านคุณต่อกับอินเทอร์เน็ตโลกยังไง
หลาย ISP ใช้ CG-NAT (Carrier-Grade NAT) — NAT อีกชั้นที่ ISP ทำให้ ไม่ใช่แค่ Router ในบ้าน หลายร้อยหลายพันบ้านอาจแชร์ public IP เดียวกันที่ฝั่ง ISP:
flowchart LR
Internet[Internet]
ISP["CG-NAT: ISP public IP\n(shared by many homes)"]
Router[Your home router]
Device["PC / NAS\n(private IP)"]
Internet --> ISP --> Router --> Device

ในแบบ public IP จริง บ้านคุณได้ public IP เป็นของตัวเอง — ตั้ง port forward ที่ Router แล้วคนนอกเข้ามาได้ (ถ้าเปิด port)
ในแบบ CG-NAT ISP แจก “WAN IP” ให้ Router ที่ เข้าถึงจากอินเทอร์เน็ตโดยตรงไม่ได้ (มักเป็นเลขช่วง 100.64.x.x) — ขอ public IP จริงหรือ static IP ยาก บางเจ้าไม่ให้หรือคิดเงินเพิ่ม และ port forward ที่ Router ไม่ช่วย เพราะคนนอกยิงเข้ามาไม่ถึงบ้านคุณโดยตรง
เช็คง่าย ๆ: ดู WAN IP บน Router แล้วเทียบกับ “What is my IP” จากเว็บ — ถ้าไม่ตรงกัน มักอยู่หลัง CG-NAT
Public IP คืออะไร?
IP address คือ “เลขบ้าน” บนอินเทอร์เน็ต ทุกเครื่องที่ออกเน็ตจะมี IP แต่ไม่ใช่ทุก IP ที่คนนอกเข้าถึงได้โดยตรง
- Private IP (เช่น
192.168.1.50,10.0.0.5) — ใช้ภายในบ้านหรือออฟฟิศเท่านั้น laptop, PC, NAS ในเครือข่ายเดียวกันใช้เลขช่วงนี้ คนนอกอินเทอร์เน็ตโยน packet มาที่เลขนี้ตรง ๆ ไม่ได้ - Public IP — ที่อยู่ที่อินเทอร์เน็ตมองเห็น มักเป็นของ Router หรือของ ISP ถ้าเพื่อนจะเข้าเว็บหรือเกมที่รันอยู่บ้านคุณ ต้องรู้ public IP (หรือ domain ที่ชี้มาที่ IP นี้)
ที่บ้านส่วนใหญ่ Router ทำ NAT ให้เครื่องในบ้านหลายเครื่องแชร์ public IP เดียว — โทรศัพท์, laptop, NAS ใช้ private IP ภายใน แต่ออกเน็ตผ่าน public IP ของบ้านคุณ (ถ้า ISP ไม่ใช้ CG-NAT ตามที่อธิบายด้านบน)
Port forwarding คืออะไร?
แม้มี public IP แล้ว traffic จากอินเทอร์เน็ตก็มาที่ Router ก่อน ไม่ใช่เครื่องในบ้านโดยตรง Port forwarding คือการตั้ง Router ให้ “ส่งต่อ” traffic ที่เข้ามาที่ port หนึ่ง ไปยังเครื่องและ port ภายในบ้าน
sequenceDiagram
actor User as User (Internet)
participant Router as Home router
participant PC as PC / NAS
User->>Router: Connect to public IP :port
Note over Router: Port forwarding rule
Router->>PC: Forward to private IP :port
PC-->>Router: Response
Router-->>User: Response

ตัวอย่างที่หลายคนเคยทำ: เปิดห้อง Warcraft III (WC3) ให้เพื่อน join จากนอกบ้าน
- คุณ host เกมบน PC (
192.168.1.50) — เกมฟัง port 6112 (TCP/UDP) - เพื่อนนอกบ้านต้องต่อมาที่ public IP ของบ้าน ไม่ใช่
192.168.1.50 - ใน Router ตั้ง rule เช่น External port 6112 →
192.168.1.50:6112 - เพื่อนใส่ public IP (หรือ DDNS) แล้ว join ห้องได้
แนวเดียวกันกับเปิดเว็บจากบ้าน: forward port 443 จาก Router ไปที่เครื่องที่รัน service (เช่น 192.168.1.50:3000) — แต่ต้องเปิดช่องให้โลกภายนอกยิงเข้ามาได้ จึงเสี่ยงด้านความปลอดภัยมากกว่า tunnel
สรุปแล้ว วิธีเดิมต้องทำ 2 อย่าง:
- ขอ public IP จาก ISP (บางเจ้าคิดเงินเพิ่ม หรือไม่ให้เลย โดยเฉพาะเจ้าที่ใช้ CG-NAT)
- ตั้ง port forwarding บน Router (เปิดช่องโหว่ด้านความปลอดภัย)
Cloudflare Tunnel แก้ทั้งสองปัญหาด้วยวิธีกลับกัน: แทนที่จะรอรับ connection จากข้างนอก เซิร์ฟเวอร์บ้านเป็นฝ่ายเปิด connection ออกไปหา Cloudflare เอง
sequenceDiagram
actor User
participant CF as Cloudflare Edge
participant Tunnel as cloudflared
participant Server as Local Server
User->>CF: https://app.example.com
CF->>Tunnel: Forward request via tunnel
Tunnel->>Server: Forward to localhost:3000
Server-->>Tunnel: Response
Tunnel-->>CF: Return via tunnel
CF-->>User: Display page

Prerequisites
ก่อนเริ่มต้องมี:
- Cloudflare account (ฟรี)
- Domain ที่ใช้ Cloudflare เป็น nameserver — บทนี้ทำ named tunnel ซึ่งเอกสารทางการกำหนดว่าต้องเพิ่ม site ใน Cloudflare และชี้ nameserver มาก่อน (Cloudflare account ฟรี แต่ domain ต้องซื้อเอง) — ถ้าไม่อยากมี domain ใช้ quick tunnel ได้ (เปิดด้วย
cloudflared tunnel --url http://localhost:3000ได้ URL*.trycloudflare.comฟรี แต่ URL จะเปลี่ยนทุกครั้งที่รันใหม่ และต่อ Cloudflare Access ใน post 40 ไม่ได้) - Linux server ที่บ้าน — guide นี้ใช้ Ubuntu/Debian (ทดสอบบน Ubuntu 24.04)
- Service ที่รันอยู่บน localhost ที่อยากจะ expose (ตัวอย่างใช้
localhost:3000) - เครื่องต้องต่อ Cloudflare ที่ outbound port 7844 ได้ (TCP/UDP) — ถ้าอยู่หลัง firewall เข้ม ตรวจก่อน (connectivity pre-checks)
Step 1: ติดตั้ง cloudflared
cloudflared คือ daemon ที่รันบนเซิร์ฟเวอร์และเปิด tunnel ออกไปหา Cloudflare
ติดตั้งได้หลายวิธี — บน Linux มักใช้ .deb package จาก cloudflared releases ส่วน guide นี้ผมใช้ Homebrew (ดูการใช้งาน Homebrew ที่ Homebrew, Cask and mas-cli):
brew install cloudflared
ตรวจสอบว่าติดตั้งสำเร็จ:
cloudflared --version
# cloudflared version 2025.x.x
Step 2: Login เข้า Cloudflare
cloudflared tunnel login
คำสั่งนี้จะพิมพ์ URL ออกมา ให้คลิก URL นั้นใน browser แล้ว login ด้วย Cloudflare account จากนั้นเลือก domain ที่ต้องการใช้กับ tunnel
เมื่อ authorize สำเร็จ จะมีไฟล์ ~/.cloudflared/cert.pem ถูกสร้างขึ้น:
ls -la ~/.cloudflared/cert.pem
Tip: ถ้า Cloudflare บอกว่าจะ download cert ให้แทน ให้ save ไฟล์นั้นเป็น
~/.cloudflared/cert.pemด้วยตัวเอง สิ่งนี้เกิดขึ้นได้เมื่อ browser และ cloudflared อยู่คนละเครื่อง
Step 3: สร้าง Tunnel
cloudflared tunnel create my-home-server
Output จะบอก Tunnel ID (UUID) และ path ของ credentials file บันทึก Tunnel ID ไว้ใช้ขั้นตอนต่อไป ดู tunnel ที่มีทั้งหมดด้วย:
cloudflared tunnel list
Step 4: ชี้ DNS ไปที่ Tunnel
cloudflared tunnel route dns my-home-server app.yourdomain.com
คำสั่งนี้สร้าง CNAME record ใน Cloudflare DNS อัตโนมัติ โดยชี้ app.yourdomain.com ไปที่ tunnel UUID
Step 5: สร้าง Config File
สร้างไฟล์ ~/.cloudflared/config.yml:
tunnel: <tunnel-uuid>
credentials-file: /home/your-username/.cloudflared/<tunnel-uuid>.json
ingress:
- hostname: app.yourdomain.com
service: http://localhost:3000
- service: http_status:404
บรรทัดสุดท้าย service: http_status:404 เป็น catch-all ที่ต้องมีเสมอ
Note: ใช้ absolute path สำหรับ
credentials-fileเสมอ — relative path มักทำให้ tunnel ขึ้นแต่เว็บไม่โหลด
Step 6: ทดสอบ Tunnel
cloudflared tunnel run my-home-server
เปิด browser แล้วเข้า https://app.yourdomain.com — ถ้าเห็นหน้าเว็บของ service แสดงว่า tunnel ทำงานได้แล้ว
Step 7: ให้ tunnel รันต่อเนื่อง (cloudflared service install)
ถ้าปิด terminal ที่รัน cloudflared tunnel run อยู่ tunnel ก็หยุด — บน Linux ถ้าติดตั้ง cloudflared แบบ system package (เช่น .deb) สามารถให้ตัว cloudflared สร้าง systemd unit ชื่อ cloudflared ให้ได้ (เอกสาร Cloudflare)
สำคัญ: อย่ารันแค่ sudo cloudflared service install ถ้า config อยู่ที่ ~/.cloudflared/config.yml — ตอนใช้ sudo ค่า $HOME จะเป็น /root ทำให้หา config ไม่เจอ (error ประมาณ Cannot determine default configuration path) ต้อง ระบุ path config ชัดเจน:
sudo cloudflared --config /home/your-username/.cloudflared/config.yml service install
sudo systemctl start cloudflared
ตรวจสอบ:
systemctl status cloudflared
ถ้าหลัง reboot tunnel ไม่ขึ้นเอง ให้ sudo systemctl enable cloudflared (บางเวอร์ชัน service install enable ให้แล้ว)
ทางเลือกอื่นที่ community ใช้: copy config.yml (และ credentials ถ้าจำเป็น) ไป /etc/cloudflared/ แล้วค่อย service install — แต่ทาง --config ด้านบนตรงกับที่ Cloudflare แนะนำ
ทุกครั้งที่แก้ config ให้ restart:
sudo systemctl restart cloudflared
หมายเหตุ: ขั้นตอนนี้ผมอ้างจาก เอกสารทางการ และ issue ที่รายงานบ่อย (cloudflared#1457) — ยังไม่ได้รันทดสอบบนเครื่อง production เอง ถ้า
service installยัง fail อาจต้องสร้าง unit เอง (วิธีนั้นเสถียรกว่าสำหรับ Homebrew)
ติดตั้งผ่าน Homebrew (แบบที่ guide นี้ใช้) — ข้าม Step นี้แล้วสร้าง systemd unit เองใน post ถัดไปของ series
(Optional) หลาย hostname ใน tunnel เดียว
ถ้ามีหลาย service บน port ต่างกัน ใส่หลาย ingress rule ได้:
tunnel: <tunnel-uuid>
credentials-file: /home/your-username/.cloudflared/<tunnel-uuid>.json
ingress:
- hostname: app.yourdomain.com
service: http://localhost:3000
- hostname: api.yourdomain.com
service: http://localhost:8080
- hostname: dashboard.yourdomain.com
service: http://localhost:9000
- service: http_status:404
อย่าลืม route DNS แต่ละ hostname ด้วย cloudflared tunnel route dns my-home-server <hostname>
Troubleshooting
| ปัญหา | สาเหตุ | วิธีแก้ |
|---|---|---|
cloudflared tunnel login ไม่ตอบสนอง | Browser กับ cloudflared อยู่คนละเครื่อง | คลิก URL ใน browser ที่ login Cloudflare อยู่ แล้ว save cert.pem มาวางเอง |
| Tunnel ขึ้น แต่เว็บไม่โหลด | Config file path ผิด | ตรวจ path ใน config.yml ให้เป็น absolute path |
| Tunnel ขึ้น แต่ได้ 502/404 | Service บน localhost ไม่รัน | ตรวจว่า service ฟัง port ที่ตรงกับ ingress rule |
failed to dial / tunnel ต่อไม่ติด | Firewall บล็อก outbound port 7844 | เปิด outbound TCP/UDP port 7844 (connectivity pre-checks) |
ต่อไป
อยากให้ tunnel รันอัตโนมัติหลัง reboot — ไปต่อที่ เข้าเครื่องจากนอกบ้าน (2/3): systemd