1. Home
  2. Insights
  3. Security
  4. Rate Limiting คืออะไร และควรวางไว้ตรงไหนของระบบ
Security

Rate Limiting คืออะไร และควรวางไว้ตรงไหนของระบบ

อธิบายแนวคิด rate limiting ในระบบ production ว่าช่วยกันอะไร ควรวางที่ชั้นไหนของระบบ และต่างจาก idempotency, validation และ authentication อย่างไร

Rate Limiting คืออะไร และควรวางไว้ตรงไหนของระบบ

หลายระบบเริ่มต้นโดยเปิด endpoint ให้เรียกใช้งานได้ตรง ๆ ก่อน แล้วค่อยไปคิดเรื่องป้องกันภายหลังเมื่อเริ่มมี traffic จริงหรือเริ่มเจอปัญหา แต่พอระบบขึ้น production คำถามเรื่อง “เรียกได้บ่อยแค่ไหน” จะกลายเป็นเรื่องสำคัญทันที

ถ้ามี client ยิง request ถี่เกินไปจะเกิดอะไรขึ้น
ถ้ามี bot ลองยิง endpoint เดิมซ้ำ ๆ ระบบจะรับไหวไหม
ถ้ามี mobile app retry เพราะ timeout แล้วดันยิงถี่กว่าที่คาดไว้ล่ะ
ถ้ามี public API ที่ไม่มีเพดานเลย endpoint สำคัญจะโดนแย่งทรัพยากรหรือไม่
ถ้ามี webhook จาก provider ยิงเข้ามาเป็น burst ช่วงเวลาเดียว ระบบด้านในจะรับไหวแค่ไหน

ปัญหาเหล่านี้ไม่ได้มีแต่เรื่อง attacker อย่างเดียว หลายครั้งมันเกิดจากผู้ใช้จริง integration จริง หรือ behavior ปกติของระบบ distributed ที่มี retry, reconnect, fan-out และ partial failure

ตรงนี้เองที่ Rate Limiting เข้ามามีบทบาท

บทความนี้อธิบายว่า rate limiting คืออะไร ช่วยแก้ปัญหาอะไร และควรวางไว้ตรงไหนของระบบเพื่อให้ป้องกันได้จริงโดยไม่กลายเป็นแค่ตัวเลขที่ตั้งไว้สวย ๆ

TL;DR

สรุปให้สั้นที่สุดได้แบบนี้

Rate limiting คือการกำหนดเพดานการเรียกใช้งานภายในช่วงเวลาที่กำหนด เพื่อกัน abuse, ลด load ที่ไม่จำเป็น, และปกป้องทรัพยากรสำคัญของระบบ

แต่ rate limiting ที่ดีไม่ได้มีไว้แค่ “กันคนยิงเยอะ” มันต้องถูกวางในชั้นที่เหมาะกับความเสี่ยงของระบบด้วย

Rate Limiting คืออะไร

Rate limiting คือกลไกที่ใช้ควบคุมจำนวน request หรือ operation ที่ผู้ใช้, client, token, IP หรือ service หนึ่ง ๆ สามารถทำได้ภายในช่วงเวลาที่กำหนด

ตัวอย่างเช่น

  • 100 requests ต่อนาที ต่อ IP
  • 10 login attempts ต่อ 15 นาที ต่อ account
  • 5 payment creation attempts ต่อ 1 นาที ต่อ user
  • 1 password reset email ต่อ 60 วินาที ต่อ email address
  • 1000 API calls ต่อวัน ต่อ API key

เป้าหมายของมันไม่ใช่แค่บล็อกคนใช้งาน แต่เพื่อรักษาเสถียรภาพของระบบและลดโอกาสที่ resource สำคัญจะถูกกินไปโดย request กลุ่มเดียว

ทำไมระบบ production ต้องมี rate limiting

ในระบบจริง ปัญหาที่ rate limiting ช่วยได้มีหลายแบบ

แบบแรกคือกัน traffic ที่ถี่เกินไปจาก client ปกติ เช่น app retry รัวเพราะ network ไม่นิ่ง หรือ frontend เรียก endpoint เดิมซ้ำโดยไม่ได้ตั้งใจ

