검증된 크롤러 IP 목록: 공식 피드 19개로 정상 봇 허용 목록 만들기
봇 운영자 10곳의 공식 파일 19개로 만든 검증된 크롤러 IP 목록입니다. 파일 날짜, 프리픽스 수, 허용 목록 코드, 그리고 검증되었는데도 review를 받은 봇을 다룹니다.

호스팅 네트워크 트래픽을 챌린지하는 방화벽 규칙은 원하는 크롤러까지 걸러 냅니다. 2026년 10월 29일에 조회한 검증된 크롤러 주소는 모두 호스팅 네트워크에 있었습니다. ClaudeBot은 Amazon, DuckDuckGo는 Microsoft, AhrefsBot은 OVH, Googlebot은 Google 자체 네트워크였습니다. 대신 User-Agent로 규칙을 느슨하게 하면 헤더에 "Googlebot"이라고 적는 스크레이퍼가 모두 통과합니다.
운영자들은 증거를 무료로 공개하지만, 서로 다른 웹사이트 10곳에 파일 19개로 흩어져 있고, 날짜도 제각각이며 갱신 일정도 밝히지 않습니다. 그중 두 파일은 같은 목록입니다. 트래픽은 실재합니다. Imperva의 2026 Bad Bot Report를 소개하며 Thales는 이렇게 썼습니다. "Bots now dominate the internet, accounting for over 53% of all web traffic, with 40% classified as malicious."(봇이 이제 인터넷을 지배하며 전체 웹 트래픽의 53% 이상을 차지하고, 그중 40%는 악성으로 분류됩니다.)
아래에서는 2026년 10월 29일 기준 각 파일과 파일에 적힌 날짜, 그 파일의 실제 주소를 GeoIPHub IP 조회 API에 넣었을 때의 결과, 허용 목록을 만드는 스크립트, 공유 파일에서 오작동하지 않는 정책 검사, 그리고 어떤 파일로도 다룰 수 없는 봇을 다룹니다.

