Skip to main content

> Jitrak Blog

>_ Remote Access: Expose Services with Cloudflare Tunnel (1/3)

# เข้าเครื่องจากนอกบ้าน (1/3) — เปิด tunnel expose service ด้วย Cloudflare Tunnel ไม่ต้อง public IP หรือ port forward

@ Yosapol Jitrak | 7 Jun 2026 17:35

ผมเริ่มใช้ Cloudflare Tunnel ตอนที่อยากเข้า dashboard จากนอกบ้านแต่ ISP ไม่ให้ public IP — ไม่ต้องยุ่งกับ port forwarding อีกเลย

Series: เข้าเครื่องจากนอกบ้าน (3 ส่วน)

  1. Remote Access: Expose Services with Cloudflare Tunnel (1/3) — ติดตั้ง cloudflared สร้าง tunnel ทดสอบ expose localhost (บทความนี้)
  2. Remote Access: Auto-start After Reboot with systemd (2/3) — systemd คืออะไร ประยุกต์กับ cloudflared
  3. 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

CG-NAT path from Internet to home 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

Port forwarding via home Router

ตัวอย่างที่หลายคนเคยทำ: เปิดห้อง Warcraft III (WC3) ให้เพื่อน join จากนอกบ้าน

  1. คุณ host เกมบน PC (192.168.1.50) — เกมฟัง port 6112 (TCP/UDP)
  2. เพื่อนนอกบ้านต้องต่อมาที่ public IP ของบ้าน ไม่ใช่ 192.168.1.50
  3. ใน Router ตั้ง rule เช่น External port 6112 → 192.168.1.50:6112
  4. เพื่อนใส่ public IP (หรือ DDNS) แล้ว join ห้องได้

แนวเดียวกันกับเปิดเว็บจากบ้าน: forward port 443 จาก Router ไปที่เครื่องที่รัน service (เช่น 192.168.1.50:3000) — แต่ต้องเปิดช่องให้โลกภายนอกยิงเข้ามาได้ จึงเสี่ยงด้านความปลอดภัยมากกว่า tunnel

สรุปแล้ว วิธีเดิมต้องทำ 2 อย่าง:

  1. ขอ public IP จาก ISP (บางเจ้าคิดเงินเพิ่ม หรือไม่ให้เลย โดยเฉพาะเจ้าที่ใช้ CG-NAT)
  2. ตั้ง 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

Cloudflare Tunnel workflow

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/404Service บน 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

~/shortcuts

> Keyboard shortcuts

  • Toggle this help?H
  • Back to topT
  • Scroll downJ
  • Scroll upK
  • Forward (newer post or next page)F
  • Back (older post or previous page)B
  • Go homeG
  • Close helpEsc