แบบที่สองคือกัน abusive behavior เช่น bot, brute force, scraping, credential stuffing, spam submit หรือ public endpoint ที่ถูกยิงเพื่อกินทรัพยากร

แบบที่สามคือช่วยแยกความสำคัญของทรัพยากรในระบบ เช่น endpoint ที่แพงมากอย่าง search, export, upload, AI inference, report generation หรือ payment flow ไม่ควรถูกเรียกได้แบบไม่มีเพดาน

แบบที่สี่คือช่วยป้องกัน downstream service ของคุณเอง เช่น database, Redis, queue, third-party API หรือ internal service อื่น ๆ จาก burst ที่ไม่ควรปล่อยผ่าน

ดังนั้น rate limiting ไม่ใช่แค่เรื่อง security แต่เป็นทั้งเรื่อง reliability และ cost control ด้วย

Rate Limiting ไม่ได้แทนสิ่งอื่น

จุดที่สับสนกันบ่อยคือหลายทีมคิดว่าใส่ rate limiting แล้วแปลว่าปลอดภัยขึ้นครบทุกมิติ ซึ่งไม่จริง

Rate limiting ไม่ได้แทน authentication
Rate limiting ไม่ได้แทน authorization
Rate limiting ไม่ได้แทน validation
Rate limiting ไม่ได้แทน idempotency
Rate limiting ไม่ได้แทน webhook signature verification

มันเป็นคนละชั้นกัน

ตัวอย่างเช่น ถ้า endpoint POST /payments ไม่มี idempotency แล้ว client retry ซ้ำ Rate limiting อาจช่วยลดความถี่ได้ แต่ไม่ได้รับประกันว่า request ซ้ำจะไม่สร้าง side effect ซ้ำ

หรือถ้า webhook endpoint ไม่มี signature verification ต่อให้มี rate limit ก็ยังไม่ได้ยืนยันว่า request ที่เข้ามาเป็นของจริงจาก provider

ควรวาง Rate Limiting ตรงไหนของระบบ

นี่คือคำถามสำคัญที่สุดข้อหนึ่ง เพราะหลายระบบมี rate limiting อยู่จริง แต่ไปวางผิดชั้นจนช่วยได้ไม่มาก

คำตอบคือ ไม่มีตำแหน่งเดียวที่ถูกเสมอไป ระบบจริงมักต้องมีมากกว่าหนึ่งชั้น

ชั้นหน้า: Edge หรือ API Gateway

ชั้นนี้เหมาะกับการกัน traffic กว้าง ๆ ก่อนเข้าระบบ เช่น

  • per-IP limit
  • per-path limit
  • burst protection
  • bot-like behavior เบื้องต้น
  • กัน request volume ที่ไม่ควรเข้ามาถึง app

ข้อดีคือช่วยกัน load ได้ตั้งแต่ต้นทาง และลดภาระ application server ได้มาก

แต่ข้อจำกัดคือชั้นนี้มักรู้ context ธุรกิจไม่ลึกพอ เช่นไม่รู้ว่า user คนนี้เป็นใครในระดับ business identity หรือ operation นี้แพงเพราะอะไร

ชั้น application

ชั้นนี้เหมาะกับการใช้ context ที่ลึกขึ้น เช่น

  • per-user
  • per-account
  • per-tenant
  • per-API key
  • per-operation
  • per-resource

ตัวอย่างเช่น

  • จำกัดการส่ง OTP ต่อหมายเลขโทรศัพท์
  • จำกัดการสร้าง payment ต่อ user
  • จำกัดการ export report ต่อ tenant
  • จำกัดการอัปโหลดเอกสารต่อ account

ข้อดีคือผูกกับ logic ธุรกิจได้ดี และแยก policy ตาม endpoint หรือ operation ได้ละเอียดกว่า

ชั้น downstream หรือ resource-specific protection

บางกรณีควรมีการป้องกันเฉพาะจุดเพิ่ม เช่น

  • queue concurrency limit
  • DB connection pool guard
  • per-provider throttle ก่อนเรียก third-party API
  • per-job type rate guard
  • email sending throttle
  • SMS sending throttle

