การทดสอบ API บน localhost ที่ต้องรับ webhook หรือ callback จากบริการภายนอก จำเป็นต้องเปิดเผยเซิร์ฟเวอร์การพัฒนาในเครื่องของคุณออกสู่สาธารณะชั่วคราว บริการ Tunneling เช่น ngrok, NPort, Cloudflare Tunnel และอื่นๆ จะสร้างการเชื่อมต่อที่ปลอดภัยที่ทำให้ localhost ของคุณมี URL สาธารณะ
ทำไมคุณถึงต้องการ Localhost Tunneling
คุณกำลังสร้าง API ที่จะเชื่อมโยงกับบริการภายนอก ทุกอย่างทำงานได้ดีบนแล็ปท็อปของคุณ — endpoint ตอบสนองอย่างถูกต้อง ข้อมูลไหลลื่น จากนั้นคุณก็ลองทดสอบ webhook callbacks จาก Stripe, GitHub, Twilio หรือบริการภายนอกอื่นๆ
ปัญหา: บริการภายนอกไม่สามารถเข้าถึง localhost:3000 ได้ เซิร์ฟเวอร์การพัฒนาของคุณไม่สามารถเข้าถึงได้จากอินเทอร์เน็ต
สถานการณ์ทั่วไปที่ทำให้การทำงานของคุณหยุดชะงัก:
1. การทดสอบ Webhook
บริการต่างๆ เช่น Stripe ส่งการยืนยันการชำระเงิน, GitHub ส่งเหตุการณ์เกี่ยวกับ Repository, Slack ส่งเหตุการณ์การโต้ตอบ — ทั้งหมดในรูปแบบคำขอ POST ไปยัง API ของคุณ ในระหว่างการพัฒนา บริการเหล่านี้ต้องการ URL สาธารณะเพื่อส่ง webhook ไปยัง

2. OAuth Callback URLs
เมื่อคุณใช้งาน "เข้าสู่ระบบด้วย Google," "เข้าสู่ระบบด้วย GitHub" หรือ OAuth flow ใดๆ ผู้ให้บริการการตรวจสอบสิทธิ์จะเปลี่ยนเส้นทางผู้ใช้กลับไปยังแอปพลิเคชันของคุณพร้อมกับรหัสการอนุญาต URL การเปลี่ยนเส้นทางต้องสามารถเข้าถึงได้จากสาธารณะและตรงกับที่คุณลงทะเบียนไว้กับผู้ให้บริการ

