EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Security / AI crawler ยิง ?add-to-cart= จนโหลดพุ่ง — บล็อกยังไงไม่ให้เสีย SEO
Security

AI crawler ยิง ?add-to-cart= จนโหลดพุ่ง — บล็อกยังไงไม่ให้เสีย SEO

โดย Ton-Or ·12 ส.ค. 2026·อ่าน 10 นาที
AI crawler ยิง ?add-to-cart= จนโหลดพุ่ง — บล็อกยังไงไม่ให้เสีย SEO

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

สรุปใน 1 นาที

อาการ/ปัญหา
โหลดเซิร์ฟเวอร์ WooCommerce พุ่ง เพราะ AI crawler ตัวเดียวกด add-to-cart ทุกสินค้า คิดเป็น 84% ของทราฟฟิก
ใครควรอ่าน
คนดูแลร้าน WooCommerce ที่โหลดพุ่งผิดปกติและสงสัยว่าโดนบอทเดินเว็บ
ทางแก้แบบสั้น
เปิด log หาตัวต้นเหตุ แล้วปิดเป็นชั้น robots.txt, rel=nofollow, nginx limit_req เฉพาะเส้นทาง add-to-cart และ 403 หน้าส่วนตัว หน้าสินค้ายัง index ครบ
ข้ามไปขั้นตอน

พอดีเช้าวันหนึ่งได้เมลเตือนจาก DirectAdmin ว่า load เฉลี่ยเกิน threshold แล้วในเมลชี้ว่าตัวกิน CPU คือ PHP-FPM pool ของร้านค้าร้านหนึ่งกับ mysql ตอนแรกผมนึกว่าโดนยิง แต่พอเปิด access log ของ vhost นั้นมาไล่จริง ๆ กลับเจอว่ามันไม่ใช่การโจมตีเลย เป็นบอทเก็บข้อมูล AI ตัวเดียวที่ไล่คลิกลิงก์ ?add-to-cart= ของสินค้าทุกตัวในร้าน ผมเลยอยากเอาเคสนี้มาเล่าเป็นแนวทาง เพราะร้านค้า WooCommerce ทุกร้านมีช่องนี้เปิดอยู่เหมือนกันหมด

ชื่อโดเมน ชื่อ user และ IP ในบทความเป็นของสมมติทั้งหมด (shop.example, shopuser, 203.0.113.x) ส่วนโครงสร้าง config ตัวเลข และผลทดสอบเป็นของจริงจากเครื่องที่ผมดูแลอยู่

เปิด log ดูก่อนว่าใครยิง อย่าเพิ่งเดา

คำสั่งชุดแรกที่ผมใช้ทุกครั้งเวลาโหลดพุ่งคือไล่นับ IP กับ user-agent ในไฟล์ log ของ vhost นั้นตรง ๆ ไม่ต้องพึ่งเครื่องมืออะไรเลย

LOG=/var/log/nginx/domains/shop.example.log

# ใครยิงเยอะสุด 10 อันดับแรกของวันนี้
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | head -10

# IP นั้นเป็นใคร ใช้ UA อะไร
grep '^203.0.113.45 ' $LOG | awk -F'"' '{print $6}' | sort | uniq -c | sort -rn | head -3

# มันไปกดอะไรบ้าง
grep '^203.0.113.45 ' $LOG | awk '{print $7}' | cut -d'?' -f2 | cut -d'&' -f1 \
  | sort | uniq -c | sort -rn | head -10

# สถานะที่ตอบกลับไป
grep '^203.0.113.45 ' $LOG | awk '{print $9}' | sort | uniq -c | sort -rn

ผลที่ได้ออกมาชัดจนไม่ต้องตีความ

75,868request ต่อวันจาก IP เดียว
84%ของทราฟฟิกทั้ง vhost วันนั้น
43,570ครั้งที่ตอบ 302 (redirect หลัง add-to-cart)
12,052ครั้งที่วิ่งเข้า /cart/ ตามหลัง

user-agent เป็นบอทเก็บข้อมูลของค่าย AI เจ้าหนึ่ง ไม่ใช่ UA ปลอม ไม่ได้ยิงถี่แบบ flood ด้วยซ้ำ มันแค่ขยัน และมันเดินตามลิงก์ที่ธีมของร้านวางไว้ให้เดินเท่านั้นเอง