ชั้นนี้สำคัญมากเมื่อ endpoint หนึ่งไม่ได้แพงที่ app layer แต่อาจไปแพงที่ downstream

ถ้าจะเริ่มให้ถูก ควรคิดจาก “อะไรแพง” และ “อะไรเสี่ยง”

แทนที่จะถามแค่ว่า “endpoint ไหนควร limit” ให้ถามว่า

  • operation ไหนสร้าง side effect สำคัญ
  • operation ไหนกินทรัพยากรสูง
  • operation ไหนถ้าถูกยิงซ้ำจะสร้างความเสียหาย
  • operation ไหนมักโดน brute force หรือ spam
  • operation ไหนผูกกับ cost จริง เช่น AI, SMS, email, OCR, export, query หนัก

เมื่อคิดจากคำถามพวกนี้ คุณจะออก policy ได้แม่นกว่าการตั้ง limit เท่ากันหมดทุก route

ตัวอย่าง endpoint ที่ควรมี rate limiting อย่างจริงจัง

บาง endpoint ควรระวังเป็นพิเศษมากกว่า endpoint ทั่วไป

  • login
  • password reset
  • OTP send / OTP verify
  • signup
  • contact form
  • file upload
  • search ที่ query หนัก
  • report/export
  • AI generation
  • payment creation
  • refund request
  • webhook ingestion
  • public APIs

แต่แม้ชื่อ endpoint จะเหมือนกัน ระบบแต่ละตัวก็ยังต้องตั้งค่าต่างกัน เพราะ pattern การใช้งานและต้นทุนจริงไม่เท่ากัน

จะ limit ตามอะไรดี

คำตอบขึ้นอยู่กับประเภทของ endpoint และความเสี่ยง

ต่อ IP

เหมาะกับ public endpoints ระดับต้น เพราะใช้ได้แม้ยังไม่รู้ตัวตนผู้ใช้ชัดเจน แต่ข้อเสียคือผู้ใช้หลายคนอาจแชร์ IP เดียวกันได้

ต่อ user หรือ account

เหมาะกับ endpoint ที่ผ่าน auth แล้ว และช่วยสะท้อนพฤติกรรมผู้ใช้จริงได้ดีกว่า IP

ต่อ API key หรือ client id

เหมาะกับ public API หรือ partner integrations ที่มี credential ชัดเจน

ต่อ tenant

เหมาะกับระบบ B2B หรือ multi-tenant ที่ต้องการ fairness ระหว่างลูกค้าแต่ละราย

ต่อ resource หรือ operation เฉพาะ

เหมาะกับ action ที่แพงหรือเสี่ยงมาก เช่นส่ง OTP, สร้าง invoice, export รายงาน, ยิง OCR, สร้าง AI job

ในระบบจริง มักไม่ได้เลือกอย่างใดอย่างหนึ่ง แต่ใช้หลายแบบซ้อนกัน

Rate Limiting ต่างจาก Throttling ยังไง

บางคนใช้สองคำนี้แทนกัน แต่ในทางปฏิบัติความหมายมักใกล้กันโดยมีน้ำหนักต่างกันเล็กน้อย

  • Rate limiting มักหมายถึงการกำหนดเพดานว่าภายในช่วงเวลาหนึ่งทำได้กี่ครั้ง
  • Throttling มักหมายถึงการทำให้การไหลของ request ช้าลงหรือจำกัดอัตราเพื่อควบคุม load

ในหลายระบบ เครื่องมือเดียวกันอาจทำทั้งสองอย่าง เพียงแต่สิ่งสำคัญจริง ๆ คือ behavior ที่ผู้ใช้เจอและผลกระทบต่อทรัพยากร ไม่ใช่คำเรียก

ถ้าตั้ง limit ผิด จะเกิดอะไรขึ้น

ถ้าตั้ง limit สูงเกินไป มันแทบไม่ช่วยป้องกันอะไร

ถ้าตั้งต่ำเกินไป ผู้ใช้จริงจะโดนบล็อก ทั้งที่ไม่ได้ทำอะไรผิด โดยเฉพาะ mobile users, shared network, office NAT, หรือ integration ที่มี burst ปกติ

