MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

8분 읽기 · 마지막 업데이트

robots.txt 디버깅 가이드: 어느 줄이 URL을 막는지 RFC 9309로 찾기

robots.txt의 판정은 그룹 하나와 이기는 규칙 하나로 정해집니다. 크롤러가 이를 고르는 순서를 알면 추측 대신 정확한 줄을 짚을 수 있습니다.

robots.txt가 다루는 범위

로봇 배제 프로토콜은 RFC 9309로 표준화되어 있습니다. robots.txt는 얌전한 크롤러에게 어떤 경로를 요청해도 되는지 알려 주는 파일입니다. 제어 대상은 색인이 아니라 크롤링이고, 접근 제어도 아닙니다. 차단한 URL도 브라우저로는 누구나 열 수 있고, 규칙을 무시하는 봇은 막지 못합니다.

파일의 적용 범위는 하나의 오리진입니다. https://example.com/robots.txt는 그 스킴·호스트·포트에만 적용됩니다. https://www.example.com, https://blog.example.com, http://example.com은 각각 자기 파일이 필요하고, 한쪽 규칙이 다른 쪽으로 번지지 않습니다. 규칙이 먹지 않는 것 같으면 먼저 문제의 호스트가 실제로 내려주는 파일을 고치고 있는지부터 확인하세요.

규칙은 도메인이 아니라 경로와 쿼리 문자열에 대해 비교합니다. 이 글의 판정 결과는 모두 이 사이트의 robots.txt 테스터가 쓰는 판정 함수를 직접 실행해 얻은 값이고, 이 함수는 RFC 9309를 따릅니다.

1단계: 이 크롤러에 적용되는 그룹

파일은 그룹의 나열입니다. 그룹은 User-agent 줄 하나 이상으로 시작하고 그 아래 Allow·Disallow 줄이 이어집니다. RFC 9309는 크롤러의 제품 토큰과 일치하는 그룹을 대소문자 구분 없이 찾으라고 합니다. 일치하는 그룹이 있으면 그 규칙을 쓰고, 어떤 그룹도 크롤러를 지정하지 않을 때만 * 그룹을 봅니다. 적용되는 그룹은 하나뿐이라서, 이름이 지정된 그룹 위에 * 그룹의 규칙이 더해지는 일은 없습니다.

가장 흔한 오해가 여기서 나옵니다. 아래 파일에서 크롤러별로 무엇을 가져갈 수 있는지 살펴보겠습니다.

크롤러경로판정결정한 규칙
Googlebot/drafts/a차단Googlebot 그룹의 Disallow /drafts/ (7번째 줄)
Googlebot/private/a허용* 그룹은 Googlebot에 적용되지 않음
GOOGLEBOT/drafts/a차단토큰 비교는 대소문자를 구분하지 않음
Googlebot-Image/drafts/a허용자기 그룹(9~10번째 줄)이 있어 Googlebot 규칙을 물려받지 않음
Googlebot-News/drafts/a차단자기 그룹이 없어 Googlebot 그룹을 사용
Bingbot/private/a차단지정된 그룹이 없어 *로 이동: Disallow /private/ (2번째 줄)
User-agent: *
Disallow: /private/
Disallow: /tmp
Allow: /tmp/public/

User-agent: Googlebot
Disallow: /drafts/

User-agent: Googlebot-Image
Disallow: /photos/

그룹에서 자주 걸리는 세부 사항

비교 대상은 user-agent 문자열 전체의 부분 문자열이 아니라 제품 토큰입니다. User-agent: bot은 이름 안에 글자가 들어 있어도 Googlebot과 일치하지 않습니다. Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) 같은 전체 문자열로 테스트하면 테스터가 토큰 googlebot을 뽑아내고, 결과는 Googlebot만 넣었을 때와 같습니다.

연속된 User-agent 줄은 하나의 그룹을 공유하므로 이름 두 개가 같은 규칙 위에 올 수 있습니다. 빈 줄이 있어도 그룹이 끝나지는 않고, 규칙 줄 뒤에 오는 User-agent 줄이 새 그룹을 시작합니다. 같은 크롤러가 서로 다른 그룹에 두 번 나오면 RFC 9309에 따라 규칙을 비교하기 전에 하나의 그룹으로 합쳐집니다. 긴 파일 아래쪽에 User-agent: Googlebot 블록을 하나 더 넣으면 위쪽 블록의 결과가 달라질 수 있고, 테스터는 여러 그룹에 나오는 이름에 경고를 띄웁니다.