ตัวเครื่องเองไม่ได้ใกล้ตายนะครับ 16 core โหลดขึ้นไป 10 คิดเป็นราว 63% RAM ว่างอีก 41 GB อัตรา 5xx ทั้งวันอยู่ที่ 0.3% แปลว่า threshold ที่ตั้งเตือนไว้ต่ำเกินไปด้วยส่วนหนึ่ง (ผมขยับจาก 6 ไปเป็น 14 ทีหลัง) แต่ที่ต้องแก้จริง ๆ คือทำไม request เจ็ดหมื่นกว่าอันนั้นถึงแพงขนาดนั้น

ทำไม ?add-to-cart= ถึงแพงกว่าหน้าอื่นทั้งเว็บ

หน้าสินค้าหรือหน้าแรกของร้าน ถ้ามี page cache อยู่ บอทกดเท่าไหร่ก็ไม่เจ็บ เพราะได้ HTML สำเร็จรูปกลับไป ไม่แตะ PHP ไม่แตะ MySQL แต่ ?add-to-cart= เป็นคนละเรื่องกันคนละโลก

หน้าสินค้าปกติ

cache ได้ ทุกคนได้ก้อนเดียวกัน

ไม่มี cookie เฉพาะคน ตอบจาก cache ได้เลย บอทกดพันครั้งก็ยังเบา และนี่คือหน้าที่เราอยากให้ Google เก็บไปทำ index

?add-to-cart=

ผูกกับ session รายคน cache ไม่ได้

ต้องสร้าง session ใหม่ เขียนตะกร้าลง DB แล้ว 302 กลับ ตามด้วย /cart/ และ wc-ajax=get_refreshed_fragments อีกดอก เท่ากับ boot WordPress + WooCommerce เต็มก้อนทุกครั้ง

หนึ่งคลิกของบอท = งานฝั่งเซิร์ฟเวอร์กี่ต่อ?add-to-cart=ไม่มี cache→boot WP+WCPHP-FPM 1 slot→เขียน sessionMySQL write→302 → /cart/บูตซ้ำอีกรอบ→wc-ajax fragmentsรอบที่สาม

คิดเลขคร่าว ๆ ก็พอเห็นภาพ สินค้าในร้านมีหลักพันตัว บอทเดินครบหนึ่งรอบเท่ากับสั่งให้เครื่องบูต WooCommerce เป็นหมื่นครั้ง ทั้งที่ไม่มีคนซื้อสักคน pool ของ user นั้นตั้ง pm.max_children = 10 ไว้ พอ 10 slot ถูกจองด้วยงานตะกร้าหมด ลูกค้าจริงที่เข้ามาก็ต้องรอคิว นี่คือที่มาของอาการ "เว็บอืดเป็นพัก ๆ" ที่เจ้าของร้านแจ้งมาพอดี

ชั้นที่ 1 robots.txt สำหรับบอทที่ยอมฟัง

ผมเริ่มจากชั้นที่ถูกที่สุดก่อนเสมอครับ ร้านนี้ไม่ได้ลงปลั๊กอิน SEO ที่ยึด robots.txt ไว้ เลยเขียนเป็นไฟล์จริงลงไปได้เลย ถ้าใครใช้ปลั๊กอิน SEO อยู่ต้องไปแก้ในหน้าปลั๊กอินแทน ไม่งั้นไฟล์จริงจะไม่ถูกใช้