ถ้าตั้งแบบเดียวกันทุก endpoint คุณจะปกป้อง endpoint ราคาถูกพอ ๆ กับ endpoint ราคาแพง ซึ่งไม่สมเหตุผล

ถ้าตั้งไว้แค่ edge แต่ไม่ผูกกับ business identity ระบบอาจยังโดนยิงจาก account เดียวกันผ่านหลาย IP ได้อยู่

ดังนั้น rate limiting ที่ดีต้องมาจากการเข้าใจ traffic pattern จริง ไม่ใช่เดาตัวเลขแล้วหวังว่าจะพอดีเอง

ตัวอย่างแนวคิด policy ที่ใช้งานได้จริง

แนวคิดที่ใช้งานได้จริงมักดูประมาณนี้

  • login: limit ค่อนข้างเข้ม ต่อ IP และต่อ account
  • password reset: limit ต่อ email และต่อ IP
  • payment creation: limit ต่อ user และบังคับ idempotency
  • file upload intent: limit ต่อ user, ต่อ IP และต่อ file size policy
  • AI generation: limit ต่อ user หรือ tenant ตามแพ็กเกจ
  • webhook endpoint: limit แบบระวังมาก เพราะ provider อาจ burst เป็นปกติ ต้องผูกกับ signature verification และ dedup ด้วย
  • export report: limit ต่อ tenant เพราะเป็นงานแพงและอาจกิน DB หรือ background jobs

Webhook ต้องมี rate limiting ไหม

ควรมี แต่ต้องเข้าใจธรรมชาติของมัน

Webhook ต่างจาก login หรือ public form เพราะ provider ที่ถูกต้องก็อาจส่ง request แบบ burst ได้ โดยเฉพาะเวลา retry หรือมี event จำนวนมากเกิดติดกัน ดังนั้นถ้าตั้ง limit แบบตัดทิ้งแข็ง ๆ โดยไม่คิด flow จริง คุณอาจบล็อก event ถูกต้องของตัวเอง

สำหรับ webhook ที่ปลอดภัยกว่า ควรมีอย่างน้อยสามชั้นร่วมกัน

  • signature verification
  • event deduplication
  • rate awareness หรือ burst handling ที่เหมาะกับ provider

กล่าวอีกแบบคือ rate limit สำหรับ webhook ต้องออกแบบด้วยความเข้าใจ provider behavior ไม่ใช่ตั้งเลขสุ่ม ๆ แล้วจบ

Idempotency กับ Rate Limiting ต้องมีทั้งคู่ไหม

บ่อยครั้งคำตอบคือใช่ โดยเฉพาะ endpoint ที่มี side effect

Rate limiting ช่วยลดความถี่และปกป้องทรัพยากร
Idempotency ช่วยกัน request เดิมไม่ให้สร้าง side effect ซ้ำ

เช่นใน POST /payments

ถ้ามี rate limiting อย่างเดียว แต่ไม่มี idempotency request เดิมที่ผ่าน limit อาจยัง charge ซ้ำได้
ถ้ามี idempotency อย่างเดียว แต่ไม่มี rate limiting ระบบอาจยังโดนยิงจน resource หนักเกินไป

ทั้งสองอย่างจึงทำหน้าที่ต่างกันและควรใช้ร่วมกันในหลายกรณี

Request ID สำคัญกับการ debug rate limiting มาก

เวลาผู้ใช้บอกว่า “ทำไมโดน 429” หรือทีมภายในเจอว่า integration บางตัวเริ่ม fail การมี request_id และ structured logging จะช่วยมาก เพราะคุณจะตอบคำถามได้ว่า

  • limit rule ไหนเป็นคนตัด
  • ตัดตาม key แบบไหน เช่น IP, user, tenant, API key
  • window เท่าไร
  • count ตอนนั้นอยู่ที่เท่าไร
  • request มาจากเส้นทางไหน
  • มี burst จาก source เดิมต่อเนื่องหรือไม่

ถ้าไม่มีข้อมูลพวกนี้ ทีมมักต้องเดาจากอาการ ซึ่งแก้ปัญหายากและมักจบด้วยการ “เพิ่ม limit ไปก่อน” แบบไม่รู้ว่าจริง ๆ เกิดอะไรขึ้น

