Sửa lỗi 429 và rate limit trong SearXNG
SearXNG trả lỗi 429 do limiter local hoặc search engine block IP server. Đọc log để phân biệt hai trường hợp và chọn đúng cách xử lý, không đoán.
Vì sao SearXNG trả về lỗi 429
Một instance SearXNG tự host trả về lỗi 429 vì hai nguyên nhân không liên quan, và rate limit bạn cần xử lý thường không phải là nguyên nhân bạn nghĩ. Nguyên nhân thứ nhất là ở local: limiter của SearXNG xác định request đến từ bot và trả về Too Many Requests với status 429. Nguyên nhân thứ hai nằm ở upstream: một search engine từ chối địa chỉ IP của server bạn. Trường hợp này đến người dùng dưới dạng trang kết quả bị thiếu nội dung, không phải lỗi 429.
Hai trường hợp này không dùng chung cách xử lý. Limiter thuộc về bạn nên bạn có thể thay đổi nó. Việc upstream block xảy ra ở phía Google, nên không thiết lập nào trong settings.yml có thể gỡ block đó. Log sẽ cho biết bạn đang gặp trường hợp nào trong khoảng một phút, vì vậy hãy bắt đầu từ đó.
Hướng dẫn này giả định bạn đã cài đặt bằng container như mô tả trong instance SearXNG tự host trên VPS của bạn. Tên của mọi setting bên dưới lấy từ tài liệu upstream và source hiện tại, được kiểm tra vào tháng 8 năm 2026.
Đọc log trước khi thay đổi cài đặt
Tái hiện sự cố khi đang mở cửa sổ log.
cd ./searxng/
docker compose logs -f searxng-coreCác message của limiter đến từ logger có tên searx.limiter và chứa địa chỉ IP. Khi khớp blocklist, log có dạng BLOCK 203.0.113.10: matched BLOCKLIST; khi khớp allowlist, log có dạng PASS 203.0.113.10: matched PASSLIST. Nếu limiter không thể truy cập counter store, log sẽ ghi The limiter requires Valkey, please consult the documentation. Điều này có nghĩa là không có gì được đếm.
Mỗi lần kiểm tra bot đều được ghi ở mức debug, nên mặc định bạn sẽ không thấy các dòng này. Bật debug cho một lần kiểm tra trong settings.yml:
general:
debug: trueSau đó log sẽ thêm các dòng có dạng NOT OK (http_accept_language) bên cạnh network của client và nêu tên lần kiểm tra đã thất bại. Tắt debug lại ngay sau đó, vì upstream khuyến cáo không chạy instance đã triển khai với debug được bật.
Lỗi của engine hoàn toàn khác. Log nêu tên engine thay vì địa chỉ IP. Trường hợp phổ biến nhất là timeout:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)Trang này cũng hiển thị thông tin tương tự. Khi enable_metrics giữ giá trị mặc định là true, instance của bạn ghi lỗi engine ở /stats/errors, còn /preferences liệt kê những engine hiện đang phản hồi. Nếu /stats/errors đã đầy nhưng log không có dòng searx.limiter nào, limiter không phải nguyên nhân của sự cố.
Ghim phiên bản trước khi debug bất cứ thứ gì
Thiết lập container upstream gồm 2 file.
mkdir -p ./searxng/core-config/
cd ./searxng/
curl -fsSL \
-O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
-O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example
cp -i .env.example .envFile compose lấy docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. Biến chưa được đặt có nghĩa là latest, còn latest nghĩa là instance sẽ thay đổi ở lần docker compose pull tiếp theo. Vì vậy, một thiết lập hoạt động tuần trước có thể không còn khớp với code đang đọc nó. Tag của SearXNG chứa ngày và commit. Tag ví dụ trong upstream .env.example tính đến tháng 8 năm 2026 là 2026.3.25-541c6c3cb, vì vậy hãy đặt một tag cụ thể trong .env:
SEARXNG_VERSION=2026.3.25-541c6c3cbKiểm tra các tag đã publish và ghim release mà bạn đã thực sự kiểm thử, sau đó debug trên một mục tiêu cố định. File .env cũng chứa secret key của bạn, vì vậy hãy đọc cách file env và secret hoạt động trong Docker Compose trước khi commit thư mục đó vào bất kỳ nơi nào.
Bộ giới hạn cần Valkey, nếu không thì sẽ không chạy
Bộ giới hạn đếm số request theo từng client, và các bộ đếm này phải được chia sẻ giữa các worker process. Store dùng cho việc đó là Valkey, fork được duy trì của Redis. Các hướng dẫn SearXNG cũ gọi thiết lập này là redis:. Bản phát hành hiện tại đọc valkey:, vì vậy hãy lấy tên key từ tài liệu hiện tại thay vì từ một bài viết cũ.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0File compose upstream đã chạy một service searxng-valkey trên image docker.io/valkey/valkey:9-alpine, nên hostname đó phân giải được bên trong compose network. Có thể đặt cùng giá trị bằng biến môi trường SEARXNG_VALKEY_URL, và URL Unix socket (unix:///path/to/socket.sock?db=0) cũng hoạt động khi SearXNG và Valkey dùng chung một host.
Điều xảy ra khi thiếu store phụ thuộc vào một key khác. Với public_instance: false, bộ giới hạn ghi lỗi Valkey vào log rồi dừng, nên instance vẫn tiếp tục phục vụ nhưng hoàn toàn không rate limiting. Với public_instance: true, process gọi sys.exit(1) thay vào đó, vì một instance đang mở nhưng bot protection bị hỏng sẽ thu thập CAPTCHA (bài kiểm tra Turing công khai tự động hoàn toàn để phân biệt máy tính và con người) từ mọi engine chỉ trong một ngày. Nếu một container restart liên tục ngay sau khi bạn đặt public_instance: true thì đây chính là nguyên nhân; dòng cuối cùng trước mỗi lần thoát sẽ nêu Valkey.
Bộ giới hạn thực sự đếm gì
The data behind this chart
[
{
"label": "Burst, normal client",
"max_requests": 15,
"window": "20 seconds"
},
{
"label": "Burst, flagged client",
"max_requests": 2,
"window": "20 seconds"
},
{
"label": "Sustained, normal client",
"max_requests": 150,
"window": "10 minutes"
},
{
"label": "Sustained, flagged client",
"max_requests": 10,
"window": "10 minutes"
},
{
"label": "Any non-HTML format",
"max_requests": 4,
"window": "1 hour"
},
{
"label": "Flagged requests before block",
"max_requests": 3,
"window": "30 days"
}
]Một client bình thường được phép thực hiện 15 request trong cửa sổ burst 20 giây và 150 request trong cửa sổ 10 phút. Khi một request bị đánh dấu là đáng ngờ, cùng client đó chỉ còn được thực hiện 2 request trong mỗi cửa sổ burst. Hàng cuối cùng là mức nghiêm ngặt nhất: sau 3 request bị đánh dấu trong cửa sổ 30 ngày, địa chỉ đó sẽ được redirect về trang bắt đầu thay vì thực hiện tìm kiếm, và log ghi BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).
Các con số này là hằng số trong searx/botdetection/ip_limit.py. Đây không phải setting và limiter.toml không cung cấp cách thay đổi chúng, vì vậy muốn đổi phải sửa source. /etc/searxng/limiter.toml kiểm soát các tiền tố địa chỉ dùng để nhóm client, danh sách trusted proxy, kiểm tra link token tùy chọn, cùng các pass list và block list.
Một request bị đánh dấu là đáng ngờ dựa trên các kiểm tra header. Mỗi kiểm tra có một tên mà bạn sẽ thấy trong debug log:
http_accept: headerAcceptkhông chứatext/html.http_accept_encoding: headerAccept-Encodingkhông chỉ địnhgziphoặcdeflate.http_accept_language: không có headerAccept-Language.http_connection: headerConnectionđược đặt thànhclose.http_user_agent:User-Agentbị thiếu hoặc khớp với một mẫu bot đã biết.http_sec_fetch: headerSec-Fetch-ModehoặcSec-Fetch-Destkhông giống nội dung mà trình duyệt gửi.
Trình duyệt gửi tất cả các header này. Một lệnh gọi curl thông thường hầu như không gửi header nào trong số đó, vì vậy request kiểm thử viết thủ công sẽ bị đánh dấu ngay lần đầu, trong khi cùng một tìm kiếm vẫn hoạt động trong tab trình duyệt. Vì thế, trường hợp “hoạt động trong trình duyệt nhưng script nhận 429” là kết quả bình thường, không phải điều gì khó giải thích.
Reverse proxy khiến limiter chặn tất cả người dùng cùng lúc
Đây là cách phổ biến nhất khiến một instance đang hoạt động bị lỗi. SearXNG lấy địa chỉ client từ IP không tin cậy đầu tiên trong X-Forwarded-For, nếu không có thì dùng X-Real-IP, rồi tiếp tục dùng địa chỉ đã mở connection. Việc có tin cậy các header đó hay không do trusted_proxies trong limiter.toml quyết định.
Nếu địa chỉ của proxy không có trong danh sách đó, các header sẽ bị bỏ qua và mọi visitor đều được xem là đến từ địa chỉ của proxy. Khi đó họ dùng chung một counter. Toàn bộ site sẽ bị chặn ngay khi tổng số request vượt 150 request trong 10 phút. Chỉ một người reload trang kết quả vài lần cũng có thể khiến tất cả người khác bị chặn theo.
Tin cậy quá mức còn nguy hiểm hơn. Nếu đưa một public range vào danh sách, bất kỳ visitor nào cũng có thể gửi header X-Forwarded-For của riêng mình và chọn một identity mới cho mỗi request. Điều này tắt limiter đối với bất kỳ ai biết cách khai thác. Chỉ liệt kê địa chỉ mà proxy của bạn trực tiếp kết nối từ đó. Trong Docker, đó thường là một bridge network bên trong 172.16.0.0/12, và dòng cấu hình này mặc định bị comment out.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]Proxy cũng phải gửi các header này. Nginx không tự thêm chúng:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header Connection $http_connection;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}Caddy và Traefik tự đặt các forwarded header, nên với hai proxy này bạn chỉ cần xử lý phần trusted_proxies. Các điểm đánh đổi được trình bày trong chọn reverse proxy cho service tự host. Để kiểm tra một trong hai cấu hình, bật debug, dùng điện thoại truy vấn một lần qua mobile data, rồi xác nhận network trong dòng log là địa chỉ của điện thoại chứ không phải địa chỉ của proxy.
Agent của bạn được 4 request API mỗi giờ
Mặc định, JSON output bị tắt, nên cần thêm nó vào agent:
search:
formats:
- html
- jsonBây giờ đọc lại dòng trong bảng. Mọi request yêu cầu format khác HTML sẽ được tính trong cửa sổ riêng: 4 request mỗi 1 hour, trên mỗi địa chỉ. Một research agent có thể dùng hết hạn mức đó trong một task, và mọi call sau đó đều trả về 429. Không thể tăng hạn mức vì con số này nằm trong source.
Cách sửa phù hợp là cho limiter biết client này không phải client lạ. Thêm địa chỉ của nó vào pass list trong limiter.toml:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip được ưu tiên hơn mọi phương thức khác, nên client nằm trong allowlist cũng bỏ qua các bước kiểm tra header và một call curl không có tham số vẫn hoạt động. Giữ range nhỏ nhất có thể, và ưu tiên subnet VPN hoặc network của container thay vì bất kỳ dải nào có thể định tuyến. Cách sửa phù hợp khác là hoàn toàn không cho agent đi qua public path: trỏ nó đến địa chỉ container trên internal network, nơi proxy và limiter của proxy không nhìn thấy traffic. Phần cấu hình này được trình bày trong cấp cho AI agent một kỹ năng tìm kiếm SearXNG.
Không nên trỏ agent đến một instance public do người khác vận hành. Đây là cách nhanh nhất khiến IP của tình nguyện viên bị các upstream engine chặn, và cũng là lý do JSON format bị tắt mặc định ngay từ đầu.
Khi các engine chặn bạn
The data behind this chart
[
{
"label": "SearxEngineTooManyRequests",
"suspended_seconds": 3600,
"roughly": "1 hour"
},
{
"label": "SearxEngineAccessDenied",
"suspended_seconds": 86400,
"roughly": "1 day"
},
{
"label": "SearxEngineCaptcha",
"suspended_seconds": 86400,
"roughly": "1 day"
},
{
"label": "recaptcha_SearxEngineCaptcha",
"suspended_seconds": 604800,
"roughly": "7 days"
},
{
"label": "cf_SearxEngineCaptcha",
"suspended_seconds": 1296000,
"roughly": "15 days"
}
]Khi một engine trả về mã 429 hoặc trang CAPTCHA riêng, SearXNG tạo một exception có tên và tạm thời ngừng gửi request đến engine đó. Phản hồi quá nhiều request sẽ tạm ngưng engine trong 3600 giây. Phản hồi CAPTCHA thông thường hoặc bị từ chối truy cập sẽ tạm ngưng engine trong 1 day. CAPTCHA do Cloudflare cung cấp sẽ tạm ngưng engine trong 15 days, lâu nhất trong danh sách mặc định, vì phản hồi đó cho thấy việc chặn xảy ra ở edge và retry sẽ không có tác dụng.
Các lỗi thông thường dùng thiết lập khác. Timeout hoặc lỗi parse sẽ tạm ngưng engine trong thời gian ngắn, được tính từ search.ban_time_on_fail. Giá trị này mặc định là 5 giây và bị giới hạn ở 120 giây bởi search.max_ban_time_on_fail. Vì vậy, engine chậm sẽ tự hoạt động lại trong vòng vài phút, còn engine bị chặn sẽ không hoạt động trong nhiều giờ. Sự khác biệt này giải thích một triệu chứng thường bị cho là ngẫu nhiên: kết quả vẫn bình thường, rồi kết quả từ một engine biến mất trong phần thời gian còn lại của buổi chiều.
Bạn nên xử lý timeout trước khi quy trách nhiệm cho bất kỳ bên nào. request_timeout mặc định là 2.0 giây. Giá trị này khá thấp đối với một VPS nhỏ nằm xa edge server gần nhất của engine.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout là giá trị mặc định cho mọi engine, max_request_timeout là giới hạn trên, và từng engine có thể dùng timeout riêng. Tăng các giá trị này sẽ đổi độ trễ tải trang lấy ít lỗi hơn. Vì vậy, hãy tăng từng 0.5 giây và theo dõi /stats/errors thay vì tăng thẳng lên 10.
Nếu một engine thực sự đang chặn địa chỉ của bạn, hãy xóa engine đó. Mỗi lượt tìm kiếm phải chờ engine chậm nhất, nên giữ một engine bị tạm ngưng vĩnh viễn chỉ làm tăng độ trễ mà không trả về kết quả.
use_default_settings:
engines:
remove:
- googleÁp dụng thay đổi bằng docker compose restart searxng-core, sau đó chạy một vài lượt tìm kiếm và tải lại /stats/errors. Nếu sau 5 phút sử dụng thực tế trang vẫn trống, thay đổi đã có hiệu lực.
Địa chỉ IP của datacentre sẽ bị xem là bot
Địa chỉ VPS của bạn thuộc một dải IP hosting, và các công cụ tìm kiếm lớn chấm điểm những dải này là lưu lượng tự động. Một số công cụ sẽ hiển thị CAPTCHA cho mọi request từ địa chỉ như vậy, bất kể header có hợp lệ hay tốc độ request có chậm đến đâu. Không có thiết lập nào trong settings.yml thay đổi được đánh giá đó.
Bạn có thể thay đổi những công cụ tìm kiếm được sử dụng và việc instance có được công khai hay không. Một instance riêng tư chỉ được một hộ gia đình sử dụng hiếm khi kích hoạt các cơ chế chặn. Một instance công khai trên IP hosting sẽ bị các công cụ nghiêm ngặt nhất đình chỉ nhiều lần. Đây là trạng thái bình thường của phần mềm, không phải lỗi trong config của bạn. SearXNG có thể định tuyến request đến công cụ tìm kiếm qua proxy bằng outgoing.proxies hoặc outgoing.using_tor_proxy, nhờ đó chuyển traffic sang một địa chỉ khác. Exit node và các pool proxy giá rẻ thường bị chấm điểm kém hơn dải IP hosting, vì vậy kết quả có thể tệ hơn sau khi thay đổi này.
Theo dõi instance để phát hiện sự cố sớm
SearXNG vẫn phản hồi trên cổng của nó ngay cả khi tất cả engine bị tạm dừng. Vì vậy, kiểm tra uptime chỉ theo dõi status code vẫn hiển thị trạng thái bình thường trong khi instance không trả về nội dung. Hãy kiểm tra nội dung thay thế: gửi một truy vấn tìm kiếm thực tế và đối chiếu một từ mà bạn dự kiến sẽ xuất hiện trong response body. Theo dõi từ khóa bằng Uptime Kuma thực hiện đúng việc đó mà không cần thêm công cụ. Sau mỗi lần nâng phiên bản, hãy kiểm tra cả /stats/errors, vì HTML của các engine có thể thay đổi và parser có thể hỏng mà không liên quan đến rate limit.
FAQ
Vì sao SearXNG trả về 429 cho mọi người dùng sau khi tôi đặt nó sau reverse proxy?
Vì limiter đang tính reverse proxy là client. SearXNG chỉ đọc X-Forwarded-For khi địa chỉ kết nối được liệt kê trong trusted_proxies ở /etc/searxng/limiter.toml. Nếu địa chỉ này không được liệt kê, mọi người dùng sẽ dùng chung một bộ đếm và cùng vượt ngưỡng 150 request trong 10 phút. Thêm địa chỉ mà proxy dùng để kết nối. Với Docker, địa chỉ này thường nằm trong dải bridge 172.16.0.0/12. Đồng thời bảo đảm proxy gửi X-Real-IP và X-Forwarded-For. Không bao giờ liệt kê một dải mạng mà bạn không kiểm soát, vì một mạng được tin cậy cho phép bất kỳ người dùng nào đặt header đó và chọn một danh tính mới cho mỗi request.
Limiter của SearXNG cho phép bao nhiêu request API mỗi giờ?
Bốn request cho mỗi địa chỉ IP mỗi giờ. Mọi request yêu cầu format khác HTML sẽ được tính trong một cửa sổ một giờ riêng. Giới hạn này được đặt trong searx/botdetection/ip_limit.py thay vì limiter.toml, nên không thể tăng từ config. Một agent hoặc script sẽ vượt giới hạn này trong một tác vụ. Thêm địa chỉ của client vào pass_ip trong limiter.toml, hoặc truy cập instance qua một mạng nội bộ nơi limiter không nhìn thấy request.
Vì sao kết quả tìm kiếm của tôi trả về rỗng mà không có lỗi 429?
Các engine đang từ chối server của bạn, không phải người dùng của bạn. Mở /stats/errors trên instance của bạn. File này ghi rõ từng engine bị lỗi và nguyên nhân. Mục CAPTCHA hoặc access-denied có nghĩa là engine đó đã chặn địa chỉ IP của server bạn. Sau đó SearXNG sẽ tạm ngưng engine: trong một giờ sau khi nhận câu trả lời too-many-requests và trong một ngày sau CAPTCHA. Không có setting cục bộ nào gỡ được block từ upstream. Vì vậy, hãy xóa các engine đang chặn địa chỉ của bạn và giữ lại những engine vẫn trả lời.
Tôi có nên bật limiter trên instance riêng tư không?
Nếu không có ai ngoài bạn truy cập instance, hãy để limiter: false. Limiter tạo dependency vào Valkey và chặn các script của chính bạn, trong khi nó bảo vệ khỏi loại traffic không tồn tại. Hãy bật limiter ngay khi instance có địa chỉ public, đồng thời bật public_instance: true. Hai tùy chọn này được thiết kế để dùng cùng nhau: khi có public_instance: true nhưng Valkey không hoạt động, process sẽ thoát với status 1 thay vì chạy mà không được bảo vệ.