3. การเชื่อมโยง API ของบุคคลที่สาม
API บางตัวต้องการ callback URL สำหรับการดำเนินการแบบ asynchronous ตัวอย่างเช่น บริการแปลงวิดีโอจะแจ้งเตือน API ของคุณเมื่อการประมวลผลเสร็จสิ้น หรือผู้ประมวลผลการชำระเงินจะยืนยันธุรกรรม
4. การพัฒนาแอปพลิเคชันมือถือ
การทดสอบ API ของคุณจากอุปกรณ์มือถือบนเครือข่ายเดียวกันมักจะล้มเหลวเนื่องจากแอปมือถือไม่สามารถระบุ localhost ได้ การใช้ tunnel จะทำให้คุณมี URL ที่ใช้งานได้จากทุกอุปกรณ์
5. การสาธิตให้ลูกค้าดู
บางครั้งคุณจำเป็นต้องแสดงผลงานที่กำลังดำเนินอยู่ให้ลูกค้าหรือผู้มีส่วนได้ส่วนเสียดู การ deploy ไปยัง staging สำหรับการเปลี่ยนแปลงเล็กๆ น้อยๆ ทุกครั้งจะทำให้การทำงานช้าลง URL สาธารณะชั่วคราวช่วยให้ลูกค้าสามารถทดสอบสภาพแวดล้อมการพัฒนาของคุณได้
Localhost Tunneling ทำงานอย่างไร
บริการ Tunneling สร้างการเชื่อมต่อที่ปลอดภัยระหว่างเซิร์ฟเวอร์คลาวด์ของพวกเขากับเครื่องคอมพิวเตอร์ของคุณ:
External Service → Tunneling Service (public URL) → Secure Connection → Your Localhost:3000
กระบวนการ:
- คุณเริ่มไคลเอนต์ tunnel บนเครื่องของคุณ โดยชี้ไปยังพอร์ตภายในเครื่องของคุณ
- ไคลเอนต์เชื่อมต่อ ไปยังโครงสร้างพื้นฐานคลาวด์ของบริการ tunneling
- บริการกำหนด URL สาธารณะ (เช่น
https://abc123.ngrok.io) - คำขอที่เข้ามา ไปยัง URL สาธารณะนั้นจะถูกส่งผ่านการเชื่อมต่อที่เข้ารหัสไปยัง localhost ของคุณ
- เซิร์ฟเวอร์ภายในเครื่องของคุณ จะได้รับคำขอราวกับว่ามันมาจากไคลเอนต์โดยตรง
- การตอบสนองจะไหลกลับ ผ่าน tunnel ไปยังผู้ร้องขอ
สิ่งนี้เกิดขึ้นอย่างโปร่งใส เซิร์ฟเวอร์ภายในเครื่องของคุณไม่จำเป็นต้องรู้ว่ามันอยู่เบื้องหลัง tunnel
การเปรียบเทียบบริการ Tunneling ยอดนิยม
นี่คือตัวเลือกที่ได้รับความนิยมมากที่สุดในปี 2026 พร้อมจุดแข็งและข้อจำกัด:
ngrok (ยอดนิยมที่สุด)
เหมาะสำหรับ: โครงการที่จัดตั้งแล้ว, ทีมที่ต้องการความน่าเชื่อถือ
ngrok http 3000

ข้อดี:
- เป็นมาตรฐานอุตสาหกรรมพร้อมเอกสารประกอบที่ครอบคลุม
- มี Web inspector UI เพื่อดูคำขอทั้งหมด
- สามารถใช้โดเมนที่กำหนดเองได้ในแพลนแบบชำระเงิน
- ฟังก์ชันการเล่นซ้ำคำขอ (Request replay)
- การยุติ TLS
ข้อเสีย:
- แพลนฟรีมีข้อจำกัดเซสชัน 2 ชั่วโมง
- URL สุ่มในแพลนฟรี (เปลี่ยนทุกเซสชัน)
- ราคาเริ่มต้นที่ 10 ดอลลาร์/เดือน สำหรับ URL ถาวร
แพลนฟรี:
- 1 ตัวแทนออนไลน์
- 40 การเชื่อมต่อ/นาที
- URL สุ่มที่หมดอายุ
แพลนแบบชำระเงิน: $8-$20/เดือน

NPort (ทางเลือกฟรีที่กำลังมาแรง)
เหมาะสำหรับ: นักพัฒนาที่ต้องการหลีกเลี่ยงค่าใช้จ่ายรายเดือน

nport start 3000
ข้อดี:
- ฟรีและโอเพนซอร์สทั้งหมด
- ไม่มีข้อจำกัดด้านเวลาเซสชัน
- มี subdomain ที่กำหนดเองได้
- มีตัวเลือก self-hosted
- มีชุดคุณสมบัติคล้ายกับ ngrok แบบฟรี
ข้อเสีย:
- ชุมชนเล็กกว่า (มีบทเรียนน้อยกว่า)
- ยังไม่สมบูรณ์เท่า (เปิดตัวปี 2025)
- ไม่มีการสนับสนุนเชิงพาณิชย์
แพลนฟรี:
- Tunnel ไม่จำกัด
- ไม่จำกัดเวลา
- Subdomain ที่กำหนดเองได้

นี่คือเครื่องมือที่กำลังได้รับความนิยมบน Dev.to เนื่องจากนักพัฒนาแสวงหาทางเลือกอื่นแทน ngrok โดยไม่มีค่าใช้จ่ายต่อเนื่อง
Cloudflare Tunnel (เหมาะที่สุดสำหรับสภาพแวดล้อมใกล้เคียง Production)
เหมาะสำหรับ: ทีมที่ใช้ Cloudflare อยู่แล้ว, ต้องการ tunnel ที่ทำงานระยะยาว

cloudflared tunnel --url http://localhost:3000
ข้อดี:
- โครงสร้างพื้นฐานระดับองค์กร
- มีการป้องกัน DDoS
- รวมเข้ากับ Cloudflare Zero Trust
- ไม่จำกัดแบนด์วิดท์
- ฟรีสำหรับกรณีการใช้งานส่วนใหญ่
ข้อเสีย:
- การตั้งค่าที่ซับซ้อนกว่า
- ต้องมีบัญชี Cloudflare
- เกินความจำเป็นสำหรับการทดสอบ webhook แบบง่ายๆ
แพลนฟรี:
- แบนด์วิดท์ไม่จำกัด
- Tunnel ไม่จำกัด
- การป้องกัน DDoS
Localtunnel (ง่ายที่สุด)
เหมาะสำหรับ: การทดสอบแบบครั้งเดียวที่รวดเร็ว, ไม่ต้องติดตั้ง
npx localtunnel --port 3000
ข้อดี:
- ไม่ต้องลงทะเบียน
- ไม่ต้องตั้งค่าใดๆ
- ไม่ต้องติดตั้ง (รันผ่าน npx)
- โอเพนซอร์ส
ข้อเสีย:
- ไม่น่าเชื่อถือ (ล่มบ่อย)
- ไม่มีการตรวจสอบคำขอ
- มีแต่ URL สุ่มเท่านั้น
- เอกสารประกอบน้อยมาก

แพลนฟรี:
- ทุกอย่างฟรี
- ไม่มีข้อจำกัดคุณสมบัติ
Tailscale Funnel (เหมาะที่สุดสำหรับทีม)
เหมาะสำหรับ: การแชร์ส่วนตัวภายในทีม, การสาธิตที่ปลอดภัย

tailscale serve https / http://localhost:3000
tailscale funnel 443 on
ข้อดี:
- อิงตาม WireGuard (รวดเร็ว, ปลอดภัย)
- เป็นส่วนตัวโดยค่าเริ่มต้น (มองเห็นได้เฉพาะเครือข่าย Tailscale ของคุณ)
- สามารถเปิดเผยต่อสาธารณะได้เมื่อจำเป็น
- ยอดเยี่ยมสำหรับการทำงานร่วมกันเป็นทีม
ข้อเสีย:
- ต้องตั้งค่า Tailscale
- มีช่วงการเรียนรู้ที่สูงขึ้น
- ออกแบบมาเพื่อเครือข่ายส่วนตัวเป็นหลัก
แพลนฟรี:
- สูงสุด 100 อุปกรณ์
- แบนด์วิดท์ไม่จำกัด

ตารางเปรียบเทียบ
| คุณสมบัติ | ngrok | NPort | Cloudflare Tunnel | Localtunnel | Tailscale |
|---|---|---|---|---|---|
| ราคา | ฟรี/$10+ | ฟรี | ฟรี | ฟรี | ฟรี/ชำระเงิน |
| จำกัดเวลาเซสชัน | 2 ชั่วโมง | ไม่มี | ไม่มี | ไม่มี | ไม่มี |
| โดเมนที่กำหนดเอง | ชำระเงิน | ฟรี | ใช่ | ไม่ | ใช่ |
| ตัวตรวจสอบคำขอ | ใช่ | พื้นฐาน | ไม่ | ไม่ | ไม่ |
| ความซับซ้อนในการตั้งค่า | ต่ำ | ต่ำ | ปานกลาง | ต่ำมาก | ปานกลาง |
| ความน่าเชื่อถือ | ยอดเยี่ยม | ดี | ยอดเยี่ยม | แย่ | ยอดเยี่ยม |
| เหมาะสำหรับ | การทดสอบ Production | นักพัฒนาที่คำนึงถึงค่าใช้จ่าย | องค์กร | การทดสอบด่วน | การแชร์ในทีม |
การตั้งค่า Localhost Tunnel ครั้งแรกของคุณ
มาดูขั้นตอนการตั้งค่าด้วยเครื่องมือที่พบบ่อยที่สุด เราจะใช้ Node.js Express API เป็นตัวอย่าง แต่สิ่งนี้ใช้งานได้กับเซิร์ฟเวอร์ภายในเครื่องทุกประเภท
ตัวอย่าง: เซิร์ฟเวอร์ API ภายในเครื่อง
// server.js
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
console.log('Webhook received:', req.body);
res.json({ received: true });
});
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
ตัวเลือกที่ 1: การใช้ ngrok
ขั้นตอนที่ 1: ติดตั้ง ngrok
# macOS
brew install ngrok
# Windows (ผ่าน Chocolatey)
choco install ngrok
# Linux
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | \
sudo tee /etc/apt/sources.list.d/ngrok.list && \
sudo apt update && sudo apt install ngrok
ขั้นตอนที่ 2: ตรวจสอบสิทธิ์ (เป็นทางเลือกแต่แนะนำ)
ngrok config add-authtoken YOUR_AUTH_TOKEN
ขั้นตอนที่ 3: เริ่มต้น tunnel
ngrok http 3000
ผลลัพธ์:
Session Status online
Account you@example.com (Plan: Free)
Version 3.5.0
Region United States (us)
Forwarding https://abc123.ngrok.io -> http://localhost:3000
ตอนนี้ API ของคุณสามารถเข้าถึงได้ที่ https://abc123.ngrok.io
ขั้นตอนที่ 4: ทดสอบ
curl https://abc123.ngrok.io/health
# {"status":"healthy"}
ตัวเลือกที่ 2: การใช้ NPort (ทางเลือกฟรี)
ขั้นตอนที่ 1: ติดตั้ง NPort
npm install -g nport-cli
# หรือ
curl -sSL https://nport.io/install.sh | bash
ขั้นตอนที่ 2: เริ่มต้น tunnel
nport start 3000 --subdomain myapi
ผลลัพธ์:
✓ Tunnel started successfully
Public URL: https://myapi.nport.io
Local URL: http://localhost:3000
ขั้นตอนที่ 3: ทดสอบ
curl https://myapi.nport.io/health
# {"status":"healthy"}
ตัวเลือกที่ 3: การใช้ Cloudflare Tunnel
ขั้นตอนที่ 1: ติดตั้ง cloudflared
# macOS
brew install cloudflare/cloudflare/cloudflared
# Linux
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
ขั้นตอนที่ 2: Quick tunnel (ไม่ต้องลงทะเบียน)
cloudflared tunnel --url http://localhost:3000
ผลลัพธ์:
2026-01-27T12:00:00Z INF Your quick tunnel is: https://xyz789.trycloudflare.com
สำหรับ tunnel แบบถาวร (ต้องมีบัญชี Cloudflare):
# เข้าสู่ระบบ
cloudflared tunnel login
# สร้าง tunnel
cloudflared tunnel create myapi
# กำหนดค่าและรัน
cloudflared tunnel --config config.yml run myapi
การทดสอบ Webhooks ด้วย Apidog
ตอนนี้ localhost ของคุณสามารถเข้าถึงได้จากสาธารณะแล้ว มาทดสอบ webhook อย่างเป็นระบบโดยใช้ Apidog
ทำไมต้องรวม Tunneling + Apidog?
Tunneling แก้ปัญหาการเข้าถึง; Apidog แก้ปัญหาการยืนยัน:
- บริการ Tunneling ทำให้ localhost ของคุณเข้าถึงได้
- Apidog ทดสอบการตอบสนอง API ของคุณ ตรวจสอบความถูกต้องของข้อมูล ทำให้สถานการณ์เป็นไปโดยอัตโนมัติ
การตั้งค่าการทดสอบ Webhook ใน Apidog
ขั้นตอนที่ 1: นำเข้าหรือสร้าง API ของคุณ
- เปิด Apidog