이름이 지정된 그룹이 * 그룹을 대체하므로 계속 적용하고 싶은 일반 규칙은 그 그룹 안에 다시 적어야 합니다. 아래 예에서 Googlebot 그룹에는 Allow: /뿐이라 /secret까지 허용되고, Bingbot은 전체 차단 규칙에 걸립니다.

User-agent: *
Disallow: /
Allow: /public/

User-agent: Googlebot
Allow: /

# Googlebot /secret   -> 허용 (allow /, 6번째 줄)
# Bingbot   /secret   -> 차단 (disallow /, 2번째 줄)
# Bingbot   /public/x -> 허용 (allow /public/, 3번째 줄)

2단계: 가장 긴 패턴이 이기고, 길이가 같으면 Allow

선택된 그룹 안에서는 경로와 맞는 모든 규칙이 후보가 됩니다. RFC 9309는 가장 구체적인 일치, 곧 옥텟 수가 가장 많은 일치를 따르라고 하고, 실제로는 가장 긴 패턴을 뜻합니다. Allow와 Disallow가 같은 길이로 맞으면 Allow를 쓰는 것이 권장됩니다. 맞는 규칙을 모두 비교하기 때문에 그룹 안에서 줄의 순서는 결과에 영향을 주지 않습니다.

아래 그룹은 겹치는 규칙 쌍이 세 개 들어 있습니다. 모든 판정은 판정 함수를 실행해서 얻었습니다.

  • 길이는 정규화한 패턴의 글자 수로 세며 *도 한 글자입니다. 두 규칙이 겹칠 때만 직접 길이를 비교하면 됩니다.
  • 값이 빈 Disallow:는 아무 경로와도 맞지 않으며, 그 그룹에는 모두 허용이라는 표준적인 표현입니다.
  • 어떤 규칙에도 맞지 않는 경로는 허용됩니다. robots.txt는 허용 목록이 아니라 예외를 둘 수 있는 차단 목록입니다.
경로판정이긴 규칙이유
/tmp/public/r.html허용Allow /tmp/public/ (3번째 줄)12자가 4자보다 김
/tmp/other차단Disallow /tmp (2번째 줄)Disallow만 일치
/tmpfile차단Disallow /tmp (2번째 줄)접두사 매칭이라 /tmpfile도 일치
/search허용Allow /search (5번째 줄)길이가 같아 Allow가 동률을 가져감
/search?q=1허용Allow /search (5번째 줄)쿼리 문자열도 비교 대상에 포함
/shop/shoes/cart허용Allow /shop/*/cart$ (7번째 줄)13자가 12자보다 김, 패턴이 경로 끝에서 끝남
/shop/shoes/cart?x=1차단Disallow /shop/*/cart (6번째 줄)쿼리 때문에 $가 맞지 않음
User-agent: *
Disallow: /tmp
Allow: /tmp/public/
Disallow: /search
Allow: /search
Disallow: /shop/*/cart
Allow: /shop/*/cart$

와일드카드, 끝 고정, 대소문자

RFC 9309가 패턴 안에서 정의하는 특수 문자는 둘뿐입니다. *는 /를 포함해 길이 0 이상의 임의 문자열이고, 패턴 맨 끝의 $는 비교 대상 텍스트의 끝에 고정합니다. 나머지는 모두 접두사로 비교하는 문자 그대로의 값입니다. 정규식, 문자 클래스, 선택 구문은 없습니다.

user-agent 토큰과 달리 패턴은 대소문자를 구분합니다. 문의가 가장 많은 조합을 아래 파일로 확인해 봅니다.

