Cloudflare Bot Fight가 AdsBot을 막던 날
seominhyuk.com은 workers.dev와 달리 Managed Challenge만 보이던 문제. Bot Fight Mode를 끄고 AdsBot 200을 확인하기까지의 기록입니다.

증상은 단순했습니다. https://warpsense-blog.…workers.dev는 글이 잘 보이는데, https://seominhyuk.com만 열면 Just a moment...가 뜹니다. 브라우저에서는 잠깐 기다리면 넘어갈 수도 있지만, Googlebot·AdsBot은 그대로 403입니다.
처음엔 배포 실패인 줄 알았습니다. 그런데 ads.txt와 robots.txt는 AdsBot으로도 200이 나오고, 홈 HTML과 sitemap만 챌린지였습니다. Worker 설정(html_handling, assets) 문제가 아니라 존 보안 쪽이었습니다.
재현: 같은 콘텐츠, 다른 호스트
같은 Worker 스크립트인데 호스트만 달랐습니다. 그래서 “빌드가 깨졌다”는 가설부터 버렸습니다. 재현은 이렇게 했습니다.
# 프리뷰(대체로 통과)
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
"https://warpsense-blog.<계정>.workers.dev/"
# 커스텀 도메인(당시 실패)
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
https://seominhyuk.com/
커스텀 도메인 쪽 응답 헤더에 cf-mitigated: challenge가 보이면, 본문을 열어 보지 않아도 거의 확정입니다. 본문 앞자리를 보면 Just a moment... 패턴이 나옵니다. 브라우저로 “잠깐 기다리면 된다”고 넘어가면, 사람만 통과하고 크롤러는 영원히 남습니다.
텍스트 파일만 통과하는 것도 함정이었습니다.
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
https://seominhyuk.com/robots.txt
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
https://seominhyuk.com/ads.txt
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
https://seominhyuk.com/sitemap-index.xml
당시 /robots.txt·/ads.txt는 200, /와 sitemap은 챌린지였습니다. SEO 파일 문법을 고칠 문제가 아니었습니다.
원인
Cloudflare API로 존 bot_management를 보면 대략 이런 값이었습니다.
fight_mode: trueenable_js: true
이게 무료 플랜의 Bot Fight Mode입니다. Managed Challenge(cf-mitigated: challenge)를 붙입니다. security_level이 medium이어도 HTML은 막힐 수 있고, 반대로 security_level만 essentially_off로 낮춰도 fight_mode가 켜져 있으면 챌린지가 남았습니다.
WAF Custom Rule로 Googlebot UA를 skip 하는 규칙을 넣어 봤습니다. Super Bot Fight 단계(http_request_sbfm) skip은 되지만, 무료 Bot Fight의 Managed Challenge는 그대로인 경우가 있었습니다. 결국 fight_mode를 false로 끄는 쪽이 확실했습니다.
내가 먼저 실패한 시도
- 캐시만 purge — 챌린지 HTML이 캐시에 남았을까 해서 비웠습니다. 사람 브라우저 체감만 조금 바뀌고, AdsBot은 그대로였습니다.
- Worker
html_handling/ assets 경로 재확인 — workers.dev가 정상이라 배포 산출물 가설은 금방 죽었습니다. - security_level만 낮추기 —
essentially_off로 바꿔도fight_mode: true면 Managed Challenge가 남았습니다. “보안 레벨”과 “Bot Fight”는 다른 스위치입니다. - WAF에서 Googlebot skip — Super Bot Fight용 skip 경로와 무료 Bot Fight 경로가 다르다는 걸 나중에야 알았습니다. 규칙이 “적용됨”으로 보여도 AdsBot curl은 403이었습니다.
실패를 기록해 둔 덕분에, 다음부터는 “대시보드에 초록불”보다 봇 UA curl 결과를 진실로 삼습니다.
한 일
- Zone Settings에서
security_level→essentially_off,browser_check→off PUT /zones/.../bot_management로fight_mode: false,enable_js: false- 캐시 purge
- 다시 curl
curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
https://seominhyuk.com/
이후 홈, /about, /privacy, sitemap, 샘플 글이 200이 됐습니다. cf-mitigated 헤더도 사라졌습니다.
대시보드에서는 Security → Bots 에서 Bot Fight Mode를 끄면 같은 효과입니다. API 토큰에 Zone Settings / Bot Management 권한이 있어야 자동화할 수 있습니다. Workers만 배포 가능한 토큰으로는 안 바뀝니다. 배포 CI와 보안 설정 CI를 같은 토큰에 몰아넣지 않는 편이 낫습니다. 권한이 과한 토큰이 로그·노션에 붙으면 면적이 커집니다.
끄기 전후 체크리스트
끄기 직후 저는 최소한 아래를 한 번에 돌립니다. 한 URL만 통과했다고 끝내지 않습니다.
UA='AdsBot-Google (+http://www.google.com/adsbot.html)'
for path in / /about /privacy /robots.txt /ads.txt /sitemap-index.xml; do
echo "=== $path ==="
curl -sI -A "$UA" "https://seominhyuk.com$path" | head -n 15
done
헤더에 cf-mitigated가 없고, HTML 경로가 200이면 통과로 봅니다. Googlebot UA로도 한 번 더 확인합니다. AdSense·Search Console은 그다음입니다.
검수 전에 남기는 습관
도메인만 붙이고 AdSense·Search Console로 바로 가지 않습니다. 최소한:
- AdsBot으로
/와/ads.txt - Googlebot으로
/sitemap-index.xml
을 봅니다. 텍스트 파일만 통과하고 HTML이 막히면, SEO 파일보다 챌린지를 의심합니다.
광고·검색 승인을 노리는 동안에는 Bot Fight를 다시 켜지 않는 편이 낫습니다. 나중에 켜야 한다면 Google 크롤러를 예외로 두는 규칙이 실제로 먹히는지, 끄기 전에 반드시 봇 UA로 재확인하세요. “규칙 저장됨”과 “AdsBot이 200”은 다른 사건입니다.
운영 습관으로는 배포 스크립트 끝에 위 curl 루프를 붙여 둡니다. 실패하면 exit non-zero로 끊습니다. 사람 눈으로 시크릿 창만 보면, JS 챌린지를 통과한 뒤의 HTML만 보고 “정상”이라고 착각하기 쉽습니다.
트레이드오프도 분명합니다. Bot Fight를 끄면 저품질 봇 트래픽이 늘 수 있습니다. 블로그 초기에 광고·검색 검수가 목표면 그 비용을 감수하고, 트래픽이 커진 뒤에는 WAF·속도 제한·유료 Bot 관리로 옮기는 쪽이 현실적입니다. 무료 fight_mode에만 기대면 “사람만 되고 구글만 막히는” 상태가 다시 옵니다.
이 장애를 겪고 나서야 Search Console 색인 요청 글의 “요청 버튼보다 접근성” 문장이 실감 났습니다. 배포 성공과 크롤 가능은 다른 문제입니다. 도메인 DNS·HTTPS까지 맞춰 둔 뒤에도 같은 증상이면, 커스텀 도메인 DNS 체크보다 존 Bot 설정을 먼저 의니다.