robots.txt와 sitemap.xml 올바르게 쓰는 법
정적 블로그에서 robots.txt와 sitemap을 Minimal하게 유지하는 방법과, 파일이 200인데도 본문 HTML이 막히던 사례입니다.

robots.txt와 sitemap은 순위를 바로 올려 주지 않습니다. 다만 “어디를 봐도 되는지”, “어떤 URL이 있는지”를 크롤러에게 알려 주는 기본 안내판입니다. 서민혁닷컴은 Astro @astrojs/sitemap으로 빌드 시 사이트맵을 만들고, public/robots.txt는 아주 짧게 둡니다.
지금 쓰는 robots.txt
User-agent: *
Allow: /
Sitemap: https://seominhyuk.com/sitemap-index.xml
이것만으로도 블로그 초기에는 충분합니다. /posts/를 Disallow로 막는 실수만 하지 않으면 됩니다. robots.txt는 보안 도구가 아닙니다. 관리자 URL이나 토큰을 적는 용도가 아닙니다.
ads.txt는 광고 쪽 파일이라 robots와 별개지만, 같은 public 폴더에 둡니다. 검수 전에 AdsBot으로 ads.txt와 홈 HTML을 둘 다 치는 게 좋습니다.
내가 피한(그리고 예전에 거의 저지를 뻔한) 실수
Disallow: /posts/— “초안이 색인될까 봐”라는 생각에 가깝습니다. 공개 블로그면 본문이 막힙니다. 초안은 배포하지 않거나 noindex로 분리합니다.- Sitemap URL을
http://로 적기 — canonical·사이트맵이 https인데 robots만 http면 안내판이 엇나갑니다. - 여러 User-agent 블록에 서로 다른 Allow/Disallow — 초기에는
*하나면 됩니다. 복잡하게 나누면 나중에 “왜 이 봇만”을 디버깅하기 어렵습니다. - robots에 비밀 경로를 적어 ‘숨기기’ — 공개 파일에 경로를 광고하는 것과 같습니다. 진짜로 막으려면 인증·비공개 배포가 답입니다.
sitemap은 인덱스가 생기기 쉽다
Astro sitemap을 켜면 sitemap-index.xml이 진입점이고, 실제 URL 목록은 sitemap-0.xml 같은 파일로 갈라질 수 있습니다. Search Console에는 인덱스 URL을 제출하면 됩니다. 예전에 /sitemap.xml만 가정하고 404를 보면, 빌드 산출물 이름을 다시 확인하세요.
사이트맵에 넣지 않는 것: 검색 결과, 태그 조합으로 무한히 늘어나는 페이지, 프리뷰 URL. 공개할 글·소개·약관 정도만 넣습니다.
로컬에서 내가 확인하는 순서
npm run build
ls -la dist/robots.txt dist/sitemap*.xml
# 인덱스가 있으면 본문 링크를 한 번 열어 본다
head -n 40 dist/sitemap-index.xml
public/robots.txt만 있고 dist/에 없으면, Astro가 public을 복사하는 단계에서 빠졌거나 빌드 전에 배포한 겁니다. Search Console에만 “제출됨”이 뜨고 실제로는 404인 상태가 됩니다. 반대로 sitemap 플러그인을 켠 뒤 dist/sitemap-0.xml은 있는데 Search Console에 /sitemap.xml만 넣어 두면 계속 실패합니다. 파일 이름과 제출 URL을 같게 맞춥니다.
슬러그를 바꾼 뒤에는 사이트맵에 새 URL이 들어갔는지, 옛 URL이 301/리다이렉트로 이어지는지 같이 봅니다. canonical이 옛 경로를 가리키면 색인이 꼬입니다. 구조화 데이터(JSON-LD) URL과도 맞춰 두는 편이 덜 헷갈립니다. 관련해서는 JSON-LD 글과 같이 보면 됩니다.
“파일은 200인데 글은 403”이 가능했다
이상하게도 AdsBot으로 치면:
/robots.txt→ 200/ads.txt→ 200/,/sitemap-index.xml→ 403 Challenge
처럼 텍스트 파일만 통과하는 경우가 있었습니다. robots와 sitemap 내용이 잘못돼서가 아니라, Cloudflare가 HTML(과 일부 XML) 앞에 Managed Challenge를 붙인 탓입니다. 파일 문법을 고치기 전에 curl로 응답 본문 앞자리를 보세요. Just a moment...면 SEO 파일이 아니라 보안 설정 문제입니다. 대응은 Bot Fight 글에 있습니다.
실패했던 디버깅 순서
처음엔 robots 문법을 의심했습니다. Allow/Disallow 공백, Sitemap 줄 위치, BOM 같은 걸 의심하며 파일을 여러 번 고쳤습니다. 그런데 AdsBot으로 robots는 200이고 본문은 User-agent 내용이 그대로였습니다. 문법 문제가 아니었습니다.
그다음엔 sitemap URL 목록이 비었는지 의심했습니다. 로컬 dist/sitemap-0.xml에는 글 URL이 있었습니다. 프로덕션에서 sitemap만 챌린지 HTML이 내려오니, Search Console은 “가져올 수 없음”에 가깝게 보였습니다.
결정적 단서는 같은 Worker의 workers.dev는 HTML 200, 커스텀 도메인만 챌린지였다는 점이었습니다. 안내판(robots/sitemap)을 고칠 문제가 아니라, 문이 잠긴 문제였습니다.
검수용으로 남겨 둔 명령:
UA='AdsBot-Google (+http://www.google.com/adsbot.html)'
curl -sI -A "$UA" https://seominhyuk.com/robots.txt | head -n 12
curl -sI -A "$UA" https://seominhyuk.com/ | head -n 12
curl -sI -A "$UA" https://seominhyuk.com/sitemap-index.xml | head -n 12
# 본문이 챌린지인지 확인
curl -s -A "$UA" https://seominhyuk.com/ | head -n 5
헤더에 cf-mitigated: challenge가 보이거나 본문 앞에 Just a moment...가 보이면, robots.txt 줄을 더 만지지 않습니다.
운영할 때
글 슬러그를 바꾸면 사이트맵의 새 URL과 旧 URL 리다이렉트를 같이 봅니다. canonical이 옛 경로를 가리키면 색인이 꼬입니다.
로컬에서 빌드한 뒤 dist/robots.txt, dist/sitemap-*.xml이 있는지 확인합니다. public/에만 두고 빌드 설정이 빠져 배포본에 없으면 Search Console만 이상해집니다.
배포 직후에는 CDN에 옛 sitemap이 남을 수 있습니다. 새 글이 사이트맵에 안 보이면 퍼지 후 다시 curl합니다. Search Console Sitemaps 화면의 “성공”은 제출 접수를 뜻하는 경우가 많고, 개별 URL 색인과는 별개입니다.
트레이드오프: robots를 짧게 두면 “세밀한 봇별 규칙”은 포기합니다. 대신 실수 면적이 줄고, 장애가 나면 Bot Fight·DNS·canonical처럼 다른 층을 의심하기 쉽습니다. 블로그 초기에는 그 단순함이 이득이었습니다.
색인 요청 흐름은 Search Console 색인 요청에 이어서 보면 됩니다. robots·sitemap은 “안내판”이고, 안내판이 가리키는 문이 잠겨 있으면(챌린지) 안내를 고쳐도 방문자는 못 들어갑니다.