정상 봇 vs 악성 봇: 차이는 이름이 아니라 증거입니다
정상 봇이란 들여보내기로 결정했고 그 IP가 운영자의 것임을 증명할 수 있는 봇입니다. 악성 봇은 둘 중 하나라도 통과하지 못하며, 가장 흔한 유형은 정상 봇의 User-Agent를 쓰는 스크레이퍼입니다.
정상 봇에는 검색 크롤러, AI 검색 및 사용자 요청 기반 페처, SEO 도구, 웹 아카이버가 있습니다. 악성 봇에는 스크레이퍼, 크리덴셜 스터퍼, 클릭 봇, 스팸 봇이 있습니다. 많은 용어집이 정상 봇은 "스스로를 밝힌다"고 말합니다. 악성 봇도 그렇게 합니다. 신원을 그대로 베끼는 것이며, 애초에 스크레이퍼가 크롤러 이름을 빌리는 이유가 이것입니다.
그러니 유용한 질문은 "이 봇이 정상 봇인가?"가 아니라 "이 요청이 자기가 말한 운영자에게서 온 것인가?"입니다. 두 번째 질문에는 기계적인 답이 있고, 이것이 IP 기반 봇 탐지의 핵심입니다. 운영자는 자기 IP 대역을 통제하고 다른 누구도 그럴 수 없습니다. 증명된 봇을 들여보낼지는 정책의 문제이며 사이트마다 다를 수 있습니다.
검증된 크롤러 IP 목록: 운영자 10곳의 공식 파일 19개
이 목록은 검색 엔진 봇 IP 목록으로 쓸 수 있고 그 이상으로도 쓸 수 있습니다. GeoIPHub가 불러오는 대역 파일 19개는 검색 엔진과 AI 기업부터 아카이브와 SEO 도구까지 운영자 10곳에서 옵니다. 아래는 각 파일의 프리픽스 수와 파일 안에 적힌 날짜이며, 2026년 10월 29일 UTC 04:41~04:42에 가져온 것입니다. 19개 모두 HTTP 200으로 응답했고 형태도 같습니다. ipv4Prefix 또는 ipv6Prefix 항목으로 이루어진 prefixes 배열입니다.
| 운영자 | 봇 (파일) | 파일 URL | 프리픽스 (IPv4 / IPv6) | 파일 내 날짜 | API crawler.name |
|---|---|---|---|---|---|
| 일반 크롤러, Googlebot 포함 | common-crawlers.json | 170 / 147 | 2026년 10월 28일 | Googlebot | |
| 특수 목적 크롤러, AdsBot 포함 | special-crawlers.json | 136 / 136 | 2026년 10월 28일 | GoogleSpecialCrawler | |
| 사용자 요청 기반 페처 | user-triggered-fetchers.json | 534 / 534 | 2026년 10월 28일 | GoogleUserTriggeredFetcher | |
| 사용자 요청 기반 페처 (google.com) | user-triggered-fetchers-google.json | 252 / 252 | 2026년 10월 28일 | GoogleUserTriggeredFetcherGoogle | |
| 사용자 요청 기반 에이전트 (Google-Agent) | user-triggered-agents.json | 12 / 8 | 2026년 10월 28일 | GoogleUserTriggeredAgent | |
| Microsoft | Bingbot | bingbot.json | 28 / 0 | 2024년 1월 3일 | Bingbot |
| Apple | Applebot | applebot.json | 24 / 0 | 2026년 9월 15일 | Applebot |
| DuckDuckGo | DuckDuckBot | duckduckbot.json | 486 / 0 | 2026년 9월 1일 | DuckAssistBot (아래 참조) |
| DuckDuckGo | DuckAssistBot | duckassistbot.json | 486 / 0 | 2026년 9월 1일 | DuckAssistBot |
| OpenAI | GPTBot | gptbot.json | 18 / 0 | 2026년 9월 22일 | GPTBot |
| OpenAI | ChatGPT-User | chatgpt-user.json | 235 / 0 | 2026년 10월 27일 | ChatGPTUser |
| OpenAI | OAI-SearchBot | searchbot.json | 39 / 0 | 2026년 1월 2일 | OAISearchBot |
| Anthropic | ClaudeBot, Claude-User, Claude-SearchBot | bots.json | 14 / 0 | 2026년 10월 20일 | ClaudeBot |
| Perplexity | Perplexity-User | perplexity-user.json | 10 / 0 | 2026년 10월 28일 | PerplexityUser |
| Perplexity | PerplexityBot | perplexitybot.json | 13 / 0 | 2026년 10월 28일 | PerplexityBot |
| Mistral | MistralAI-User | mistralai-user-ips.json | 4 / 0 | 2025년 2월 19일 | MistralAIUser |
| Mistral | MistralAI-Index | mistralai-index-ips.json | 2 / 0 | 2026년 4월 19일 | MistralAIIndex |
| Common Crawl | CCBot | ccbot.json | 4 / 1 | 2026년 8월 11일 | CCBot |
| Ahrefs | AhrefsBot | crawler-ip-ranges | 81 / 0 | 파일에 날짜 없음 | AhrefsBot |
GeoIPHub 스캐너는 모든 파일을 매일 다시 가져옵니다. 2026년 10월 27일에 각 파일에서 주소 하나씩을 조회했고 19개 모두 검증되어 돌아왔습니다. Perplexity의 두 주소는 10월 28일에 교체된 이전 파일에서 가져온 것입니다.
프리픽스는 모두 3,626개이고 그중 2,181개가 Google의 파일 다섯 개에 들어 있습니다. 또한 고유한 목록은 18개뿐입니다. DuckDuckGo의 두 파일은 가져왔을 때 바이트 단위로 동일했습니다(MD5 해시가 같음). Perplexity의 두 파일은 10월 27일과 28일 사이에 프리픽스가 4개와 8개에서 10개와 13개로 바뀌었습니다. 누구의 표든, 이 글의 표도 포함해서, 설정 파일에 손으로 베껴 넣지 말라는 경고로 충분합니다.
네 가지 참고 사항이 있습니다. Google의 예전 경로인 developers.google.com/search/apis/ipranges/googlebot.json은 이제 common-crawlers.json으로 301 리디렉션으로 응답하므로, 코드가 리디렉션을 따라가면 이 경로를 링크한 오래된 가이드도 여전히 동작합니다. Microsoft의 파일 날짜는 2024년 1월입니다. Perplexity 문서는 아직 www.perplexity.com URL을 링크하지만 위의 www.perplexity.ai 파일로 리디렉션됩니다. 그리고 www.gstatic.com/ipranges/goog.json은 Google의 모든 주소를 다루므로 크롤러 목록이 아닙니다. Google의 파일 다섯 개가 크롤러 그룹별로 어떻게 나뉘는지는 Googlebot 가이드를 참고하세요.
일치하면 무엇이 반환되는가: 실제 조회 네 건
GeoIPHub API에서 일치하면 threat.crawler.verified: true, 운영자, 파일 레이블, verification_method: "ip_range_list"가 반환됩니다. 어느 파일에도 없는 주소는 verified: false를 반환하며, 이는 "검증되지 않음"이라는 뜻일 뿐 그 이상은 아닙니다. 다음은 실시간 응답에서 줄여 온 ClaudeBot의 주소입니다(2026년 10월 29일 UTC 04:42:21).
{
"asn": {
"asn": 16509,
"org": "AMAZON-02 - Amazon.com, Inc."
},
"threat": {
"is_crawler": true,
"crawler": {
"verified": true,
"name": "ClaudeBot",
"operator": "Anthropic",
"category": "ai",
"verification_method": "ip_range_list"
}
},
"scoring": {
"fraud_score": 0,
"recommended_action": "allow",
"detection_methods": ["datacenter_ip", "no_ptr_datacenter", "verified_crawler"]
}
}
2026년 10월 29일에 dig -x 216.73.216.10은 PTR 레코드를 반환하지 않았으므로 역방향 DNS로는 이 주소를 보증할 수 없습니다. Anthropic의 파일만이 보증할 수 있습니다. UTC 04:42:28에서 04:42:44 사이에 조회한 나머지 세 주소는 다음과 같습니다.
| IP | 출처 | verified | operator | name | category | 점수, 조치 |
|---|---|---|---|---|---|---|
| 216.73.216.10 | Anthropic의 파일 | true | Anthropic | ClaudeBot | ai | 0, allow |
| 107.178.192.1 | Google의 사용자 요청 기반 페처 파일 | true | GoogleUserTriggeredFetcher | fetcher | 0, allow | |
| 104.43.54.127 | DuckDuckGo 파일 둘 다 | true | DuckDuckGo | DuckAssistBot | ai | 0, allow |
| 31.13.64.1 | Meta의 네트워크, AS32934 | false | Meta | Facebook-Bot | social_preview | 0, allow |
이제 요청 하나를 추적해 보겠습니다. 104.43.54.127에서 온 요청이 DuckDuckBot이라고 주장한다고 합시다. 이 주소는 DuckDuckGo의 두 파일 모두에 /32로 올라 있습니다. 조회 결과는 verified: true, operator: "DuckDuckGo"이지만 name: "DuckAssistBot", category: "ai"입니다. "검색 봇은 허용하고 AI 봇은 요청 빈도를 제한한다"는 정책을 이름이나 카테고리 기준으로 쓰면 DuckDuckGo의 진짜 검색 크롤러가 제한을 받습니다. 주장된 봇과 그 운영자를 기준으로 쓰면 통과합니다. DuckDuckBot이라는 이름을 예상했는데 DuckAssistBot이 나왔습니다. 이름 필드는 어떤 봇이 문을 두드렸는지가 아니라 어떤 파일이 마지막에 불려 왔는지를 알려 주었습니다.
Meta가 또 다른 함정입니다. Meta의 네트워크(AS32934)는 ASN으로 매칭하므로 is_crawler는 true이지만 verified는 false이고 verification_method는 "none"입니다. 미검증으로 취급하세요. 우리 문서에 크롤러 필드와 각 필드의 의미가 나와 있습니다.
파일 하나가 여러 봇을 포괄할 수 있으므로 정책은 운영자를 기준으로 하세요
주장된 User-Agent를 운영자에 대응시킨 다음, verified: true이며 같은 운영자일 것을 요구하세요. 파일 하나가 여러 봇을 대표할 수 있으므로 crawler.name이나 category를 스위치로 쓰지 마세요.
- DuckDuckGo: 파일 두 개, 목록 하나. 우리 스캐너는
duckassistbot.json을 두 번째로 불러오므로 마지막 쓰기가 이기고, 모든 DuckDuckGo 주소에 DuckAssistBot 레이블이 붙습니다. - Anthropic: ClaudeBot, Claude-User, Claude-SearchBot을 위한 파일 하나. Anthropic의 크롤러 도움말 페이지는 이렇게 말합니다. "If a crawler has a source IP address on this list, it indicates that the crawler is coming from Anthropic."(크롤러의 출발지 IP 주소가 이 목록에 있다면, 그 크롤러는 Anthropic에서 오는 것입니다.) 따라서 일치는 Anthropic으로 검증되었다는 뜻이며, 어떤 봇인지는 User-Agent가 알려 줍니다.
- Google: common-crawlers 파일은 Googlebot이라는 하나의 레이블 아래 Google의 여러 크롤러를 포괄합니다.
is_crawler만으로도 허용 목록에는 충분히 강하지 않습니다. Meta의 미검증 매칭에서도 true이기 때문입니다. 또한 API는 요청 헤더를 보지 못합니다. IP를 받을 뿐이므로, 검사에서 주장하는 쪽은 항상 자체 코드에서 실행됩니다.
크롤러 허용 목록을 네 단계로 만드는 방법
네 단계로 만듭니다. 허용할 User-Agent마다 운영자를 대응시키고, 그 운영자의 파일을 불러오고, IP를 파일과 대조하고, 실패한 주장은 모두 요청 빈도 제한기로 보냅니다.
- 주장 맵. 허용할 User-Agent 토큰(오래된 WAF는 이를 화이트리스트라고 부릅니다)과, 그 IP가 들어 있어야 하는 파일의 운영자를 나열합니다.
- 파일. 아래 스크립트로 직접 병합 목록을 만들거나, 새 IP마다 조회를 한 번 호출하고 판정을 몇 주가 아니라 몇 시간만 캐시하세요. 대역은 바뀌기 때문입니다.
- 대조. IP를 주장된 운영자의 프리픽스하고만 대조합니다.
- 미검증 경로. 403으로 응답하지 마세요. 검증할 수 없는 것은 차단하는 대신 요청 빈도를 제한해서, 새 프리픽스에서 오는 진짜 크롤러가 사라지는 대신 느려지게 하세요.
파일 단위가 아니라 운영자 단위인 이유는 무엇일까요? 일부 운영자는 파일 단위 고정을 불가능하게 만들기 때문입니다. Anthropic의 봇 세 개는 파일 하나를 공유하고 DuckDuckGo의 봇 두 개는 목록 하나를 공유하므로, "Claude-SearchBot 전용 파일"을 요구하는 검사는 대조할 대상이 없습니다. 운영자 단위 매칭은 파일이 따로 있는 Google, OpenAI, Perplexity, Mistral에서 정밀도를 조금 잃는 대신, 공유 파일에서는 절대 오작동하지 않습니다.
규칙이 두 가지 있습니다. ASN이 아니라 정확한 공개 프리픽스와 대조하세요. 우리 조회에서 Microsoft의 AS8075에는 Bingbot, GPTBot, ChatGPT-User, DuckDuckGo가 함께 있으므로, 이를 허용하면 모든 Azure 고객이 들어옵니다. 그리고 User-Agent만으로 허용하지 마세요. 파일이 없는 봇에는 운영자가 문서화한 경우 정방향 확인 역방향 DNS를 사용하세요.
빌더는 파일 19개를 모두 가져와 운영자별로 프리픽스를 묶고, 파일별 가져온 시각과 함께 allowlist.json을 씁니다. 파일이 다운로드에 실패하거나, 파싱에 실패하거나, 비어서 오거나, 마지막 정상 사본의 절반 미만으로 줄어들면 그 파일의 이전 프리픽스를 유지합니다. GeoIPHub 스캐너도 파일별 최소 프리픽스 수를 고정해 같은 방식으로 동작합니다.
// build-allowlist.mjs: Node 18+, no dependencies. Run once a day.
import fs from "node:fs";
const FEEDS = {
Google: [
"https://developers.google.com/static/crawling/ipranges/common-crawlers.json",
"https://developers.google.com/static/crawling/ipranges/special-crawlers.json",
"https://developers.google.com/static/crawling/ipranges/user-triggered-fetchers.json",
"https://developers.google.com/static/crawling/ipranges/user-triggered-fetchers-google.json",
"https://developers.google.com/static/crawling/ipranges/user-triggered-agents.json",
],
Microsoft: ["https://www.bing.com/toolbox/bingbot.json"],
Apple: ["https://search.developer.apple.com/applebot.json"],
DuckDuckGo: [
"https://duckduckgo.com/duckduckbot.json",
"https://duckduckgo.com/duckassistbot.json",
],
OpenAI: [
"https://openai.com/gptbot.json",
"https://openai.com/chatgpt-user.json",
"https://openai.com/searchbot.json",
],
Anthropic: ["https://claude.com/crawling/bots.json"],
Perplexity: [
"https://www.perplexity.ai/perplexity-user.json",
"https://www.perplexity.ai/perplexitybot.json",
],
Mistral: [
"https://mistral.ai/mistralai-user-ips.json",
"https://mistral.ai/mistralai-index-ips.json",
],
CommonCrawl: ["https://index.commoncrawl.org/ccbot.json"],
Ahrefs: ["https://api.ahrefs.com/v3/public/crawler-ip-ranges"],
};
const OUT = "allowlist.json";
const previous = fs.existsSync(OUT)
? JSON.parse(fs.readFileSync(OUT, "utf8"))
: { files: {} };
const files = {};
for (const [operator, urls] of Object.entries(FEEDS)) {
for (const url of urls) {
try {
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();
const prefixes = (body.prefixes || [])
.map((p) => p.ipv4Prefix || p.ipv6Prefix)
.filter(Boolean);
if (prefixes.length === 0) throw new Error("no prefixes in file");
const last = previous.files[url];
// A real shrink this large is rare: check it by hand before you trust it.
if (last && prefixes.length < last.prefixes.length / 2) {
throw new Error(`${prefixes.length} prefixes, last good copy had ${last.prefixes.length}`);
}
files[url] = {
operator,
prefixes,
file_date: body.creationTime || null,
fetched_at: new Date().toISOString(),
};
} catch (err) {
// Fail safe: a broken fetch keeps yesterday's prefixes for that file.
if (previous.files[url]) files[url] = previous.files[url];
console.error(`${url}: ${err.message}; ${files[url] ? "kept previous prefixes" : "no previous copy"}`);
}
}
}
fs.writeFileSync(`${OUT}.tmp`, JSON.stringify({ built_at: new Date().toISOString(), files }, null, 1));
fs.renameSync(`${OUT}.tmp`, OUT);
console.log(`${Object.keys(files).length} files written to ${OUT}`);
20 5 * * * cd /srv/bots && node build-allowlist.mjs 같은 cron 한 줄로 예약하세요. 정책 검사는 이 파일이나 조회 응답 중 하나를 읽으며, 두 경로 모두 주장된 봇의 운영자를 기준으로 합니다.
// check-claim.mjs: decide whether a bot claim is proven.
import net from "node:net";
// Claimed User-Agent token, and the operator whose file must contain the IP.
// Not exhaustive; add the bots you allow.
const CLAIMS = [
["googlebot", "Google"], ["adsbot-google", "Google"], ["google-agent", "Google"],
["bingbot", "Microsoft"], ["applebot", "Apple"],
["duckduckbot", "DuckDuckGo"], ["duckassistbot", "DuckDuckGo"],
["gptbot", "OpenAI"], ["oai-searchbot", "OpenAI"], ["chatgpt-user", "OpenAI"],
["claudebot", "Anthropic"], ["claude-searchbot", "Anthropic"], ["claude-user", "Anthropic"],
["perplexitybot", "Perplexity"], ["perplexity-user", "Perplexity"],
["mistralai-user", "Mistral"], ["mistralai-index", "Mistral"],
["ccbot", "CommonCrawl"], ["ahrefsbot", "Ahrefs"],
];
function findClaim(userAgent) {
const ua = (userAgent || "").toLowerCase();
return CLAIMS.find(([token]) => ua.includes(token)) || null;
}
// Path 1: your own allowlist.json from build-allowlist.mjs.
export function loadLists(allowlist) {
const lists = {};
for (const file of Object.values(allowlist.files)) {
lists[file.operator] ||= new net.BlockList();
for (const cidr of file.prefixes) {
const [addr, len] = cidr.split("/");
lists[file.operator].addSubnet(addr, Number(len), net.isIPv6(addr) ? "ipv6" : "ipv4");
}
}
return lists;
}
export function checkWithLists(userAgent, ip, lists) {
const claim = findClaim(userAgent);
if (!claim) return { claimed: null, verified: false };
const family = net.isIPv6(ip) ? "ipv6" : "ipv4";
return { claimed: claim[0], verified: lists[claim[1]]?.check(ip, family) === true };
}
// Path 2: one lookup per new IP, using the API's crawler fields.
export async function lookup(ip, apiKey) {
try {
const res = await fetch(`https://api.geoiphub.com/v1/lookup/${ip}`, {
headers: { "x-api-key": apiKey },
signal: AbortSignal.timeout(2000),
});
return res.ok ? await res.json() : null;
} catch {
return null; // a failed lookup proves nothing: treat it as unverified
}
}
export function checkClaim(userAgent, result) {
const claim = findClaim(userAgent);
if (!claim) return { claimed: null, verified: false };
const c = result?.threat?.crawler;
return { claimed: claim[0], verified: c?.verified === true && c?.operator === claim[1] };
}
2026년 10월 29일의 파일과 조회 결과에 두 경로를 모두 실행했고, 실패 사례도 함께 점검했습니다. GPTBot 사례는 Nmap의 공개 테스트 호스트인 45.33.32.156을 사용하며, 이 호스트는 그런 요청을 보낸 적이 없습니다. 검사는 봇 단위가 아니라 운영자 단위입니다. Google의 페처 파일에 있는 주소에서 온 Googlebot 주장도 Google로서 통과합니다.
PASS ClaudeBot UA, 216.73.216.10 verified (name ClaudeBot)
PASS Claude-SearchBot UA, 216.73.216.10 verified as Anthropic (name ClaudeBot)
PASS DuckDuckBot UA, 104.43.54.127 verified (name DuckAssistBot)
PASS AhrefsBot UA, 5.39.1.230 verified
PASS Googlebot UA, 66.249.66.1 verified
PASS GPTBot UA, 45.33.32.156 not verified (no file match)
PASS ChatGPT-User UA, 66.249.66.1 not verified (operator is Google)
PASS facebookexternalhit UA, 31.13.64.1 no claim in the map
PASS Browser UA, 216.73.216.10 no claim
PASS Lookup error, ClaudeBot UA not verified
10/10 passed, lookup path and allowlist path agree
builder, bots.json forced to fail: kept previous prefixes (14)
builder, bingbot.json returns HTML: kept previous prefixes (28)
builder, applebot.json returns []: kept previous prefixes (24)
builder, chatgpt-user.json shrinks to 1: kept previous prefixes (235)
이 코드는 허용 쪽입니다. 봇별 차단 정책을 적용하려면 AI 크롤러 가이드의 nginx 및 Express 설정을 참고하세요.
검증된 봇도 악성 봇일 수 있나요? AhrefsBot의 답은 그렇다입니다
네. 검증은 누가 그 주소를 운영하는지를 증명할 뿐, 그 주소가 깨끗하거나 환영받는다는 것을 증명하지 않습니다. AhrefsBot의 5.39.1.230은 두 가지를 동시에 보여 줍니다(2026년 10월 29일 UTC 04:42:51).
| 필드 | 값 |
|---|---|
threat.crawler.verified | true |
threat.crawler.operator | Ahrefs |
threat.crawler.category | seo |
threat.is_spammer | true |
threat.dnsbl_sources | zen.spamhaus.org |
scoring.fraud_score | 35 |
scoring.recommended_action | review |
scoring.detection_methods | datacenter_blocklisted, no_ptr_datacenter, verified_crawler, known_spammer |
검증된 크롤러 신호는 28.5점을 빼지만(신뢰도 0.95에서 30점 감점) 차단 목록 등재를 상쇄하지는 않습니다. 계산 과정은 위험 점수 사유 가이드에 나와 있습니다. 이 주소에 대해 저장해 둔 이전 값이 두 개 있습니다. 악용 등재도 작동했던 10월 19일의 59점 step_up과, 10월 22일 이후의 35점 review입니다. 10월 19일에 이 주소를 조사했을 때 RIPE 레지스트리에는 /27이 OVH 블록 안에서 Ahrefs에 등록된 것으로 나타났습니다.
AhrefsBot을 들여보낼지는 허용 목록이 정합니다. 점수는 그 대역에 이력이 있음을 알려 줍니다. SEO 크롤러가 필요 없는 사이트도 많고 필요한 사이트도 많으니, 그 판단은 여러분의 몫입니다.
설계상 robots.txt를 무시하는 정상 봇
운영자 스스로 사용자 요청 기반 페처는 robots.txt를 따르지 않을 수 있다고 말하므로, "robots.txt를 준수한다"는 정상 봇의 판별 기준이 아닙니다.
- Google: "Because the fetch was requested by a user, these fetchers generally ignore robots.txt rules."(가져오기를 사용자가 요청했기 때문에 이 페처는 일반적으로 robots.txt 규칙을 무시합니다.) (Google 사용자 요청 기반 페처)
- OpenAI, ChatGPT-User에 대해: "Because these actions are initiated by a user, robots.txt rules may not apply."(이 동작은 사용자가 시작하므로 robots.txt 규칙이 적용되지 않을 수 있습니다.) (OpenAI 봇)
- Perplexity, Perplexity-User에 대해: "Since a user requested the fetch, this fetcher generally ignores robots.txt rules."(사용자가 가져오기를 요청했으므로 이 페처는 일반적으로 robots.txt 규칙을 무시합니다.) (Perplexity 크롤러)
세 곳의 논리는 같습니다. 가져오기는 한 사람을 대신해 이루어지므로 이를 차단하면 그 사람의 요청을 차단하는 것입니다. robots.txt를 건너뛰는 검증된 페처는 문서화된 대로 동작하는 것이지 잘못 동작하는 것이 아닙니다.
robots.txt는 부탁이고, 허용 목록은 강제입니다. 사용자 요청 기반 페처를 막고 싶다면 텍스트 파일의 한 줄이 아니라 엣지의 규칙이 필요합니다. OpenAI 봇 각각이 하는 일과 각각을 차단했을 때의 비용은 OpenAI 비교 글을 참고하세요.
목록은 얼마나 최신인가: 파일 날짜, 일 단위 가져오기, 이틀의 지연
목록의 최신성은 운영자의 마지막 수정에 여러분의 가져오기 시점을 더한 만큼입니다. GeoIPHub API의 경우 매일 가져오고, 변경 사항이 반영되기까지 최대 약 이틀이 걸립니다.
2026년 10월 29일에 확인한 GeoIPHub 스캐너 코드와 구성에 따른 동작은 이렇습니다. 크롤러 단계는 각 파일을 하루에 최대 한 번 가져옵니다. 가져오기에 실패하거나 그 파일의 최소치보다 적은 프리픽스가 반환되면 스캐너는 마지막 정상 대역을 유지합니다. 운영자가 제거한 프리픽스는 다음 정상 가져오기와 그다음 일일 재빌드 이후에 검증이 중단됩니다. 조회 데이터베이스는 매일 UTC 00:00에 재빌드되며 약 04:00 UTC에 반영됩니다.
실무 규칙은 이렇습니다. 검증 판정은 몇 시간 동안 캐시하고, 파일은 매일 다시 가져오며, 새로 추가된 프리픽스는 최대 약 이틀 동안 검증에 실패할 수 있다고 예상하세요. 미검증 경로를 차단이 아니라 요청 빈도 제한기로 보내야 하는 또 다른 이유입니다.
우리가 확인한 운영자 중 갱신 주기에 대해 문서에 언급한 곳은 Perplexity뿐입니다. "These addresses are updated regularly and should be the source of truth for your WAF configurations."(이 주소들은 정기적으로 갱신되며 WAF 설정의 기준 정보로 삼아야 합니다.) Google의 검증 페이지는 빈도를 밝히지 않습니다. 2026년 10월 29일에 가져온 파일 날짜는 2024년 1월(Microsoft)부터 2026년 10월 28일(Google과 Perplexity)까지입니다. 날짜가 그대로인 것은 오류가 아닙니다. 움직이지 않는 장비군도 있습니다.
우리 쪽의 한 가지 공백이 있습니다. API는 어떤 파일과 일치했는지, 우리가 언제 가져왔는지를 보여 주지 않습니다. 그 감사 추적이 필요하면 빌더를 실행하고 fetched_at 값을 보관하세요.
공개 목록이 없는 봇과 대처법
원할 만한 일부 봇은 GeoIPHub가 불러오는 파일을 공개하지 않으므로 이 목록으로는 검증할 수 없습니다.
- OAI-AdsBot: OpenAI가 문서화하고 adsbot.json을 공개하지만 우리 스캐너는 아직 불러오지 않습니다. ChatGPT에서 광고를 운영한다면 자체 빌더에 추가하세요.
- Amazonbot: Amazon은 대역을 웹 페이지 안에 보여 주며 내려받을 파일이 없습니다. 우리는 불러오지 않습니다.
- Meta: Meta의 크롤러 페이지는 "Add to your allow list either the user agent strings or the IP addresses (more secure) used by the crawler"(크롤러가 사용하는 User-Agent 문자열이나 IP 주소(더 안전함)를 허용 목록에 추가하세요)라고 하지만 대역 파일은 제공하지 않습니다. 우리는 ASN으로만 네트워크를 매칭하며 미검증입니다.
- Yandex와 Baidu: 역방향 DNS만 사용합니다. Yandex의 안내는 검사 방법을 설명합니다. 역방향 DNS 이름이 yandex.ru, yandex.net, yandex.com으로 끝나야 하고, 그 이름의 정방향 조회가 같은 IP를 반환해야 합니다.
- Bytespider: ByteDance의 대역 파일은 찾지 못했습니다.
- 가동 시간 모니터와 결제 웹훅: 많은 곳이 자체 목록을 공개합니다. Stripe는 웹훅 IP를 JSON으로 공개하며 IP 주소 페이지에 문서화되어 있습니다. 해당 벤더의 목록을 사용하세요. 이들은 크롤러가 아니며 우리 피드에도 없습니다.
- 요청에 서명하는 봇: Web Bot Auth는 봇이 IP 대신 암호화 서명으로 자신을 증명하게 하며, 크롤러 검증 가이드에서 다룹니다. Cloudflare의 검증된 봇 디렉터리는 Web Bot Auth 또는 안정적인 User-Agent와 공개 IP 목록으로 검증된 봇을 받아들입니다.
대처법은 이렇습니다. 역방향 DNS 방식이 있으면 운영자의 방식을 쓰고, 서비스에는 벤더 자체 목록을 쓰고, 나머지에는 요청 빈도 제한기를 쓰세요.
오늘 밤 빌더를 실행하거나, 로그의 IP를 IP 조회에 붙여 넣고 Crawler 행이 "Verified crawler"인지 "Unverified crawler"인지 확인해 보세요. 헤더의 이름은 주장이고, 운영자의 파일이 증거입니다.
자주 묻는 질문
검증된 크롤러 IP 목록이란 무엇인가요?
봇 운영자가 자사 크롤러를 위해 공개하는 IP 대역 파일로 만든 목록입니다. GeoIPHub는 Google, Microsoft, Apple, OpenAI, Anthropic을 포함한 운영자 10곳의 파일 19개를 불러옵니다. OpenAI는 20번째 파일인 adsbot.json도 공개하지만 아직 불러오지 않습니다. 요청의 User-Agent가 봇 이름을 대고 IP가 그 운영자의 파일 안에 있으면 검증된 크롤러입니다.
IP 조회가 User-Agent도 보나요?
아니요. GeoIPHub 조회는 IP만 받습니다. 주소가 Anthropic이나 Google의 파일에 있다는 것은 알려 줄 수 있고, 그 운영자와 User-Agent가 주장하는 봇을 비교하는 일은 자체 코드가 합니다.
검증된 봇도 악성 봇일 수 있나요?
네. AhrefsBot의 5.39.1.230은 Ahrefs가 공개한 대역과 일치했지만 Spamhaus ZEN에도 올라 있었고, 2026년 10월 29일 GeoIPHub API에서 35점 review를 받았습니다. 검증은 누가 그 주소를 운영하는지를 알려 줄 뿐, 들여보내야 하는지를 알려 주지는 않습니다.
크롤러 IP 대역은 얼마나 자주 바뀌나요?
운영자마다 다르고 대부분은 알려 주지 않습니다. 2026년 10월 29일 기준 파일 날짜는 2024년 1월(Microsoft)부터 2026년 10월 28일(Google과 Perplexity)까지였습니다. GeoIPHub는 모든 파일을 매일 다시 가져오며, 변경 사항이 API에 반영되기까지 최대 약 이틀이 걸릴 수 있습니다.
크롤러를 ASN으로 허용해도 되나요?
안 됩니다. 많은 크롤러가 공유 클라우드에서 실행됩니다. Microsoft의 네트워크에는 Bingbot, GPTBot, DuckDuckGo의 봇이 함께 있으므로, 이를 허용하면 해당 클라우드의 모든 고객이 들어옵니다. 각 운영자의 파일에 있는 정확한 프리픽스와 대조하세요.
IP 목록으로 검증할 수 없는 봇은 무엇인가요?
운영자가 파일을 공개하지 않는 봇이 그렇습니다. Yandex와 Baidu(대신 역방향 DNS 사용), Meta(대역 파일 없음, GeoIPHub는 ASN으로 네트워크를 매칭하며 미검증), Bytespider가 해당합니다. Amazon은 Amazonbot의 대역을 웹 페이지 안에만 보여 줍니다. 검증할 수 없는 것은 요청 빈도를 제한하세요.
GeoIPHub API를 만드는 팀
GeoIPHub IP 인텔리전스 API와 스캐너를 만드는 GeoIPHub 팀이 작성했습니다. 초안은 AI의 도움을 받아 준비하고 팀이 편집합니다. API 예시는 예시용으로 표시된 경우를 제외하고 라이브 API에 대한 실제 조회 결과입니다.


