SearXNG 429 오류 해결 및 속도 제한 설정 방법
SearXNG에서 발생하는 429 오류의 두 가지 원인인 로컬 속도 제한과 업스트림 IP 차단을 구분하는 법을 설명합니다. 로그 분석을 통해 정확한 원인을 파악하고 설정을 수정하여 오류를 해결하는 구체적인 방법을 안내합니다.
SearXNG에서 429 오류가 발생하는 이유
직접 호스팅하는 SearXNG 인스턴스에서 429 오류가 발생하는 원인은 서로 관련 없는 두 가지가 있으며, 해결해야 할 속도 제한(rate limit)은 보통 예상하는 것과 다릅니다. 첫 번째 원인은 로컬 문제입니다. SearXNG 자체 제한 장치가 요청을 봇으로 판단하여 Too Many Requests 상태 코드 429로 응답한 경우입니다. 두 번째는 업스트림 문제입니다. 검색 엔진이 서버의 IP 주소를 거부한 경우이며, 이 경우 사용자에게는 429 오류가 아닌 일부 결과가 누락된 페이지로 나타납니다.
두 사례의 해결 방법은 서로 다릅니다. 제한 장치는 직접 관리하므로 설정을 변경할 수 있습니다. 반면 업스트림 차단은 Google 측에서 발생하므로 settings.yml의 어떤 설정을 변경해도 해결되지 않습니다. 로그를 확인하면 1분 안에 어떤 문제인지 파악할 수 있으므로 로그 확인부터 시작하십시오.
이 가이드는 직접 호스팅하는 SearXNG 인스턴스에서 설명한 컨테이너 설치 방식을 기준으로 합니다. 아래의 모든 설정 이름은 2026년 8월 기준으로 확인된 최신 업스트림 문서 및 소스 코드를 따릅니다.
설정을 변경하기 전에 로그를 확인하십시오
로그 창을 열어둔 상태에서 문제를 재현하십시오.
cd ./searxng/
docker compose logs -f searxng-core제한 장치(limiter) 메시지는 searx.limiter이라는 이름의 로거에서 발생하며 IP 주소를 명시합니다. 차단 목록(blocklist) 적중 시에는 BLOCK 203.0.113.10: matched BLOCKLIST, 허용 목록(allowlist) 적중 시에는 PASS 203.0.113.10: matched PASSLIST이라고 기록됩니다. 제한 장치가 카운터 저장소에 접근할 수 없는 경우 로그에는 The limiter requires Valkey, please consult the documentation가 표시되며, 이는 아무것도 집계되지 않고 있음을 의미합니다.
개별 봇 검사는 디버그 수준에서 기록되므로 기본적으로는 보이지 않습니다. 테스트를 위해 settings.yml에서 디버그 모드를 켜십시오:
general:
debug: true그러면 로그에 클라이언트 네트워크 옆으로 NOT OK (http_accept_language) 형태의 줄이 추가되며, 실패한 검사 항목이 명시됩니다. 테스트 후에는 반드시 다시 끄십시오. 배포된 인스턴스를 디버그 모드로 실행하지 말라는 것이 업스트림의 권장 사항입니다.
엔진 오류는 이와 전혀 다르게 나타납니다. IP 대신 엔진 이름이 표시되며, 가장 흔한 오류는 시간 초과(timeout)입니다:
HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)이와 관련된 페이지도 별도로 존재합니다. enable_metrics가 기본값인 true으로 설정된 경우, 인스턴스는 /stats/errors에 엔진 오류를 기록하며, /preferences에는 현재 응답 중인 엔진 목록이 나열됩니다. /stats/errors이 가득 찼는데 로그에 searx.limiter 줄이 없다면, 제한 장치 문제는 아닙니다.
디버깅 전 버전 고정하기
업스트림 컨테이너 설정은 두 개의 파일로 구성됩니다.
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 .envcompose 파일은 docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}을(를) 가져옵니다. 변수가 설정되지 않으면 latest이(가) 되며, latest은(는) 다음 docker compose pull 시점에 인스턴스가 변경됨을 의미합니다. 따라서 지난주에 작동하던 설정이 이를 읽는 코드와 맞지 않게 될 수 있습니다. SearXNG 태그에는 날짜와 커밋 정보가 포함됩니다. 2026년 8월 기준 업스트림 .env.example의 예시 태그는 2026.3.25-541c6c3cb이므로, .env에 실제 태그를 설정하십시오.
SEARXNG_VERSION=2026.3.25-541c6c3cb게시된 태그를 확인하고 실제로 테스트한 릴리스를 고정하십시오. 그런 다음 고정된 대상을 기준으로 디버깅하십시오. 동일한 .env 파일에 비밀 키가 포함되어 있으므로, 해당 디렉터리를 커밋하기 전에 Docker Compose에서 환경 파일과 비밀이 작동하는 방식을 읽어보십시오.
리미터는 Valkey가 필요하며, 그렇지 않으면 실행되지 않습니다
리미터는 클라이언트별 요청 수를 계산하며, 이 수치는 워커 프로세스 간에 공유되어야 합니다. 이 저장소가 바로 Redis의 유지 관리 포크인 Valkey입니다. 이전 SearXNG 가이드에서는 이 설정을 redis:이라고 부릅니다. 현재 릴리스에서는 valkey:을 사용하므로, 오래된 게시물이 아닌 현재 문서에서 키 이름을 복사하십시오.
use_default_settings: true
server:
secret_key: "change-this-value"
limiter: true
public_instance: false
valkey:
url: valkey://searxng-valkey:6379/0업스트림 compose 파일은 이미 docker.io/valkey/valkey:9-alpine 이미지를 사용하는 searxng-valkey 서비스를 실행하고 있으므로, 해당 호스트 이름은 compose 네트워크 내부에서 해석됩니다. 동일한 값을 SEARXNG_VALKEY_URL 환경 변수로 설정할 수 있으며, SearXNG와 Valkey가 호스트를 공유하는 경우 Unix 소켓 URL(unix:///path/to/socket.sock?db=0)도 작동합니다.
저장소가 없을 때 발생하는 현상은 다른 키 하나에 따라 달라집니다. public_instance: false 설정 시, 리미터는 Valkey 오류를 로그에 기록하고 포기하므로 인스턴스는 속도 제한 없이 계속 서비스를 제공합니다. public_instance: true 설정 시, 프로세스는 대신 sys.exit(1)를 호출합니다. 봇 보호 기능이 고장 난 채로 열려 있는 인스턴스는 하루 만에 모든 엔진으로부터 CAPTCHA(컴퓨터와 인간을 구별하기 위한 완전 자동화된 공개 튜링 테스트)를 수집하기 때문입니다. public_instance: true를 설정한 직후 컨테이너가 반복적으로 재시작된다면 이 경우에 해당하며, 종료 직전 마지막 줄에 Valkey가 언급됩니다.
리미터가 실제로 집계하는 항목
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"
}
]일반 클라이언트는 20초 버스트 윈도우 내에서 15 회, 10분 윈도우 내에서 150 회의 요청을 보낼 수 있습니다. 요청이 의심스러운 것으로 표시되면, 해당 클라이언트는 버스트 윈도우당 2 회로 제한이 강화됩니다. 마지막 항목은 가장 엄격합니다. 30일 윈도우 내에서 3 회 이상 의심스러운 요청으로 표시되면, 해당 주소는 검색 대신 시작 페이지로 리다이렉트되며 로그에는 BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /)이 기록됩니다.
이 수치들은 searx/botdetection/ip_limit.py 내의 상수입니다. 이는 설정값이 아니며 limiter.toml에서 노출하지 않으므로, 변경하려면 소스 코드를 직접 수정해야 합니다. /etc/searxng/limiter.toml가 제어하는 항목은 클라이언트를 그룹화하는 데 사용되는 주소 접두사, 신뢰할 수 있는 프록시 목록, 선택적 링크 토큰 확인, 그리고 허용 및 차단 목록입니다.
요청은 헤더 검사를 통해 의심스러운 것으로 표시되며, 각 검사에는 디버그 로그에서 확인할 수 있는 고유한 이름이 있습니다.
http_accept:Accept헤더에text/html가 포함되어 있지 않습니다.http_accept_encoding:Accept-Encoding헤더에gzip나deflate이 명시되어 있지 않습니다.http_accept_language:Accept-Language헤더가 존재하지 않습니다.http_connection:Connection헤더가close로 설정되어 있습니다.http_user_agent:User-Agent이 누락되었거나 알려진 봇 패턴과 일치합니다.http_sec_fetch:Sec-Fetch-Mode또는Sec-Fetch-Dest헤더가 브라우저가 전송하는 값과 다릅니다.
브라우저는 이 모든 헤더를 전송합니다. 일반적인 curl 호출은 이 중 대부분을 전송하지 않으므로, 수동으로 작성한 테스트 요청은 첫 시도부터 차단되는 반면 브라우저 탭에서는 검색이 정상적으로 작동합니다. 이것이 바로 "브라우저에서는 잘 되는데 스크립트에서는 429 오류가 발생한다"는 현상이 미스터리가 아닌 일반적인 결과인 이유입니다.
리버스 프록시 환경에서 제한기가 모든 사용자를 동시에 차단하는 경우
이는 정상적으로 작동하던 인스턴스를 망가뜨리는 가장 흔한 원인입니다. SearXNG는 X-Forwarded-For에 명시된 신뢰할 수 없는 첫 번째 IP 주소에서 클라이언트 주소를 가져오며, 실패 시 X-Real-IP를 참조하고, 마지막으로 연결을 맺은 주소를 사용합니다. 이러한 헤더를 신뢰할지 여부는 limiter.toml 내의 trusted_proxies 설정에 따라 결정됩니다.
프록시의 주소가 해당 목록에 없으면 헤더는 무시되며, 모든 방문자가 프록시의 주소를 가진 것으로 간주됩니다. 이 경우 모든 사용자가 하나의 카운터를 공유하게 되어, 전체 요청 수가 10분 동안 150를 초과하면 사이트 전체가 차단됩니다. 한 명의 사용자가 결과 페이지를 몇 번 새로고침하는 것만으로도 모든 사용자의 접속이 차단될 수 있습니다.
너무 많은 대상을 신뢰하는 것은 더 위험합니다. 공용 IP 대역이 목록에 포함되어 있으면, 방문자가 직접 X-Forwarded-For 헤더를 조작하여 요청마다 새로운 신원을 생성할 수 있으며, 이는 해당 방법을 아는 사용자에게 제한기를 무력화하는 결과를 초래합니다. 오직 프록시가 연결을 시도하는 주소만 목록에 추가하십시오. Docker 환경에서는 일반적으로 172.16.0.0/12 내부의 브리지 네트워크 주소이며, 해당 설정 줄은 기본적으로 주석 처리되어 있습니다.
[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48
trusted_proxies = [
'127.0.0.0/8',
'::1',
'172.16.0.0/12',
]프록시 또한 헤더를 전송해야 합니다. Nginx는 기본적으로 어떠한 헤더도 추가하지 않습니다:
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와 Traefik은 전달 헤더를 자동으로 설정하므로, 이들을 사용할 때는 trusted_proxies 설정만 완료하면 됩니다. 각 방식의 장단점은 셀프 호스팅 서비스를 위한 리버스 프록시 선택하기에서 다룹니다. 설정을 확인하려면 debug를 활성화한 뒤, 모바일 데이터를 사용하여 휴대폰으로 검색을 한 번 수행하십시오. 로그에 기록된 네트워크 주소가 프록시의 주소가 아닌 휴대폰의 주소인지 확인하면 됩니다.
에이전트가 시간당 4회의 API 요청을 수행하는 경우
JSON 출력은 기본적으로 비활성화되어 있으므로, 에이전트가 이를 사용하려면 추가 설정이 필요합니다:
search:
formats:
- html
- json이제 차트 행을 다시 확인하십시오. HTML 이외의 형식을 요청하는 모든 요청은 자체 윈도우에서 계산됩니다. 주소당 1 hour마다 4회의 요청이 허용됩니다. 리서치 에이전트는 단일 작업으로 이 할당량을 모두 소진하며, 이후의 모든 호출은 429 오류를 반환합니다. 이 제한 값은 소스 코드에 하드코딩되어 있으므로 제한을 높이는 것은 해결책이 아닙니다.
가장 깔끔한 해결책은 제한 장치(limiter)에 해당 클라이언트가 신뢰할 수 있는 대상임을 알리는 것입니다. limiter.toml의 허용 목록(pass list)에 해당 주소를 추가하십시오:
[botdetection.ip_lists]
block_ip = []
pass_ip = [
'10.8.0.0/24',
]
pass_searxng_org = truepass_ip은 다른 모든 방식보다 우선순위가 높으므로, 허용 목록에 포함된 클라이언트는 헤더 검사를 건너뛰며 일반적인 curl 호출도 정상적으로 작동합니다. IP 범위는 가능한 한 작게 유지하고, 라우팅 가능한 주소보다는 VPN 서브넷이나 컨테이너 네트워크를 사용하는 것을 권장합니다. 또 다른 깔끔한 해결책은 에이전트를 공용 경로에서 완전히 분리하는 것입니다. 프록시와 제한 장치가 트래픽을 감지할 수 없는 내부 네트워크의 컨테이너 주소로 에이전트를 연결하십시오. 이 구성 방법은 AI 에이전트에게 SearXNG 검색 기능 부여하기에서 다룹니다.
타인이 운영하는 공용 인스턴스를 에이전트의 대상으로 지정하는 것은 피해야 합니다. 이는 자원 봉사자의 IP 주소가 상위 엔진에 의해 차단되는 가장 빠른 길이며, JSON 형식이 기본적으로 비활성화되어 있는 이유이기도 합니다.
검색 엔진이 차단하는 경우
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"
}
]검색 엔진이 429 응답을 보내거나 CAPTCHA 페이지를 반환하면, SearXNG는 명명된 예외를 발생시키고 해당 엔진에 대한 요청을 일정 시간 중단합니다. 요청이 너무 많다는 응답을 받으면 3600 초 동안 엔진이 일시 중단됩니다. 일반적인 CAPTCHA나 접근 거부 응답은 1 day 동안 엔진을 중단시킵니다. Cloudflare를 통해 제공되는 CAPTCHA는 15 days 동안 중단되는데, 이는 목록에서 가장 긴 기본값입니다. 이러한 응답은 차단이 엣지 단계에서 발생했음을 의미하므로 재시도해도 소용이 없기 때문입니다.
일반적인 오류는 다른 설정을 따릅니다. 타임아웃이나 파싱 오류가 발생하면 search.ban_time_on_fail에서 파생된 짧은 시간 동안 엔진이 중단됩니다. 이 값의 기본값은 5초이며 search.max_ban_time_on_fail에 의해 최대 120초로 제한됩니다. 따라서 느린 엔진은 몇 분 안에 스스로 복구되지만, 차단된 엔진은 몇 시간 동안 사용할 수 없게 됩니다. 이러한 차이 때문에 사용자는 결과가 잘 나오다가 오후 내내 특정 엔진의 결과만 사라지는 현상을 무작위적인 문제로 보고하곤 합니다.
누군가를 탓하기 전에 타임아웃 문제를 먼저 해결해야 합니다. 기본값인 request_timeout은 2.0초인데, 이는 엔진의 가장 가까운 엣지 서버와 멀리 떨어진 소규모 VPS 환경에서는 다소 촉박한 시간입니다.
outgoing:
request_timeout: 3.0
max_request_timeout: 10.0
engines:
- name: bing
timeout: 5.0request_timeout는 모든 엔진의 기본값이며, max_request_timeout은 상한선입니다. 개별 엔진은 고유한 timeout를 가질 수 있습니다. 이 값을 높이면 실패는 줄어들지만 페이지 응답 속도가 느려집니다. 따라서 10초로 바로 올리기보다는 0.5초 단위로 조정하며 /stats/errors를 확인하는 것이 좋습니다.
귀하의 IP 주소를 확실하게 차단하는 엔진이 있다면 해당 엔진을 제거하십시오. 모든 검색은 가장 느린 엔진의 응답을 기다려야 하므로, 영구적으로 중단된 엔진을 유지하는 것은 응답 속도만 늦출 뿐 아무런 결과도 가져오지 못합니다.
use_default_settings:
engines:
remove:
- googledocker compose restart searxng-core으로 변경 사항을 적용한 뒤, 검색을 몇 번 수행하고 /stats/errors을 새로고침하십시오. 5분 정도 실제로 사용한 후에도 페이지가 비어 있다면 변경 사항이 정상적으로 적용된 것입니다.
데이터센터 IP는 봇으로 간주됩니다
귀하의 VPS 주소는 호스팅 대역에 속하며, 대형 검색 엔진은 이러한 대역을 자동화된 트래픽으로 평가합니다. 일부 엔진은 헤더가 아무리 정중하고 요청 속도가 느리더라도 해당 주소에서 오는 모든 요청에 CAPTCHA를 표시합니다. settings.yml의 어떤 설정으로도 이러한 판단을 바꿀 수는 없습니다.
변경할 수 있는 것은 어떤 엔진에 요청할지, 그리고 귀하의 인스턴스를 공개적으로 나열할지 여부입니다. 한 가구에서 사용하는 개인용 인스턴스는 거의 차단되지 않습니다. 호스팅 IP를 사용하는 공개 인스턴스는 가장 엄격한 엔진에서 정지 처분을 받게 되며, 이는 설정 오류가 아니라 소프트웨어의 정상적인 동작 상태입니다. SearXNG는 outgoing.proxies 또는 outgoing.using_tor_proxy을 사용하여 프록시를 통해 엔진 요청을 라우팅할 수 있으며, 이를 통해 트래픽을 다른 주소로 보낼 수 있습니다. 출구 노드(exit node)와 저렴한 프록시 풀은 호스팅 대역보다 더 낮은 점수를 받으므로, 이러한 조치로 인해 검색 결과의 품질이 저하될 수 있음을 유의하십시오.
인스턴스를 모니터링하여 문제 발생 시 즉시 파악하기
SearXNG는 모든 검색 엔진이 일시 중단된 상태에서도 해당 포트로 응답을 보냅니다. 따라서 상태 코드만 확인하는 가동 시간(uptime) 체크는 인스턴스가 실제 결과를 반환하지 않아도 정상으로 표시됩니다. 대신 응답 본문에 포함된 특정 단어를 확인하는 방식으로 콘텐츠를 검증해야 합니다. Uptime Kuma 키워드 모니터링을 사용하면 별도의 도구 없이도 이를 정확하게 수행할 수 있습니다. 또한 버전이 업데이트될 때마다 /stats/errors을 확인하십시오. 엔진의 HTML 구조가 변경되면 파서가 작동하지 않을 수 있으며, 이는 속도 제한(rate limit)과는 무관한 문제입니다.
FAQ
리버스 프록시 뒤에 배치한 SearXNG가 모든 방문자에게 429 오류를 반환하는 이유는 무엇입니까?
리미터(limiter)가 프록시를 클라이언트로 간주하여 집계하기 때문입니다. SearXNG는 연결된 주소가 /etc/searxng/limiter.toml의 trusted_proxies에 나열되어 있을 때만 X-Forwarded-For를 읽습니다. 주소가 나열되어 있지 않으면 모든 방문자가 하나의 카운터를 공유하게 되며, 이로 인해 10분당 150회 요청 제한을 모두 함께 초과하게 됩니다. 프록시가 연결을 시도하는 주소(Docker 환경에서는 일반적으로 브리지 대역인 172.16.0.0/12)를 추가하고, 프록시가 X-Real-IP 및 X-Forwarded-For 헤더를 전달하도록 설정하십시오. 신뢰할 수 없는 네트워크 대역을 나열해서는 안 됩니다. 신뢰된 네트워크로 설정하면 방문자가 헤더를 임의로 조작하여 요청마다 새로운 ID를 생성할 수 있기 때문입니다.
SearXNG 리미터는 시간당 몇 개의 API 요청을 허용합니까?
IP 주소당 시간당 4회입니다. HTML 이외의 형식을 요청하는 모든 요청은 별도의 1시간 단위로 집계되며, 이 제한은 limiter.toml가 아닌 searx/botdetection/ip_limit.py에 설정되어 있으므로 설정 파일에서 상향 조정할 수 없습니다. 에이전트나 스크립트가 한 번의 작업으로 요청을 수행하는 경우 이 제한에 걸릴 수 있습니다. 클라이언트 주소를 limiter.toml의 pass_ip에 추가하거나, 리미터가 요청을 감지하지 못하는 내부 네트워크를 통해 인스턴스에 접근하십시오.
429 오류 없이 검색 결과가 비어 있는 이유는 무엇입니까?
사용자가 아닌 검색 엔진 측에서 서버의 요청을 거부하고 있기 때문입니다. 자신의 인스턴스에서 /stats/errors를 확인하십시오. 실패한 각 엔진과 그 이유가 나열되어 있습니다. CAPTCHA나 접근 거부 메시지가 표시된다면 해당 엔진이 서버의 IP 주소를 차단한 것입니다. SearXNG는 요청 과다 응답 시 1시간, CAPTCHA 발생 시 하루 동안 해당 엔진을 일시 중단합니다. 로컬 설정으로는 업스트림 차단을 해제할 수 없으므로, 주소를 차단하는 엔진을 제거하고 정상적으로 응답하는 엔진만 유지하십시오.
개인용 인스턴스에서도 리미터를 활성화해야 합니까?
본인 외에 아무도 인스턴스에 접근하지 않는다면 limiter: false을 그대로 두십시오. 리미터를 활성화하면 Valkey 의존성이 추가되고 본인의 스크립트까지 차단될 수 있으며, 외부 트래픽이 없는 환경에서는 불필요한 기능입니다. 인스턴스가 공인 IP를 갖게 되는 즉시 public_instance: true와 함께 리미터를 활성화하십시오. 이 설정은 의도적인 것입니다. public_instance: true가 설정되어 있는데 Valkey가 정상 작동하지 않으면, 보호되지 않은 상태로 실행되는 대신 프로세스가 상태 코드 1로 종료됩니다.