경로판정이유
/files/a.pdf차단정확히 .pdf로 끝남
/files/a.pdf?dl=1허용쿼리 때문에 경로가 .pdf로 끝나지 않음
/files/a.PDF허용대소문자가 달라 일치하지 않음
/files/a.pdf/허용끝의 슬래시가 $ 고정을 깨뜨림
/p?sessionid=9차단/*?sessionid=와 일치
/p?id=1&sessionid=9허용패턴은 sessionid 바로 앞에 ?를 요구함
/admin/허용소문자 경로이고 규칙은 /Admin/용
/Admin/x차단대소문자가 정확히 일치
/draft허용/*/draft는 draft 앞에 슬래시가 필요해 최상위 /draft는 일치하지 않음
/a/b/draft/c차단*가 a/b를 삼키고, 접두사 매칭이라 뒤는 무관
User-agent: *
Disallow: /*.pdf$
Disallow: /*?sessionid=
Disallow: /Admin/
Disallow: /*/draft

퍼센트 인코딩과 비ASCII 경로

실제 URL에는 비ASCII 문자와 퍼센트 이스케이프가 들어가고, RFC 9309는 정규화한 뒤 비교하라고 합니다. 크롤러는 /café/menu를 /caf%C3%A9/menu로 요청하므로 규칙을 어느 쪽으로 적어도 같게 동작해야 합니다. 테스터는 양쪽을 정규화합니다. 16진수는 대문자로 바꾸고, %7E 같은 비예약 문자는 디코딩하고, 비ASCII 문자는 UTF-8로 인코딩합니다.

파일에 Disallow: /caf%C3%A9/가 있으면 /café/menu와 /caf%c3%a9/menu가 모두 차단되고, 규칙을 소문자 16진수로 적어도 결과는 같습니다. 예약 문자는 인코딩된 채로 남습니다. /a%2fb와 /a/b는 다른 경로이므로, 애플리케이션이 인코딩된 슬래시를 데이터로 쓴다면 이 차이가 중요합니다.

파일 가져오기: 상태 코드, 리디렉션, 크기, 캐시

규칙을 보기 전에 크롤러는 파일부터 가져와야 하고, RFC 9309는 결과별 의미를 정해 두었습니다. 2xx 응답은 파싱합니다. 리디렉션은 따라가며 최소 연속 다섯 번은 따라가야 합니다. 4xx 응답은 파일이 없는 것으로 보아 크롤러가 호스트의 모든 리소스에 접근해도 되므로, robots.txt가 없는 것은 전체 허용과 같습니다. 5xx 응답이나 네트워크 실패는 접근 불가로 보아, 다시 가져올 수 있을 때까지 전체 차단으로 가정해야 합니다.

이 비대칭 때문에 배포가 불안정하면 파일 내용은 멀쩡한데 사이트 전체의 크롤링이 조용히 멈출 수 있습니다. 크롤러가 따른 것은 내 규칙이 아니라 오류 응답이기 때문입니다. robots.txt를 다른 정적 자산과 같은 곳에서 200과 text/plain으로 내려주면 대부분의 문제를 피합니다. 상태 코드 200으로 나간 HTML 오류 페이지는 더 나쁩니다. 규칙 파일로 파싱되어 모든 줄이 알 수 없는 줄로 무시되기 때문입니다.

크롤러는 최소 500 키비바이트를 파싱해야 하고 한도를 넘는 부분은 무시해도 되므로, 아주 큰 파일 아래쪽의 규칙은 조용히 버려질 수 있습니다. 이 사이트의 테스터는 처음 500 KiB만 분석하고 나머지 바이트를 무시하면 경고합니다. 응답은 캐시될 수 있으며 RFC 9309는 갱신할 수 없는 경우가 아니면 24시간이 넘은 사본을 쓰지 않도록 권고합니다. 규칙을 고친 뒤 크롤러에 반영되기까지 하루 정도는 걸릴 수 있습니다.

Sitemap 줄과 효과 없는 지시어

Sitemap:은 RFC 9309에 정의되어 있지 않고 sitemaps.org 프로토콜에서 온 지시어입니다. 그룹과 무관해서 파일 어디에 있든 모든 크롤러에 적용되고, 값은 절대 URL이어야 합니다. 테스터는 http와 https만 받고 나머지는 경고하므로 Sitemap: /sitemap.xml처럼 상대 경로를 적는 흔한 실수를 잡아냅니다. 사이트맵 내용 자체는 XML 사이트맵 검사기에 넣어 확인하세요.

