- Tổng hợp cách chặn phần lớn bot có triển khai hoặc cấu hình kém bằng cách kết hợp giao thức HTTP/dải IP/tín hiệu client/đặc tính TCP/dấu vân tay TLS/nén nội dung mà không cần CDN
- Mọi phương pháp đều cần được điều chỉnh có chọn lọc; nếu không phân tích trước log truy cập 1~3 năm, bạn có thể chặn nhầm người dùng VPN/công cụ tìm kiếm/CDN/trường học/thư viện/người dùng một số ngôn ngữ
- Chặn client HTTP/1.1 và các dải AS/CIDR của trung tâm dữ liệu có thể loại bỏ nhiều bot, nhưng các công cụ tìm kiếm như GoogleBot và người dùng hợp lệ sử dụng trung tâm dữ liệu cũng có thể bị loại trừ
- Kiểm tra header bằng Nginx và bộ lọc kích thước cửa sổ TCP/MSS/TTL của nftables giúp giảm crawler và scanner đơn giản, nhưng có thể gây dương tính giả trong các môi trường hợp lệ như LTE/VPN/Windows
- Về dài hạn, bài viết đề xuất phát hiện dấu vân tay TLS JA4 và phản hồi chỉ dùng Brotli như các phương án thay thế, nhưng không bảo đảm chặn hoàn toàn và cảnh báo không dùng cho dịch vụ đang tạo doanh thu
Phạm vi chặn và tiền đề áp dụng
- Trước hết cần quyết định mục tiêu chặn là một số bot/phần lớn bot/tất cả bot
- Các phương pháp được đề cập ở đây không nhằm chặn toàn bộ công cụ tự động hóa tinh vi, mà tập trung vào việc chặn tương đối đơn giản phần lớn bot có triển khai hoặc cấu hình kém
- Với từng phương pháp, bài viết cũng nêu rủi ro chặn nhầm người dùng bình thường và công cụ tìm kiếm
- Sau khi được chia sẻ trên Hacker News ngày 26/7/2026, phần lớn chức năng chặn sẽ được chuyển từ blog sang một trang demo riêng, biến thành dạng câu đố để độc giả đọc cách làm rồi tự thử truy cập
- Mọi cấu hình có thể được chỉnh sửa hoặc bỏ qua có chọn lọc, và cần nghiên cứu cũng như kiểm thử đầy đủ trước khi áp dụng thực tế
- Người dùng hợp lệ/hệ thống nội bộ của tổ chức/dịch vụ bên ngoài đang phụ thuộc có thể bị chặn, nên trách nhiệm áp dụng hoàn toàn thuộc về người vận hành
- Không dùng trong môi trường vận hành đang tạo doanh thu
Cách 1: Phân biệt theo giao thức HTTP
- Rủi ro chặn người dùng bình thường thấp, rủi ro chặn một số công cụ tìm kiếm ở mức trung bình
- Tận dụng khác biệt rằng trình duyệt thông thường dùng HTTP/2.0, còn nhiều bot dùng HTTP/1.1
- GoogleBot được cho là dùng HTTP/1.1 và sẽ bị chặn bằng cách này
- Crawler của Bing và Facebook dùng HTTP/2.0
- Các dịch vụ lấy tiêu đề liên kết hoặc bản xem trước ngắn qua HTTP/1.1 cũng có thể bị chặn
- Opera Mini cũng bị loại khỏi đối tượng cho phép
- Trong Nginx, cấu hình để nếu
$server_protocol không phải HTTP/2.0 thì chuyển hướng sang trang khác hoặc trả về 200, 403, 444
if ($server_protocol != HTTP/2.0) {
return 403 'Upgrade your client';
}
- Trả về
444 có thể ngắt kết nối mà không gửi phản hồi riêng
- Mỗi tổ chức cần tự đánh giá lợi ích đạt được có lớn hơn thiệt hại khi chặn lưu lượng từ Google Search hay không
Cách 2: Chặn dải IP trung tâm dữ liệu
- Rủi ro chặn người dùng gia đình/LTE bình thường thấp, nhưng với người dùng VPN là mức trung bình, còn công cụ tìm kiếm chạy trong trung tâm dữ liệu có rủi ro bị chặn cao
- Tìm các yêu cầu đáng ngờ bằng cách kết hợp các tín hiệu sau trong log truy cập 1~2 năm gần đây
- Giao thức HTTP
- User-Agent
Accept-Language
Sec-Fetch-Mode
Accept
- Tra IP đáng ngờ trên BGP Tools hoặc Hurricane Electric BGP Toolkit để kiểm tra AS sở hữu và các Prefix đang được quảng bá
- Danh sách network blackhole được cung cấp có thể bao gồm cả dải CDN và công cụ tìm kiếm, nên cần áp dụng có chọn lọc
- Trước các danh sách riêng, cấu hình đang dùng đã blackhole các dải
3/8, 10/8, 11/8, 25/8, 26/8, 38/8, 41/8, 60/8, 61/8, 200/8, 224/3
- Sau khi sao chép trang Prefix của AS, dùng hàm shell để chỉ trích xuất CIDR rồi sắp xếp/xóa trùng/gộp
- Dùng sum_cidr.pl, cần module Perl
Net::CIDR::Lite
- Kết quả tạo ra được rà soát rồi chuyển vào các file
/usr/local/etc/*.netset
- Khi server khởi động, thêm từng CIDR làm tuyến blackhole
for CflIP in $(grep -E ^[1-9] /usr/local/etc/_cloudflare.netset); do
/sbin/ip route add blackhole "${CflIP}" 2>/dev/null
done
- Trong ví dụ, toàn bộ dải Cloudflare bị chặn để không nhận yêu cầu thông qua Workers và các dịch vụ tương tự
- Chọn cách định tuyến vì blackhole routing của Linux dùng ít CPU hơn so với quy tắc ipset của firewall
- Cũng có thể chặn dải của nhà cung cấp hosting nơi server hiện tại đang đặt
- DNS/cấu hình/dịch vụ nội bộ không được dùng cùng không gian địa chỉ đó
- Tuyến gateway kết nối trực tiếp được ưu tiên hơn tuyến blackhole
Cách 3: Chặn quốc gia/proxy/Tor/IP độc hại
- Tải các danh sách trong kho FireHOL Blocklists và thêm các dải cần thiết làm tuyến blackhole
- File danh sách có chứa chú thích, nên cần loại bỏ bằng
grep -Ev '^#' rồi xử lý
- Đặc biệt khuyến nghị các danh sách sau
firehol_abusers_30d.netset
firehol_level2.netset
- Danh sách lớn có thể làm script khởi động chạy lâu hơn
- Bài viết cũng cung cấp các file cấu hình đã dùng cho server thực tế và firewall
Cách 4: Kiểm tra tín hiệu HTTP client
- User-Agent hoặc header có thể bị giả mạo, nhưng giả định là các bot đơn giản ưu tiên tốc độ thường không giả mạo đúng cách
- Với yêu cầu
Curl, Wget thì trả về văn bản thuần; với yêu cầu chứa Bot, GPT, LLM, Spider thì trả về 410 Gone
if ($http_user_agent ~* Curl) { return 200 '\nGNU Terry Pratchett\n\n'; }
if ($http_user_agent ~* Wget) { return 200 '\nGNU Terry Pratchett\n\n'; }
if ($http_user_agent ~* Bot) { return 410 '1000101'; }
if ($http_user_agent ~* GPT) { return 410 '1000101'; }
if ($http_user_agent ~* LLM) { return 410 '1000101'; }
if ($http_user_agent ~* Spider) { return 410 '1000101'; }
- Kiểm tra một số chuỗi con User-Agent quan sát được bằng một regex dài để chặn crawler/scanner/công cụ thu thập
- Bao gồm các chuỗi con như
Go-http, Java, libwww, okhttp, urllib, python, nmap, zgrab, semrush, shodan, rss, scrap, crawler, headless, github, facebook, google, bing
- Trước khi áp dụng, cần tổng hợp User-Agent trong 2~3 năm để kiểm tra client hợp lệ thực tế có khớp regex hay không
sort access-user-agents.txt | uniq -c | sort
- Nếu yêu cầu khớp là HTTP/1.1 thì coi là nhiều khả năng là bot, nhưng cần cân nhắc ngoại lệ GoogleBot
- Nếu là yêu cầu HTTP/2.0, kiểm tra thêm IP thuộc về ai bằng công cụ BGP
Sec-Fetch-Mode
- Thêm
Sec-Fetch-Mode vào log truy cập và chặn nếu giá trị không phải một trong cors, no-cors, navigate
if ($http_sec_fetch_mode !~ (cors|no-cors|navigate)) {
return 410 '1000101';
}
Kiểm tra Referer
- Chặn nếu
Referer có một số chuỗi nhất định để ngăn yêu cầu nhúng hoặc quét nội dung từ site khác
- Kiểm tra các chuỗi liên quan đến trang quản trị/công cụ tìm kiếm/mạng xã hội/tiền mã hóa/nội dung người lớn/scanner/WordPress
- Một loại bot cụ thể tự nhận
Referer là trang gốc Google https://www.google.com/ cũng bị chặn riêng
- Đây là mẫu quan sát được ở các yêu cầu giả dạng Android cũ
Giới hạn HTTP method
- Chỉ cho phép
GET và POST cần thiết cho yêu cầu trình duyệt thông thường, chặn các method khác
if ($request_method !~ (^GET$|^POST)) {
return 410 '1000101';
}
- Trong ứng dụng thực tế, có thể giới hạn chi tiết hơn các đường dẫn cần
POST, hoặc loại bỏ hoàn toàn nếu không dùng
Kiểm tra dạng proxy và trình duyệt
- Nếu có header
X-Forwarded-For, coi là yêu cầu proxy và chặn
- Các môi trường proxy dùng chung hợp lệ như trường học hoặc thư viện cũng có thể bị chặn
- Nếu User-Agent không có chuỗi nào trong
Linux, BSD, Macintosh, Windows, Mozilla, WhatsApp, coi là yêu cầu không giống trình duyệt
- Cũng dùng quy tắc chặn nếu
Accept-Language không có en hoặc es
- Khả năng cao sẽ chặn một số trình duyệt và người dùng hợp lệ không dùng tiếng Anh/tiếng Tây Ban Nha
- Bài viết cũng đề xuất quy tắc tùy chọn để chặn riêng thiết lập ngôn ngữ chứa
br hoặc sy
Chặn quét đường dẫn nhạy cảm
- Nếu yêu cầu các file hoặc đường dẫn sau, coi là quét tự động và chặn
Cách 5: Chặn TCP scanner bằng nftables
- Kiểm tra đặc tính gói TCP SYN trong chain
PREROUTING của bảng raw của nftables
- Ghi rõ địa chỉ server ví dụ
172.238.221.88 làm đích để giảm dương tính giả có thể xảy ra khi mất gói
- Trong các gói SYN đi vào cổng 80/443, chặn các điều kiện sau
- Kích thước cửa sổ TCP dưới 12.288 byte
- MSS nằm ngoài khoảng 1.220~1.460
- Tiêu chí sử dụng là client thực tế dùng kích thước cửa sổ lớn hơn, và MSS ngoài phạm vi đó ít có khả năng là client hợp lệ
- Nếu giới hạn MSS chính xác là
1460, quy tắc sẽ nghiêm ngặt hơn nhưng có thể chặn phần lớn người dùng LTE và VPN
Giới hạn tùy chọn dựa trên TTL
- Nếu TTL của TCP SYN lớn hơn
128, có thể chặn phần lớn thiết bị LTE
- Nếu TTL lớn hơn
64, phần lớn hệ thống Windows cũng bị chặn
- TTL mặc định được mô tả như sau
- Linux/Mac/BSD:
64
- Windows:
128
- LTE: giá trị lớn hơn
Loại trừ connection tracking
- Đặt cổng 80/443 là
notrack để không đưa lưu lượng web vào bảng conntrack
- Trong trường hợp này, cũng phải tự cấu hình quy tắc stateless cho hướng gửi/nhận trong bảng filter
- Ví dụ cho phép lưu lượng giữa cổng nguồn client
1000-65535 và cổng 80/443 của server
Cách 6: Header nội dung người lớn và robot
- Dùng header RTA: Restricted To Adults để hạn chế truy cập nội dung người lớn
- Các phương thức xác minh độ tuổi ngoài RTA được xem là phục vụ theo dõi người dùng và kiếm tiền
- Luôn thêm các header sau vào phản hồi Nginx
add_header Rating 'RTA-5042-1996-1400-1577-RTA' always;
add_header adult 'porn, sex, politics, religion, philosophy' always;
add_header X-Robots-Tag "none,noindex,nofollow,nosnippet,noai" always;
- Bot thông thường có thể bỏ qua các header này, nhưng chúng có thể ảnh hưởng đến công cụ tìm kiếm hoặc bot được thiết kế để tránh nội dung người lớn
Cách 7: Phát hiện dấu vân tay TLS
- Các cách 1~6 ở trên đều là heuristic thô; về dài hạn, phân tích dấu vân tay TLS có thể là lựa chọn tốt hơn
- Có thể tận dụng JA4 với điều kiện bot không thay đổi dấu vân tay TLS để giống trình duyệt bình thường
- Trước tiên, bài viết hướng dẫn xem cách triển khai trong Deploying JA4, rồi áp dụng FoxIO JA4
Cách 8: Chỉ cung cấp nội dung nén Brotli
- Nén trước nội dung site bằng Brotli và cấu hình web server chỉ trả về file đã nén
- Tận dụng việc nhiều bot không diễn giải được HTML nén Brotli
- Sau khi áp dụng, nhiều bot không còn đi theo liên kết trên trang, cho thấy chúng thực tế không parse được HTML
- Trong Nginx, cấu hình để luôn cung cấp file Brotli tĩnh
brotli_static always;
- File HTML được nén trước như sau
cat ./i.html | brotli --best -fncv > ./i.html.br
Cách 9: Dẫn dụ scanner tự nhận diện
- Một phương pháp riêng để giảm scanner và kẻ tấn công mới liên tục dò tìm đường dẫn lỗ hổng được đề cập trong Help Attackers Self Report
1 bình luận
Ý kiến trên Hacker News
Từ góc nhìn của một người vận hành nhiều website công khai và cũng thu thập dữ liệu từ các site khác để dùng cho công cụ, tôi thắc mắc vì sao mọi người lại bận tâm đến bot đến vậy.
Ngay cả WordPress có cache cũng xử lý được khoảng 1.000 request mỗi giây trên VPS rẻ nhất, còn một site tĩnh được làm đúng cách có lẽ xử lý được gấp 10 lần như vậy. Tôi tự hỏi liệu họ đang phục vụ blog bằng thứ như Lambda, hay đó là sự ám ảnh, phòng thủ trước lỗ hổng, hoặc thói quen từ thời việc crawl thật sự ảnh hưởng đến dịch vụ
Repository là mã nguồn mở nên tôi cố ý để công khai. Hiện tôi phòng thủ bằng một kiểm tra cookie đơn giản và chỉ một số ít bot đi qua được, nhưng đó là sự đánh đổi phải hy sinh khả năng xuất hiện trên công cụ tìm kiếm
Mục đích là cho thấy cách áp dụng cho forum, image board, máy chủ chat, v.v.; mọi tùy chọn đều có thể điều chỉnh hoặc tắt. Trước khi áp dụng thật nên kiểm chứng trên máy chủ thử nghiệm, và nếu chỉ cười nhạo rồi bỏ qua cũng không sao
DMZ bị nghẽn khiến việc kiểm tra máy chủ IMAP cục bộ mất thêm vài giây không phải chuyện lớn, nhưng điều đó không có nghĩa là tôi thích hay có lý do để tiếp tục cho phép
Cuối tuần rồi tôi đã đóng các giao diện web viewvc(CVS·Subversion) và hgweb(Mercurial) đã vận hành 10–20 năm. Lý do là từ IP proxy dân dụng có 2,7 triệu request mỗi ngày, trung bình 30 request/giây, gây gánh nặng cho các chương trình uWSGI/CGI cũ và các site khác trên cùng máy chủ; lưu lượng cũng tiến gần giới hạn 1TB/tháng của VPS.
Vì các tổ hợp URL VCS động có thể lên tới hàng triệu nên hiệu quả cache cũng không chắc chắn, và không đáng bỏ thêm thời gian tinh chỉnh máy chủ, cuối cùng tôi đã chọn một bước tiến gần hơn tới Internet tập trung
Nếu chặn mọi thứ ngoài user agent “được phê duyệt”, bạn sẽ tiếp tay cho độc quyền trình duyệt hiện có và đẩy nhanh viễn cảnh dystopia. Đây cũng là vấn đề RMS đã cảnh báo từ nhiều thập kỷ trước.
Nếu có vấn đề thì nên chặn dựa trên lượng traffic và tần suất request. Bản thân tôi cũng không thể truy cập site, nhưng tôi không định thay đổi hành vi để phù hợp; giống DRM, đối thủ có quyết tâm cuối cùng cũng sẽ vượt qua
Tuy nhiên, tách khỏi lập luận rằng có thể dùng bất kỳ trình duyệt nào, cần đặc biệt cẩn trọng với trình duyệt được tạo ngẫu hứng hoặc chưa được kiểm chứng đầy đủ để chống lại site độc hại. Ứng dụng đọc cũng có thể dễ bị máy chủ độc hại tấn công nếu chưa trải qua rà soát mã bên thứ ba rộng rãi bởi chuyên gia kiểm thử xâm nhập
Tôi thích ý tưởng thêm một subdomain cpanel giả trỏ tới
169.254.169.254, khiến kẻ tấn công non tay tự port scan nhà cung cấp hosting của chính họ và có thể bị phát hiện hoặc chặnIP nguồn phân tán khắp thế giới, nhưng thực chất nhiễu quét đến từ chỉ một người
Cần thận trọng với chặn dựa trên IP. Dải IP thỉnh thoảng được phân bổ lại nên có thể chặn nhầm người vô tội, và tôi đã nhiều lần thấy các dải từng bị chặn vì là khu vực hoặc trung tâm dữ liệu được chuyển sang ISP dân dụng
Chặn HTTP/1.1 cũng có nguy cơ lớn là chặn người dùng thật đang dùng trình duyệt cũ. Ngoài ra, có những trình duyệt không gửi URL đầy đủ trong yêu cầu cross-origin, nên nếu truy cập đến từ Google Search thì referrer có thể chỉ trỏ về trang gốc của Google; nếu kết luận đó là bot nói dối rồi chặn, lưu lượng từ Google Search cũng có thể biến mất
Ngược lại, tôi thấy chặn HTTP/1.1 là hợp lý. Đã hơn 10 năm kể từ khi gần như mọi trình duyệt đều hỗ trợ các giao thức mới hơn, và nếu trình duyệt cũ đến mức đó thì phần lớn web phổ biến vốn đã hỏng, nên thêm một trang cá nhân không chạy nữa cũng là chuyện thường ngày chứ không phải ngoại lệ
Dù có bỏ lỡ các trình duyệt cũ và công cụ API, tôi vẫn sẽ duy trì chặn HTTP/1.1. Nếu là mã độc quyền của một hệ thống tài chính cũ kỹ thì còn hiểu được, nhưng Internet công khai cần tự cập nhật vì chính nó
Vì tôi đã chặn Google từ lâu, mọi yêu cầu tự nhận là đến từ Google đều là giả. Tôi xoay vòng blog qua nhiều tên miền ngẫu nhiên để cắt đứt liên hệ và snapshot, đồng thời cố kiểm soát con đường mọi người tìm thấy bài viết của tôi
Nhờ vậy tôi còn nhận được kiểm thử xâm nhập miễn phí, và kết luận rằng các biện pháp phòng thủ cùng pipeline xử lý khá vững chắc. Do áp lực sinh tồn, 90% lưu lượng bot đã chuyển sang VPN, khiến các endpoint VPN sáng lên như cây thông Noel
Tôi cũng có thể cung cấp feed IP truy cập dùng một lần, nhưng người dùng phải được xác minh đúng cách và mục đích sử dụng cũng phải được phê duyệt. Bản thân quá trình này khá thú vị
Nếu không đọc được, có thể xem bản lưu trữ tại https://archive.ph/d3236
Tôi không muốn tắt iCloud Private Relay chỉ để đọc thứ gì đó, dù tôi không phải bot. Theo các phản hồi khác của tác giả, đây là một triển khai tốt cho trang thử nghiệm, nhưng tôi mong các quản trị web khác đừng áp dụng y nguyên mọi cách nếu có thể
Nhìn vào việc nội dung phản hồi chỉ có chuỗi 410 và
Sec-Fetch-Mode:, có vẻ họ đã xem tôi là bot. Không có gì để đọc hay xem nên tôi chỉ rời đi; web hiện đại thật kinh khủngCó thể xem tình trạng hỗ trợ tại https://caniuse.com/?search=sec-fetch-mode, và một số header tại https://nochan.net/.env
PR_END_OF_FILE_ERROR, tức là không vượt qua nổi TLS handshakeTôi đoán hơn 99% lưu lượng web là bot hoặc agent, nên đang cân nhắc bỏ hiển thị số lượt truy cập. Con số này vô nghĩa và khiến trang trông đông hơn thực tế rất nhiều, nhưng tôi còn do dự vì sợ người thật sẽ không đọc được bài hoặc tải sách xuống
Nếu cần chặn, nhìn chung danh sách cho phép hiệu quả hơn danh sách chặn, và nếu không thể áp dụng danh sách cho phép thì cách này cũng có thể không phải giải pháp tốt
Các công cụ như Cloudflare và Anubis có thể gây vấn đề nghiêm trọng về khả năng tiếp cận, nên tôi thích giới hạn tần suất yêu cầu hơn. Cách đó gọn gàng mà không làm hại khả năng tiếp cận, và với vấn đề tạm thời thì chặn IP ngắn hạn cũng hoạt động tốt
Cá nhân tôi dùng fail2ban để phân tích log HTTP và chặn trong N giờ các IP yêu cầu URL bị cấm trong
robots.txt, các đường dẫn nhưwp-login.php, hoặc vượt quá giới hạn tần suất yêu cầu quá thường xuyên. Hiện tôi đang thử Anubis trên Git web UIChỉ với fail2ban cũng có thể chặn khá tốt, nhưng vài tháng đầu cần tinh chỉnh kỹ cho phù hợp với môi trường
Ban đầu tôi phát hiện bằng bộ lọc
failregex = ^ - \S+ \[\] ".*?" 40[034], rồi thêm vào các danh sách cụ thể hơn; hiện tôi chặn toàn bộ lưu lượng bằng khoảng 80 biểu thức chính quy, đến mức đã rất lâu không còn yêu cầu nào lọt tới bộ lọc40[034]chungTuy nhiên, nếu đứng sau load balancer hoặc proxy thì cần cách lấy IP thật, khiến cả fail2ban lẫn phương pháp trong bài gốc trở nên phiền phức
Nhìn các bình luận này và thử nghiệm truy cập của tôi, có vẻ họ không chỉ chặn bot mà còn chặn toàn bộ lưu lượng hợp lệ
Chủ nhật là lúc có thể thấy nhiều trình duyệt và ứng dụng lạ mà mọi người dùng để lướt web nhất; còn ngày thường thì nhiều trình duyệt phổ biến thông thường hơn, nên đây là một phép thử tốt