ตัวอย่าง Express/Node.js แบบเริ่มต้น

ตัวอย่างนี้ตั้งใจสาธิตโครงเบื้องต้นของ application-level rate limiting โดยผูก limit กับ user หรือ IP แบบง่าย ๆ เพื่อให้เห็นหลักคิดก่อน

const express = require("express");
const crypto = require("crypto");

const app = express();
app.use(express.json());

/**
 * demo only:
 * production ควรใช้ Redis หรือ durable shared store
 */
const rateStore = new Map();

app.post("/payments", (req, res) => {
  try {
    const requestId = req.get("X-Request-Id") || `req_${crypto.randomUUID()}`;
    const actorId = req.get("X-Actor-Id") || null;
    const ip = req.ip || "unknown";

    const rateKey = actorId ? `payment:user:${actorId}` : `payment:ip:${ip}`;
    const limit = 5;
    const windowMs = 60 * 1000;

    const decision = checkRateLimit(rateKey, limit, windowMs);

    if (!decision.allowed) {
      console.warn("Rate limit exceeded", {
        requestId,
        rateKey,
        retryAfterSeconds: decision.retryAfterSeconds
      });

      res.set("Retry-After", String(decision.retryAfterSeconds));
      return res.status(429).json({
        error: "Too many requests",
        requestId
      });
    }

    return res.status(201).json({
      success: true,
      requestId,
      message: "Payment request accepted"
    });
  } catch (error) {
    console.error("Payment route failed:", error);
    return res.status(500).json({
      error: "Internal server error"
    });
  }
});

function checkRateLimit(key, limit, windowMs) {
  const now = Date.now();
  const current = rateStore.get(key);

  if (!current || now >= current.resetAt) {
    rateStore.set(key, {
      count: 1,
      resetAt: now + windowMs
    });

    return {
      allowed: true,
      remaining: limit - 1,
      retryAfterSeconds: 0
    };
  }

  if (current.count >= limit) {
    return {
      allowed: false,
      remaining: 0,
      retryAfterSeconds: Math.max(1, Math.ceil((current.resetAt - now) / 1000))
    };
  }

  current.count += 1;
  rateStore.set(key, current);

  return {
    allowed: true,
    remaining: limit - current.count,
    retryAfterSeconds: 0
  };
}

app.listen(3000, () => {
  console.log("Server listening on port 3000");
});

โค้ดชุดนี้กำลังช่วยอะไร

ตัวอย่างนี้ไม่ได้พยายามทำระบบ rate limiting ครบทุกกรณี แต่กำลังสาธิตหลักสำคัญบางอย่าง

อย่างแรกคือการผูก limit กับตัวตนบางอย่าง ไม่ใช่แค่ปล่อยทุก request เท่ากันหมด
อย่างที่สองคือการแยก key สำหรับ operation เฉพาะ เช่น payment:user:<id>
อย่างที่สามคือการคืน 429 Too Many Requests พร้อม Retry-After เพื่อให้ behavior ชัดเจนขึ้น

ที่สำคัญ ตัวอย่างนี้แสดงให้เห็นว่าการ limit ควรอยู่ใกล้กับความหมายของ operation ด้วย ไม่ใช่คิดแค่ว่า “ทุก route จำกัด 100 ครั้งต่อนาทีเท่ากันหมด”

จุดที่ต้องระวังใน production

1) อย่าใช้ in-memory store จริง

Map ใช้สอนได้ดี แต่ production ไม่ควรใช้ เพราะถ้ามีหลาย instance หรือ process restart ข้อมูลจะไม่สอดคล้องกัน ควรใช้ shared store เช่น Redis หรือเครื่องมือ rate limiting ที่รองรับ distributed deployment

2) อย่าคิดว่า per-IP อย่างเดียวพอ

บางระบบอยู่หลัง NAT, mobile carrier network หรือ office proxy ทำให้ผู้ใช้หลายคนแชร์ IP เดียวกัน ถ้าใช้ per-IP อย่างเดียวอาจ block ผู้ใช้จริงเกินจำเป็น

3) อย่าตั้ง policy เดียวทั้งระบบ