파서가 쓸 수 없는 줄은 오류 없이 건너뜁니다. Crawl-delay는 지키는 크롤러도 있고 무시하는 크롤러도 있으니 부하 조절에 기대지 마세요. 콜론이 빠진 Disallow /oops 같은 줄은 버려져서 적었다고 생각한 규칙이 사실은 없는 상태가 됩니다. 경고에 줄 번호가 붙으므로 찾기 쉽습니다. 아래는 상대 경로 사이트맵, 콜론 누락, Noindex 줄이 든 파일의 실행 결과입니다.

User-agent: *
Disallow: /private
Allow: /private/help
Disallow: /private/help/old
Sitemap: https://example.com/sitemap.xml
Sitemap: /relative.xml
Crawl-delay: 10
Disallow /oops
Noindex: /x

# 경고: sitemapInvalid (6번째 줄), unknown "Disallow /oops" (8번째 줄),
#       unknown "noindex" (9번째 줄)
# /private/help/new   -> 허용 (allow /private/help, 3번째 줄)
# /private/help/old/a -> 차단 (disallow /private/help/old, 4번째 줄)
# /private/other      -> 차단 (disallow /private, 2번째 줄)

robots.txt로 검색 결과에서 페이지를 지울 수 없는 이유

Disallow는 크롤러가 URL을 가져가지 못하게 할 뿐, 검색엔진이 그 URL을 잊게 하지는 않습니다. 다른 페이지가 차단된 URL을 링크하면 검색엔진은 URL이 존재한다는 것은 알지만 페이지를 읽지 못했을 뿐이므로, 설명 없이 URL만 결과에 올릴 수 있습니다.

색인에서 빼려면 페이지를 크롤링할 수 있어야 하고, HTML의 robots 메타 태그나 X-Robots-Tag 응답 헤더로 noindex를 내려야 합니다. 같은 경로를 Disallow로도 막으면 크롤러가 페이지를 가져가지 않으니 noindex를 볼 수 없고, 두 장치가 서로를 무력화합니다. 맞는 순서는 크롤링을 허용하고, noindex를 내리고, URL이 빠질 때까지 기다린 뒤, 그다음에 차단 여부를 정하는 것입니다. robots.txt 안의 Noindex: 줄은 대안이 될 수 없습니다. Google은 2019년에 이 줄을 지원하지 않게 되었고, 테스터도 알 수 없는 지시어로 보고합니다.

비공개여야 하는 콘텐츠는 인증으로 보호하세요. 아무리 잘 쓴 robots.txt도 숨기고 싶은 경로를 알려 주는 공개 파일입니다.

디버깅 체크리스트

아래 순서대로 확인하세요. 막힌 사례의 대부분은 앞의 네 항목에서 풀립니다.

  • 스킴·호스트·포트까지 정확한 오리진에서 실제 파일을 가져와 상태 코드 200, 일반 텍스트 본문, HTML 오류 페이지가 아닌지 확인합니다.
  • 그 텍스트를 그대로 테스터에 붙여 넣어, 편집기 사본이 아니라 배포된 내용을 디버깅합니다.
  • 실제 크롤러 이름을 고르고 그 이름의 그룹이 따로 있는지 봅니다. 있다면 * 그룹은 그 크롤러에 무관합니다.
  • 쿼리 문자열을 포함한 전체 경로를 URL과 같은 대소문자·인코딩으로 테스트합니다.
  • 맞는 규칙을 모두 찾아 가장 긴 것을 고르고, Allow와 Disallow의 길이가 같으면 Allow가 이깁니다.
  • 같은 크롤러를 지정한 다른 그룹이 있는지 확인합니다. 중복 그룹은 합쳐집니다.
  • 콜론 누락, 상대 경로 Sitemap, 슬래시 없는 패턴, /를 막는 와일드카드 그룹에 대한 경고를 읽습니다.
  • 페이지가 계속 색인된다면 robots.txt 수정을 멈추고 noindex, canonical 태그, 페이지의 크롤링 가능 여부를 봅니다.

직접 해보기