2. สร้างโปรเจกต์ใหม่

3. เพิ่ม endpoint ของ webhook:

- เมธอด: POST
- URL:
{{base_url}}/webhook - ส่วนหัว:
Content-Type: application/json
ขั้นตอนที่ 2: กำหนดค่าตัวแปรสภาพแวดล้อม (Environment Variables)
ตั้งค่าสภาพแวดล้อมสองแบบ:
การพัฒนา (Tunneled):
{
"base_url": "https://abc123.ngrok.io"
}
Production:
{
"base_url": "https://api.yourapp.com"
}
สิ่งนี้ช่วยให้คุณสามารถทดสอบ endpoint เดียวกันทั้งในเครื่องและในสภาพแวดล้อม Production ได้ด้วยการคลิกเพียงครั้งเดียว
ขั้นตอนที่ 3: สร้างสถานการณ์การทดสอบ
ทดสอบสิ่งที่เกิดขึ้นเมื่อ webhook เข้ามา:
ตัวอย่าง: การทดสอบ Stripe Payment Webhook
// เนื้อหาคำขอ (Request Body)
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_test123",
"amount": 2000,
"currency": "usd",
"status": "succeeded"
}
}
}
การยืนยันใน Apidog:
- รหัสสถานะเท่ากับ 200
- การตอบกลับมี
received: true - เวลาตอบสนอง < 1000ms
- Content-Type คือ
application/json
ขั้นตอนที่ 4: จำลองบริการบุคคลที่สาม
แทนที่จะเรียกใช้ webhook จริงจาก Stripe หรือ GitHub ให้จำลองใน Apidog:
- คัดลอกตัวอย่าง payload ของ webhook จากเอกสารของบริการ
- สร้างกรณีทดสอบพร้อมสถานการณ์ต่างๆ (สำเร็จ, ล้มเหลว, กรณีขอบ)
- เรียกใช้สถานการณ์ทั้งหมดกับ localhost ที่คุณได้ทำ tunnel ไว้
- ตรวจสอบว่า API ของคุณจัดการแต่ละกรณีได้อย่างถูกต้อง
การทดสอบ OAuth Callbacks
สถานการณ์: คุณกำลังใช้งาน "เข้าสู่ระบบด้วย Google"
ขั้นตอนที่ 1: เริ่มต้น tunnel ด้วย subdomain ที่กำหนดเอง
ngrok http 3000 --subdomain myapp
# URL: https://myapp.ngrok.io
ขั้นตอนที่ 2: กำหนดค่า OAuth redirect ใน Google Console
ตั้งค่า callback URL: https://myapp.ngrok.io/auth/google/callback
ขั้นตอนที่ 3: ทดสอบ flow ใน Apidog
- ส่งคำขอไปยัง
/auth/googleเพื่อรับ URL การอนุญาต - ติดตามการเปลี่ยนเส้นทางด้วยตนเองหรือโดยโปรแกรม
- ตรวจสอบว่า callback ได้รับรหัสการอนุญาต
- ยืนยันว่าการแลกเปลี่ยนโทเค็นทำงานได้อย่างถูกต้อง
ขั้นตอนที่ 4: ตรวจสอบการจัดเก็บโทเค็น
ใช้ Apidog เพื่อ:
- ตรวจสอบว่าโทเค็นถูกจัดเก็บอย่างปลอดภัย
- ทดสอบ flow การรีเฟรชโทเค็น
- ตรวจสอบให้แน่ใจว่าโทเค็นที่หมดอายุได้รับการจัดการอย่างถูกต้อง
กรณีการใช้งานทั่วไป
1. การทดสอบ Webhook การชำระเงิน (Stripe, PayPal)
ความท้าทาย: ผู้ให้บริการชำระเงินส่ง webhook สำหรับเหตุการณ์ต่างๆ เช่น การชำระเงินสำเร็จ การคืนเงิน ข้อพิพาท
วิธีแก้ปัญหา:
# เริ่มต้น tunnel
ngrok http 3000
# กำหนดค่า webhook URL ในแดชบอร์ด Stripe
# https://abc123.ngrok.io/webhook/stripe
# ใช้ Stripe CLI เพื่อส่งต่อ webhook ทดสอบ
stripe listen --forward-to localhost:3000/webhook/stripe
# เรียกใช้เหตุการณ์ทดสอบ
stripe trigger payment_intent.succeeded
ทดสอบด้วย Apidog:
- สร้างกรณีทดสอบสำหรับแต่ละประเภทเหตุการณ์
- ตรวจสอบความซ้ำซ้อน (idempotency) (การจัดการ webhook ที่ซ้ำกัน)
- ทดสอบการตรวจสอบลายเซ็น
- ตรวจสอบให้แน่ใจว่ามีการอัปเดตฐานข้อมูลอย่างถูกต้อง
2. การทดสอบคำสั่ง Bot ของ Slack/Discord
ความท้าทาย: แพลตฟอร์มแชทส่งเหตุการณ์การโต้ตอบเมื่อผู้ใช้คลิกปุ่มหรือรันคำสั่ง
วิธีแก้ปัญหา:
# เริ่มต้น tunnel
nport start 3000 --subdomain myslackbot
# กำหนดค่าใน Slack API:
# Interactivity URL: https://myslackbot.nport.io/slack/interactions
# Slash Commands: https://myslackbot.nport.io/slack/commands
ทดสอบด้วย Apidog:
- จำลองการคลิกปุ่ม
- ทดสอบการตอบสนองคำสั่ง slash
- ตรวจสอบเวลาตอบสนอง (Slack กำหนดให้ตอบสนองภายใน <3 วินาที)
- ทดสอบการตอบสนองแบบเลื่อนเวลาด้วย
response_url
3. การทดสอบ SMS/Voice Webhooks (Twilio)
ความท้าทาย: Twilio ส่ง webhook เมื่อมี SMS เข้ามาหรือได้รับสายโทรศัพท์
วิธีแก้ปัญหา:
cloudflared tunnel --url http://localhost:3000
กำหนดค่า TwiML webhooks เพื่อชี้ไปยัง URL ของ tunnel ของคุณ
ทดสอบด้วย Apidog:
- จำลอง payload SMS ที่เข้ามา
- ทดสอบประเภทข้อความที่แตกต่างกัน (MMS, SMS)
- ตรวจสอบการตรวจสอบลายเซ็น Twilio
- ทดสอบการสร้าง TwiML ตอบกลับ
4. การทดสอบ API สำหรับแอปพลิเคชันมือถือ
ความท้าทาย: การทดสอบ API ของคุณจากอุปกรณ์จริงหรือโปรแกรมจำลอง
ปัญหาเกี่ยวกับ localhost:
// สิ่งนี้ล้มเหลวจากอุปกรณ์มือถือ
fetch('http://localhost:3000/api/users')
วิธีแก้ปัญหาด้วย tunnel:
// สิ่งนี้ใช้ได้จากทุกที่
fetch('https://myapi.ngrok.io/api/users')
ทดสอบด้วย Apidog:
- สร้างเอกสาร API ด้วย base URL ที่ tunneled
- แชร์กับทีมมือถือ
- นักพัฒนาแอปพลิเคชันมือถือสามารถทดสอบกับเซิร์ฟเวอร์การพัฒนาของคุณได้โดยตรง
- เปลี่ยนไปใช้ URL ของ staging/production เมื่อพร้อม
5. การทดสอบ GitHub/GitLab Webhooks
ความท้าทาย: การทดสอบ repository webhooks (push, pull request, issues) ในเครื่อง
วิธีแก้ปัญหา:
# เริ่มต้น tunnel
ngrok http 4000
# กำหนดค่าในการตั้งค่า GitHub repo:
# Webhook URL: https://abc123.ngrok.io/github/webhook
# Content type: application/json
# Events: Push, Pull requests
ทดสอบด้วย Apidog:
- จำลองเหตุการณ์ push
- ทดสอบเหตุการณ์ PR opened/closed
- ตรวจสอบการตรวจสอบลายเซ็น (X-Hub-Signature)
- ทดสอบตรรกะการกรองสาขา
แนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัย
การเปิดเผย localhost สู่สาธารณะสร้างความเสี่ยงด้านความปลอดภัย โปรดปฏิบัติตามแนวทางปฏิบัติดังต่อไปนี้:
1. ใช้ HTTPS เท่านั้น
บริการ tunneling ทั้งหมดมี HTTPS ให้โดยค่าเริ่มต้น ห้ามใช้ HTTP ธรรมดาสำหรับ tunnels:
# ดี
ngrok http 3000
# สร้าง https://abc123.ngrok.io
# ไม่ดี (อย่าทำสิ่งนี้)
ngrok http --scheme=http 3000
2. ใช้การตรวจสอบลายเซ็น Webhook
อย่าเชื่อ webhook ที่เข้ามาโดยไม่ตรวจสอบ ตรวจสอบลายเซ็น:
const crypto = require('crypto');
function verifyStripeSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhook/stripe', (req, res) => {
const signature = req.headers['stripe-signature'];
if (!verifyStripeSignature(req.body, signature, process.env.STRIPE_SECRET)) {
return res.status(401).send('Invalid signature');
}
// ประมวลผล webhook
});
3. จำกัดการเข้าถึงด้วย Basic Auth
เพิ่มการตรวจสอบสิทธิ์ให้กับ tunnel ของคุณ:
# ngrok พร้อม basic auth
ngrok http 3000 --auth="username:password"
# NPort พร้อม basic auth
nport start 3000 --auth username:password
ตอนนี้คำขอต้องการข้อมูลประจำตัว:
curl -u username:password https://abc123.ngrok.io/webhook
4. ใช้ความลับเฉพาะสภาพแวดล้อม
ห้าม commit ความลับของ webhook หรือ API keys:
// .env.development
STRIPE_WEBHOOK_SECRET=whsec_test_abc123
WEBHOOK_TUNNEL_URL=https://abc123.ngrok.io
// .env.production
STRIPE_WEBHOOK_SECRET=whsec_live_xyz789
WEBHOOK_URL=https://api.yourapp.com
5. ตรวจสอบการเข้าถึง Tunnel
ใช้ request inspector เพื่อตรวจสอบกิจกรรมที่น่าสงสัย:
# ngrok มีเว็บอินเทอร์เฟซที่:
http://localhost:4040
# ดูคำขอ การตอบสนอง การโจมตีซ้ำทั้งหมด
6. จำกัดระยะเวลาของ Tunnel
อย่าปล่อยให้ tunnels ทำงานตลอดไป:
# หมดอายุ tunnel โดยอัตโนมัติหลังจาก 1 ชั่วโมง
ngrok http 3000 --session-duration 1h
7. ตรวจสอบแหล่งที่มาของคำขอ
ตรวจสอบที่อยู่ IP ที่เข้ามาหรือใช้ allowlists:
const allowedIPs = [
'192.0.2.1', // IP ของ Stripe webhook
'198.51.100.0/24'
];
app.use('/webhook', (req, res, next) => {
const clientIP = req.ip;
if (!allowedIPs.includes(clientIP)) {
return res.status(403).send('Forbidden');
}
next();
});
การแก้ไขปัญหาทั่วไป
ปัญหาที่ 1: URL ของ Tunnel เปลี่ยนแปลงทุกครั้งที่เริ่มเซสชัน
ปัญหา: Tunnel ฟรีของ ngrok ใช้ URL แบบสุ่มที่เปลี่ยนแปลงทุกครั้งที่รีสตาร์ท Webhooks ที่กำหนดค่าด้วย URL เก่าจะไม่ทำงาน
วิธีแก้ปัญหา:
- ใช้แผนแบบชำระเงิน สำหรับ URL แบบคงที่:
ngrok http 3000 --domain=myapp.ngrok.app
- เปลี่ยนไปใช้ NPort พร้อม subdomain ที่กำหนดเองได้ฟรี:
nport start 3000 --subdomain myapp
# ได้ https://myapp.nport.io เสมอ
- อัปเดต webhooks ด้วยโปรแกรม ผ่าน API เมื่อ tunnel เริ่มต้น
ปัญหาที่ 2: Webhooks หมดเวลา
ปัญหา: เซิร์ฟเวอร์ในเครื่องของคุณใช้เวลานานเกินไปในการตอบสนอง บริการเช่น Slack ต้องการการตอบสนองภายใน 3 วินาที
วิธีแก้ปัญหา:
ประมวลผลแบบ asynchronous:
app.post('/webhook', async (req, res) => {
// รับทราบทันที
res.json({ received: true });
// ประมวลผลในเบื้องหลัง
processWebhookAsync(req.body).catch(console.error);
});
async function processWebhookAsync(data) {
// ทำงานที่ใช้เวลานานที่นี่ (ฐานข้อมูล, API ภายนอก ฯลฯ)
await heavyProcessing(data);
}
ทดสอบการหมดเวลาด้วย Apidog โดยการตั้งค่าขีดจำกัดเวลาหมดอายุที่เข้มงวดในสถานการณ์ทดสอบ
ปัญหาที่ 3: ข้อผิดพลาด CORS จากเบราว์เซอร์
ปัญหา: ส่วนหน้า (Frontend) ที่ส่งคำขอไปยัง URL ของ tunnel ได้รับข้อผิดพลาด CORS
วิธีแก้ปัญหา:
กำหนดค่าส่วนหัว CORS:
const cors = require('cors');
app.use(cors({
origin: [
'http://localhost:3001', // เซิร์ฟเวอร์พัฒนาส่วนหน้าของคุณ
'https://abc123.ngrok.io' // URL ของ tunnel ของคุณ
],
credentials: true
}));
ปัญหาที่ 4: การจำกัดอัตรา (Rate Limiting) ในแพลนฟรี
ปัญหา: Tunnels ฟรีมีข้อจำกัดการเชื่อมต่อ (ngrok: 40/นาที)
วิธีแก้ปัญหา:
- รวมคำขอทดสอบ ใน Apidog แทนที่จะทดสอบแต่ละรายการอย่างรวดเร็ว
- ใช้ tunnels หลายอัน สำหรับบริการที่แตกต่างกัน
- อัปเกรดเป็นแผนแบบชำระเงิน หากมีการทดสอบจำนวนมาก
- เปลี่ยนไปใช้บริการที่ไม่จำกัด เช่น Cloudflare Tunnel หรือ NPort
ปัญหาที่ 5: Tunnel หลุดบ่อย
ปัญหา: ความไม่เสถียรของเครือข่ายทำให้ tunnel หลุด
วิธีแก้ปัญหา:
ใช้ systemd/pm2 เพื่อรีสตาร์ทอัตโนมัติ:
# สร้างบริการ systemd
sudo nano /etc/systemd/system/ngrok.service
[Unit]
Description=ngrok tunnel
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser
ExecStart=/usr/local/bin/ngrok http 3000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl enable ngrok
sudo systemctl start ngrok
ปัญหาที่ 6: ไม่สามารถเข้าถึง Tunnel จากเครือข่ายเฉพาะ
ปัญหา: Firewall ขององค์กรหรือเครือข่ายที่เข้มงวดบล็อกการรับส่งข้อมูลของ tunnel
วิธีแก้ปัญหา:
- ใช้ Cloudflare Tunnel (ไม่ค่อยถูกบล็อก)
- เปลี่ยนภูมิภาคของ tunnel ให้ใกล้กับคุณมากขึ้น:
ngrok http 3000 --region eu
- ใช้ Tailscale สำหรับเครือข่ายส่วนตัวแทน tunnel สาธารณะ
รูปแบบขั้นสูง
รูปแบบที่ 1: Multi-Port Tunneling
เปิดเผยบริการหลายรายการพร้อมกัน:
# เทอร์มินัล 1: API server
ngrok http 3000
# เทอร์มินัล 2: Frontend dev server
ngrok http 3001
# เทอร์มินัล 3: Webhook worker
ngrok http 3002
หรือใช้ไฟล์กำหนดค่า ngrok:
# ~/.ngrok2/ngrok.yml
tunnels:
api:
proto: http
addr: 3000
frontend:
proto: http
addr: 3001
worker:
proto: http
addr: 3002
ngrok start --all
รูปแบบที่ 2: Tunnel + Docker Compose
# docker-compose.yml
version: '3'
services:
api:
build: .
ports:
- "3000:3000"
ngrok:
image: ngrok/ngrok:latest
command:
- "http"
- "api:3000"
environment:
NGROK_AUTHTOKEN: ${NGROK_AUTHTOKEN}
docker-compose up
รูปแบบที่ 3: การฉีด URL ของ Tunnel แบบไดนามิก
อัปเดตแอปของคุณด้วย URL ของ tunnel โดยอัตโนมัติ:
// start-tunnel.js
const ngrok = require('ngrok');
const fs = require('fs');
(async function() {
const url = await ngrok.connect(3000);
console.log(`Tunnel started: ${url}`);
// อัปเดตไฟล์ .env
fs.appendFileSync('.env', `\nTUNNEL_URL=${url}\n`);
// อัปเดต Stripe webhook
await updateStripeWebhook(url);
})();
รูปแบบที่ 4: การส่งต่อคำขอไปยังหลายสภาพแวดล้อม
ทดสอบ webhook เดียวกันกับสภาพแวดล้อม dev, staging และ production:
// webhook-multiplexer.js
app.post('/webhook', async (req, res) => {
const environments = [
'http://localhost:3000',
'https://staging.api.com',
'https://api.yourapp.com'
];
// ส่งต่อให้ทุกสภาพแวดล้อม
const results = await Promise.all(
environments.map(env =>
fetch(`${env}/webhook`, {
method: 'POST',
headers: req.headers,
body: JSON.stringify(req.body)
})
)
);
res.json({ forwarded: results.length });
});
บทสรุป
การทดสอบ API บน localhost ที่ได้รับ webhook หรือ callback ไม่จำเป็นต้อง deploy ไปยัง staging สำหรับทุกการเปลี่ยนแปลง บริการ Tunneling สร้าง URL สาธารณะชั่วคราวที่ช่วยให้บริการภายนอกสามารถเข้าถึงสภาพแวดล้อมการพัฒนาของคุณได้
เริ่มต้นด้วยแผนฟรีของเครื่องมือใดๆ หากการทดสอบ webhook กลายเป็นส่วนหนึ่งของการทำงานประจำวันของคุณ ลองพิจารณาแผนแบบชำระเงินสำหรับ URL แบบคงที่และคุณสมบัติเพิ่มเติม แต่สำหรับนักพัฒนาส่วนใหญ่ บริการ tunneling ฟรีที่รวมเข้ากับ ความสามารถในการทดสอบ API ของ Apidog ก็มีทุกสิ่งที่คุณต้องการเพื่อทดสอบ API บน localhost ได้อย่างมีประสิทธิภาพ