login, payment, AI generation, file upload, และ report export มี risk กับ cost ไม่เท่ากัน ควรแยก policy ให้เหมาะกับ operation

4) อย่าลืม log decision ให้ตามย้อนหลังได้

ถ้ามี 429 เยอะขึ้น แต่ไม่มี log ว่าโดน rule ไหนตัด การ debug จะยากมาก

5) ระวัง false positive จาก retry behavior

mobile app, job worker, partner integration และ webhook provider อาจมี retry pattern ที่เป็นปกติ ถ้าไม่เข้าใจ behavior เหล่านี้ คุณอาจตั้ง limit จนระบบตัวเอง fail ทั้งที่ traffic ไม่ได้เป็นอันตราย

แล้วควรตอบกลับอะไรเมื่อโดน limit

โดยทั่วไป 429 Too Many Requests เป็น status code ที่เหมาะที่สุด และควรคืนข้อมูลที่อธิบายได้ เช่น

  • request ถูกจำกัดเพราะอะไร
  • ควรรอประมาณกี่วินาที
  • request id คืออะไร
  • บางกรณีอาจส่ง header เช่น Retry-After

จุดสำคัญไม่ใช่แค่ตอบ 429 แต่ต้องทำให้ client หรือ integration เข้าใจได้ว่าจะ recover อย่างไร

รีวิวแนวทางนี้แบบ production-minded

Correctness

rate limiting ที่ดีช่วยให้ระบบแยก request ที่ควรรับกับ request ที่ถี่เกินไปได้ โดยไม่ทำให้ operation สำคัญล้มง่ายจาก traffic ก้อนเดียว

Security

มันช่วยลด abuse ได้จริง แต่ไม่ควรถูกมองเป็น security control ตัวเดียว ต้องทำงานร่วมกับ authentication, validation, signature verification และ idempotency ตามบริบท

Efficiency

ถ้าวางถูกชั้น มันช่วยลด load ต่อ app, database และ downstream services ได้มาก โดยเฉพาะ endpoint ที่แพงหรือโดนเรียกบ่อย

Error handling

ควร log decision ทุกครั้งที่ block และควรอธิบาย behavior ให้ client ชัด ไม่อย่างนั้นผู้เรียกจะ retry รัวกว่าเดิมแล้วทำให้แย่ลง

Checklist สั้น ๆ ก่อนปล่อย rate limiting ขึ้น production

  • รู้ว่า operation ไหนแพงและ operation ไหนเสี่ยง
  • แยก edge-level กับ application-level policy ออกจากกัน
  • เลือก key ที่เหมาะ เช่น IP, user, tenant, API key หรือ operation-specific key
  • endpoint ที่มี side effect สำคัญใช้ร่วมกับ idempotency เมื่อเหมาะสม
  • webhook flow ใช้ร่วมกับ signature verification และ deduplication
  • มี 429 และ Retry-After ที่สม่ำเสมอ
  • มี structured logging พร้อม request_id
  • ใช้ shared store ในระบบหลาย instance
  • ไม่ใช้ตัวเลขเดียวกันทั้งระบบแบบไม่ดูบริบท
  • ทบทวน policy ตาม traffic จริง ไม่ใช่ตั้งครั้งเดียวแล้วลืม

บทความที่ควรอ่านต่อ

สรุป

Rate limiting ไม่ใช่ของตกแต่งสำหรับ API แต่เป็นหนึ่งในกลไกพื้นฐานที่ช่วยให้ระบบ production คุมพฤติกรรมการเรียกใช้งานได้อย่างมีวินัยมากขึ้น

มันช่วยกัน abuse ลด load ที่ไม่จำเป็น และป้องกันไม่ให้ operation สำคัญถูกแย่งทรัพยากรจนระบบส่วนอื่นเสียไปด้วย แต่จะได้ผลก็ต่อเมื่อคุณวางมันไว้ในชั้นที่เหมาะกับความเสี่ยงและต้นทุนจริงของ operation นั้น

สรุปสั้นที่สุดอีกครั้ง

rate limiting ไม่ได้มีไว้แค่กันยิงเยอะ แต่มันมีไว้ปกป้องทรัพยากรสำคัญของระบบอย่างมีบริบท

💬 Chat (ตอบเร็ว)