# /home/shopuser/domains/shop.example/public_html/robots.txt
User-agent: *
Disallow: /*add-to-cart=
Disallow: /cart/
Disallow: /checkout/
Disallow: /my-account/
Disallow: /*?wc-ajax=
Disallow: /*?orderby=
Disallow: /*?add_to_wishlist=
Disallow: /*?yith_wcwl_

Sitemap: https://shop.example/sitemap_index.xml

จุดที่ผมอยากเน้นคือ ในลิสต์นี้ไม่มีบรรทัดไหนห้ามหน้าสินค้าหรือหน้าหมวดหมู่เลย ที่ห้ามทั้งหมดคือหน้าที่เป็นของส่วนตัวรายคน ซึ่งไม่ควรอยู่ในผลค้นหาอยู่แล้ว ต่อให้ Google เก็บไปก็เป็นหน้าตะกร้าว่างเปล่าที่ไม่มีประโยชน์กับใคร

# ตั้ง owner ให้ถูก ไม่งั้น DA/ปลั๊กอินเขียนทับไม่ได้ทีหลัง
chown shopuser:shopuser /home/shopuser/domains/shop.example/public_html/robots.txt
chmod 644 /home/shopuser/domains/shop.example/public_html/robots.txt

# เช็คว่าเสิร์ฟออกจริง ไม่ได้ถูกปลั๊กอินดักไว้
curl -s https://shop.example/robots.txt | head -20

บอท AI ตัวที่ก่อเรื่องรอบนี้เคารพ robots.txt ครับ หลังไฟล์ขึ้น ทราฟฟิกจากมันลดลงเห็นชัดในรอบเก็บข้อมูลถัดไป แต่ผมไม่หยุดแค่นี้ เพราะ robots.txt เป็นแค่ป้ายขอความร่วมมือ ไม่ใช่ประตูล็อก

robots.txt คือป้าย ไม่ใช่กำแพง

บอทที่มีชื่อเสียงจะเคารพ แต่บอทเก็บข้อมูลบางตัวอ่านแล้วไม่สนใจ ในเคสเดียวกันนี้ผมยังเห็นบอทของ search engine อีกเจ้าวิ่งเข้า ?wc-ajax= ต่อทั้งที่ปิดไปแล้วในไฟล์ ถ้าอยากได้ผลจริงต้องมีชั้นที่บังคับได้ตามมาด้วย

ชั้นที่ 2 ใส่ rel=nofollow ที่ตัวลิงก์เลย

ปัญหาต้นทางจริง ๆ คือธีมร้านค้าวางลิงก์ "หยิบใส่ตะกร้า" ไว้ในหน้ารายการสินค้าทุกใบ แล้วลิงก์พวกนั้นเป็น <a href> ธรรมดาที่บอททุกตัวมองว่า "เดินต่อได้" ผมเลยเขียน mu-plugin สั้น ๆ ไปยัด rel="nofollow" ให้ทุกลิงก์ในลูปสินค้า ไม่ต้องแตะโค้ดธีม และไม่หายตอนธีมอัปเดต

<?php
// wp-content/mu-plugins/shop-bot-nofollow.php
// ใส่ rel=nofollow ให้ปุ่ม add-to-cart ในหน้ารายการสินค้า (ไม่แตะหน้าสินค้าเดี่ยว)

add_filter('woocommerce_loop_add_to_cart_link', function ($html) {
    if (strpos($html, 'rel=') === false) {
        $html = str_replace('<a ', '<a rel="nofollow" ', $html);
    }
    return $html;
}, 20);

// ธีมบางตัวสร้างปุ่มผ่าน args แทน string ตรง ๆ เลยต้องดักทั้งสองทาง
add_filter('woocommerce_loop_add_to_cart_args', function ($args) {
    $args['attributes']['rel'] = 'nofollow';
    return $args;
}, 20);

ทำไมต้องเป็น mu-plugin ไม่ใช่ปลั๊กอินปกติ เพราะไฟล์ใน mu-plugins/ โหลดอัตโนมัติ ปิดจากหน้า admin ไม่ได้ และไม่มีใครเผลอ deactivate ทิ้งตอนไล่ debug ปัญหาอื่น ส่วนตัวผมชอบวางของที่ "ห้ามหาย" ไว้ตรงนี้หมด

เช็คผลด้วยการนับลิงก์ในหน้าจริง อย่าเช็คด้วยตาเปล่า

curl -s https://shop.example/shop/ \
  | grep -o 'add_to_cart_button[^>]*' | grep -c 'rel="nofollow"'
# 12   <- ครบทั้ง 12 ลิงก์ในหน้ารายการ

ชั้นที่ 3 nginx map + limit_req เฉพาะเส้นทางเดียว

สองชั้นแรกคือขอความร่วมมือ ชั้นนี้คือบังคับ แต่ผมไม่อยากตั้ง rate limit ทั้งเครื่องเพราะบนเครื่องนี้มีเว็บอื่นอีกแปดสิบกว่าโดเมนที่ไม่เกี่ยวอะไรด้วย เทคนิคที่ใช้คือทำ map ให้ key ของ zone เป็นค่าว่างสำหรับทุกกรณีที่ไม่ใช่เป้าหมาย เพราะ nginx จะข้าม limit_req ทันทีเมื่อ key ว่าง เท่ากับเป็น no-op ให้เว็บที่เหลือทั้งเครื่อง

# /etc/nginx/nginx-includes.conf  (DirectAdmin include ไฟล์นี้ไว้ใน http{} ให้แล้ว
# และไม่เขียนทับตอน rewrite config — เป็นที่วางของ http-context ที่ปลอดภัยที่สุดบน DA)

map $host$request_uri $addtocart_key {
    default "";
    "~*shop\.example.*add-to-cart="  $binary_remote_addr;
}

limit_req_zone $addtocart_key zone=addtocart:10m rate=2r/s;

แล้วเรียกใช้ในไฟล์ nginx เฉพาะโดเมนของ user นั้น ซึ่งบน DirectAdmin คือไฟล์ .cust_nginx ที่รอด DA rewrite

# /usr/local/directadmin/data/users/shopuser/domains/shop.example.cust_nginx
# |CUSTOM| = DA แทรกให้ที่หัว server block ทั้งขา :80 และ :443
limit_req zone=addtocart burst=5 nodelay;

จบด้วยการ reload อย่างมีสติ ทดสอบ syntax ก่อนเสมอ เพราะไฟล์นี้ถ้าพังคือทุกโดเมนบนเครื่องพังพร้อมกัน

nginx -t && systemctl reload nginx

# สั่งให้ DA เขียน config ของ user นั้นใหม่ให้รับไฟล์ cust_nginx
echo 'action=rewrite&value=nginx&user=shopuser' >> /usr/local/directadmin/data/task.queue
/usr/local/directadmin/dataskq d

ชั้นที่ 4 ตอบ 403 เฉพาะหน้าส่วนตัว หน้าสินค้ายัง index ได้ครบ

ผ่านไปไม่กี่วันก็เห็นบอทอีกเจ้าวิ่งเข้า ?wc-ajax= ทั้งที่ปิดใน robots.txt ไปแล้ว ผมเลยเพิ่มชั้นสุดท้ายเป็นการ 403 ตรง ๆ เงื่อนไขคือ "เป็นบอท" และ "กำลังเข้าหน้าที่เป็นของส่วนตัว" ต้องครบสองอย่างเท่านั้น

# ยังอยู่ใน /etc/nginx/nginx-includes.conf
map $http_user_agent $is_crawler {
    default 0;
    "~*(googlebot|bingbot|baiduspider|yandex|applebot|petalbot|gptbot|claudebot|bytespider|crawler|spider)" 1;
}

map "$is_crawler:$request_uri" $bot_private_block {
    default 0;
    "~^1:/(cart|checkout|my-account)(/|\?|$)"                 1;
    "~^1:/wp-login\.php"                                       1;
    "~^1:.*[?&](add-to-cart|wc-ajax|add_to_wishlist|yith_wcwl_)=" 1;
}
# shop.example.cust_nginx — วางไว้ระดับ server ไม่ใช่ใน location
if ($bot_private_block) { return 403; }
limit_req zone=addtocart burst=5 nodelay;

ที่ต้องวางระดับ server เพราะ if กับ return ทำงานใน rewrite phase คือทำงานก่อนจะเลือก location และก่อน proxy_pass ถ้าไปวางใน location ที่ proxy ต่อไปหา Apache มันจะยิงไม่ทัน

อย่าใส่คำว่า bot เดี่ยว ๆ ลงใน regex ของ UA

ผมเขียนชื่อบอทเต็มทุกตัวใน map ตั้งใจครับ ถ้าใส่ bot ลอย ๆ มันจะ match user-agent ของมือถือบางรุ่นที่มีคำนี้อยู่ในชื่อ (เช่นยี่ห้อที่ลงท้ายด้วย bot) ผลคือลูกค้าจริงที่ถือเครื่องรุ่นนั้นเปิดตะกร้าไม่ได้ทั้งร้าน แล้วอาการแบบนี้จะไม่มีใครแจ้งเข้ามาให้ตรง ๆ ด้วย เขาจะเลิกซื้อเฉย ๆ

ผลที่ต้องได้หลังปิดครบสี่ชั้น แยกเป็นสองฝั่งให้ชัด

ต้องได้ 403

เมื่อ UA เป็นบอท

/cart/ · /checkout/ · /my-account/ · /wp-login.php · ?add-to-cart= · ?wc-ajax= · wishlist ทั้งหมด

ต้องได้ 200 เหมือนเดิม

ทั้งบอทและคนจริง

/ · /shop/ · หน้าสินค้าทุกใบ · /sitemap.xml · /feed/ และ UA ของเบราว์เซอร์จริงต้องได้ 200 ทุกเส้นทางรวมถึงตะกร้า

กับดักตอนทดสอบ ที่ทำผมเสียเวลาไปหลายรอบ

ตรงนี้คือส่วนที่ผมอยากเล่าที่สุดในบทความนี้ เพราะมันกินเวลาผมไปหลายรอบกว่าจะรู้ตัว

หลังใส่ config เสร็จผมทดสอบด้วยวิธีที่ปกติใช้ได้เสมอ คือ curl --resolve ชี้โดเมนไปที่ 127.0.0.1 เพื่อไม่ต้องออกอินเทอร์เน็ต ผลคือได้ 200 หมดทุกอัน ทั้งที่ nginx -T ก็เห็น directive อยู่ครบ ผมลองใส่ add_header สำหรับ debug เข้าไปก็ไม่โผล่ ลองเพิ่ม location ทดสอบเข้าไปก็ได้หน้า 404 ของ WordPress กลับมาแทน ถึงตรงนั้นผมเกือบสรุปว่า .cust_nginx ใช้ไม่ได้แล้ว

ทำไม --resolve ไป 127.0.0.1 ถึงให้ผลลวงcurl 127.0.0.1ต่อเข้า loopback→server block อื่นตัว default ของเครื่อง→ไม่มี cust_nginxไม่มี if/limit_req→ได้ 200สรุปผิดว่าไม่ทำงาน

สาเหตุคือ server block ที่ DirectAdmin สร้างให้แต่ละโดเมนนั้น listen อยู่บนไอพีสาธารณะของเครื่อง ไม่ใช่ loopback พอเรายิงเข้า 127.0.0.1 คำขอเลยไปตกที่ server block ตัวอื่นซึ่งไม่มี directive ของโดเมนเราอยู่เลย ได้ 200 กลับมาแบบไม่ผิดอะไรสักอย่าง มันแค่ไม่ใช่บล็อกที่เราแก้

กฎที่ผมยึดตั้งแต่นั้น

ทดสอบ nginx ระดับโดเมนบน DirectAdmin ต้อง --resolve ไปที่ไอพีสาธารณะจริงของเครื่องเท่านั้น ถ้าจำเป็นต้องยิงจากตัวเครื่องเองก็ยังใช้ไอพีสาธารณะของมันได้ตามปกติ อย่าใช้ 127.0.0.1 เด็ดขาด เห็น 200 แล้วจะหลงทางยาว

ชุดทดสอบที่ถูกต้อง ผมใช้แบบนี้ ยิงขนานกัน 20 อันเพื่อดูว่า rate limit ทำงานจริงไหม

IP=203.0.113.10          # ไอพีสาธารณะของเซิร์ฟเวอร์ ไม่ใช่ 127.0.0.1
D=shop.example

# 1) add-to-cart 20 อันขนานกัน -> ต้องมีตัวที่โดน 429
for i in $(seq 1 20); do
  curl -s -o /dev/null -w "%{http_code}\n" --resolve $D:443:$IP \
    "https://$D/?add-to-cart=1234" &
done; wait | sort | uniq -c

# 2) หน้าแรก 20 อันขนานกัน -> ต้อง 200 ทั้งหมด ห้ามโดนหางเลข
for i in $(seq 1 20); do
  curl -s -o /dev/null -w "%{http_code}\n" --resolve $D:443:$IP "https://$D/" &
done; wait | sort | uniq -c

# 3) เทสต์ฝั่งบอท -> ต้อง 403 เฉพาะหน้าส่วนตัว
for p in /cart/ /checkout/ /my-account/ "/?wc-ajax=get_refreshed_fragments"; do
  printf '%s -> ' "$p"
  curl -s -o /dev/null -w "%{http_code}\n" -A "Googlebot/2.1" \
    --resolve $D:443:$IP "https://$D$p"
done

# 4) เทสต์ว่าหน้าที่ต้อง index ยังเข้าได้ -> ต้อง 200 ทั้งหมด
for p in / /shop/ /sitemap.xml /feed/; do
  printf '%s -> ' "$p"
  curl -s -o /dev/null -w "%{http_code}\n" -A "Googlebot/2.1" \
    --resolve $D:443:$IP "https://$D$p"
done

ผลจริงที่ได้จากรอบ 20 ขนาน

6 x 200ผ่านตาม rate 2r/s + burst 5
14 x 429ส่วนเกินโดนเบรกที่ nginx ไม่ถึง PHP
20 x 200หน้าแรกยิงพร้อมกัน ไม่กระทบเลย
0 x 5xxไม่มีอะไรพังระหว่างทดสอบ

ตัวเลข 429 ตรงนี้สำคัญกว่าที่คิดครับ เพราะ 429 คือคำตอบที่ nginx ตอบเองโดยไม่ต้องปลุก PHP-FPM เลย ต้นทุนต่อ request เหลือแทบเป็นศูนย์ ต่างจากตอนก่อนแก้ที่ทุก request ต้องบูต WooCommerce เต็มก้อน

ปิดท้ายที่ฝั่ง PHP-FPM และบทเรียนที่ผมพลาดเอง

อีกจุดที่คนชอบทำผิดคือพอเห็น pool เต็มแล้วรีบเพิ่ม pm.max_children ส่วนตัวผมมองว่าบน shared host การจำกัดจำนวนลูกคือเกราะป้องกันเว็บข้างเคียง ไม่ใช่ปัญหา เพิ่มไปมีแต่จะพากันจมทั้งเครื่อง ผมเลยคง 10 ไว้เท่าเดิม แล้วเติมแค่ timeout กันงานค้างแทน

; /usr/local/directadmin/data/users/shopuser/php/php-fpm84.conf
[shopuser]
pm = ondemand
pm.max_children = 10
pm.max_requests = 500
request_terminate_timeout = 60s
systemctl reload php-fpm84
ไฟล์ที่ DA สร้างให้ อาจโดนเขียนทับ

ทั้ง php-fpm<ver>.conf ของ user และไฟล์ config ฝั่งแคชที่ DA จัดการ เป็นไฟล์ที่ระบบอาจเขียนใหม่ตอนมีการแก้ค่าจากหน้าแผงควบคุม ผมเลยเก็บสำเนาต้นฉบับของทุกไฟล์ที่แก้ไว้นอกเครื่อง แล้วมีสคริปต์ไล่เช็คเป็นระยะว่าค่ายังอยู่ครบไหม ส่วนของที่ต้องรอดแน่ ๆ ให้ย้ายไปอยู่ใน nginx-includes.conf กับ .cust_nginx แทน สองตัวนี้ DA ไม่ยุ่งด้วย

อันนี้ผมพลาดเอง เล่าไว้กันคนอื่นพลาดซ้ำ

ตอนแก้เคสนี้ผมเห็นว่า object cache ของร้านเต็มอยู่ตลอด เลยขยายเพดานหน่วยความจำของแคชขึ้นไปหลายเท่าเพื่อลดภาระ MySQL ผลคือแก้ปัญหาตรงหน้าได้จริง แต่อีกไม่กี่สัปดาห์ถัดมาแคชตัวนั้นโตทะลุเพดานที่ระดับ cgroup ของ service กำหนดไว้ แล้วโดน kernel เบรกจนเว็บค้างตอนโพสต์สินค้า ตัวเลขข้างในแอปต้องต่ำกว่ากำแพงข้างนอกเสมอ ผมสรุปเป็นกฎ 80% แล้วเขียนแยกไว้อีกบทความหนึ่งครับ

สรุปลำดับที่ผมใช้กับเคสแบบนี้ ไล่จากถูกไปแพง ทำทีละชั้นแล้ววัดผลก่อนขึ้นชั้นถัดไปเสมอ

ชั้น 1-2 ขอความร่วมมือที่ฝั่งบอทrobots.txtปิดเฉพาะหน้าส่วนตัวrel=nofollowที่ลิงก์ตะกร้าในรายการบอทที่ฟังจะเลิกเดินเข้ามาชั้น 3-4 nginx บังคับrequest บอทชั้น 4UA บอท + หน้าส่วนตัวตอบ 403ชั้น 3limit_req 2r/sburst 5 เกินตอบ 429PHP-FPMmax_children 10เฉพาะที่ผ่านทดสอบ 20 ขนาน: 6 ตัวผ่านได้ 200 อีก 14 ตัวโดน 429 ที่ nginx ไม่ถึง PHP
สองชั้นแรกขอให้บอทเลิกเดินเข้าหน้าตะกร้า สองชั้นหลังให้ nginx บังคับ ตอบ 403 กับ 429 เองโดยไม่ปลุก PHP-FPM
  1. อ่าน log ให้ได้ตัวจริงก่อน ว่าเป็นบอทตัวไหน วิ่งเข้า path อะไร กี่ครั้ง อย่าเดาจากเมลเตือนของระบบ เพราะมันชี้แค่ปลายเหตุว่า PHP กับ MySQL กินเครื่อง
  2. robots.txt ปิดเฉพาะหน้าส่วนตัว ห้ามเผลอปิดหน้าสินค้าหรือหมวดหมู่ ทำแล้วรอดูรอบเก็บข้อมูลถัดไป
  3. ใส่ rel=nofollow ที่ลิงก์ตะกร้าในหน้ารายการสินค้าด้วย mu-plugin ตัดปัญหาที่ต้นทางว่าทำไมบอทถึงรู้จักลิงก์พวกนี้
  4. nginx limit_req แบบ map key ว่าง เพื่อให้มีผลเฉพาะโดเมนและเฉพาะ path ที่ต้องการ ไม่กระทบเว็บอื่นบนเครื่องเดียวกัน
  5. 403 เฉพาะเงื่อนไข บอท + หน้าส่วนตัว เท่านั้น แล้วทดสอบสองฝั่งเสมอ คือทั้งฝั่งที่ต้องโดนบล็อกและฝั่งที่ห้ามโดนบล็อก
  6. ทดสอบผ่านไอพีสาธารณะของเครื่อง ไม่ใช่ loopback แล้วเก็บชุดคำสั่งทดสอบไว้ใช้ซ้ำตอนมีคน rewrite config ทีหลัง

ที่อยากฝากไว้คือ อย่าเหมาว่าโหลดพุ่งเท่ากับโดนโจมตี เคสนี้ถ้าผมสั่งบล็อกทั้ง IP ไปตั้งแต่แรกก็จบเร็วเหมือนกัน แต่จะเสียโอกาสที่เนื้อหาสินค้าของร้านจะถูกเก็บไปใช้ตอบคำถามคนบนเครื่องมือ AI ด้วย ผมเลยเลือกทางที่ปิดเฉพาะประตูที่ไม่มีใครควรเดินเข้า แล้วเปิดหน้าร้านทิ้งไว้ให้ทุกคนตามเดิม

ใครจะเอาโครงนี้ไปใช้ ปรับ rate กับรายชื่อ UA ตามหน้างานได้ตามสะดวกครับ ถ้าร้านไหนใช้ปลั๊กอินแคชคนละตัวกัน path ที่ต้องปิดอาจไม่เหมือนกันเป๊ะ ให้ยึดหลักเดียวกันคือ "หน้าที่ผูกกับ session รายคน = ไม่ต้อง index" ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ </content> </invoke>

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย