DNS 레코드를 추가했는데 아직 '없는 이름'으로 나올 때: NXDOMAIN, NODATA와 네거티브 캐시
레코드를 추가한 뒤에도 일부 리졸버가 계속 '없음'으로 답하는 이유를 RFC로 정리했습니다. NXDOMAIN과 NODATA의 차이, SOA로 정해지는 네거티브 TTL, 하위 이름까지 막는 NXDOMAIN cut, 실패 캐시의 5분 상한을 다룹니다.
새 서브도메인의 레코드를 권한 서버에 넣었는데, 어떤 네트워크에서는 바로 보이고 어떤 네트워크에서는 한동안 “그런 이름은 없다”는 답이 계속 나올 때가 있습니다. 리졸버는 있는 레코드만 캐시하지 않습니다. “없다”는 답도 캐시합니다. 이것을 네거티브 캐싱이라고 하고, 규칙은 RFC 2308에 있습니다. 이 글은 RFC 2308과, RFC 2308을 갱신한 문서 가운데 이 주제와 관련된 RFC 8020, RFC 9520, 그리고 관련된 RFC 8767을 기준으로 했습니다. 특정 리졸버 제품의 기본값은 다루지 않습니다.
DNS 조회의 기본 흐름과 일반 레코드의 TTL은 도메인 이름이 IP 주소가 되기까지: DNS 조회 과정에 정리해 두었습니다.
“없다”에는 두 종류가 있다
RFC 2308 1절과 2절은 부정 응답을 두 가지로 나눕니다.
- NXDOMAIN: 응답 헤더의 RCODE가 Name Error입니다. 질문한 이름 자체가 존재하지 않는다는 뜻입니다.
- NODATA: RCODE는 NOERROR인데 답변 섹션에 해당 타입의 레코드가 없습니다. 이름은 있지만 그 타입의 레코드가 없다는 뜻입니다. NODATA를 나타내는 RCODE 값은 따로 없어서, 응답 내용을 보고 판단해야 합니다.
캐시에 저장되는 단위도 다릅니다(RFC 2308 5절).
- NXDOMAIN은
<QNAME, QCLASS>에 대해 캐시됩니다. 타입과 상관없이 그 이름 전체가 “없음”으로 기억됩니다. - NODATA는
<QNAME, QTYPE, QCLASS>에 대해 캐시됩니다. 그 타입만 “없음”입니다.
그래서 이미 A 레코드가 있는 이름에 AAAA 레코드를 추가하면, 캐시에 남아 있을 수 있는 것은 AAAA에 대한 NODATA뿐이고 A 조회에는 영향이 없습니다. 반대로 아직 없던 이름을 처음 만드는 경우라면, 그 전에 누군가 그 이름을 조회해서 받은 NXDOMAIN이 모든 타입의 조회를 막고 있을 수 있습니다.
네거티브 TTL은 SOA에서 온다
일반 응답은 답변 섹션의 레코드마다 TTL이 있습니다. 부정 응답에는 답변 레코드가 없으므로 TTL을 다른 방법으로 실어야 합니다. RFC 2308 3절은 권한 서버가 NXDOMAIN이나 NODATA로 답할 때 그 영역(zone)의 SOA 레코드를 authority 섹션에 반드시(MUST) 넣으라고 정합니다. 이 SOA 레코드의 TTL은 SOA의 MINIMUM 필드와 SOA 레코드 자신의 TTL 중 작은 값으로 설정되고, 그 값이 리졸버가 부정 응답을 캐시할 수 있는 시간입니다.
MINIMUM 필드는 원래 여러 뜻으로 쓰였습니다. RFC 2308 4절은 “영역 안 모든 레코드의 최소 TTL”이라는 원래 의미를 폐기하고, “TTL이 적히지 않은 레코드의 기본 TTL”이라는 쓰임 대신 마스터 파일에 $TTL 지시어를 추가했습니다. 그리고 MINIMUM의 의미를 부정 응답의 TTL로 새로 정했습니다. 이름은 MINIMUM이지만 지금은 네거티브 캐시 시간을 정하는 값입니다.
캐시된 부정 응답의 TTL은 일반 응답처럼 줄어들고, 0이 되면 더 이상 쓰면 안 됩니다(MUST NOT, 5절). 캐시에서 부정 응답을 돌려줄 때는 캐시에 있던 시간만큼 줄인 TTL로 SOA를 붙여야 합니다(6절). RFC 2308 10절의 예시에서는 MINIMUM이 1200초인 영역에 없는 이름을 물었을 때 첫 응답의 SOA TTL이 1200이고, 10분 뒤 캐시에서 나온 응답에서는 600입니다. 리졸버가 보여 주는 SOA TTL은 남은 시간으로 읽으면 됩니다.
여기서 두 가지를 알 수 있습니다.
- 부정 응답의 유효 시간은 리졸버가 그 응답을 받을 때 정해집니다. 나중에 MINIMUM을 낮춰도 이미 캐시된 항목은 받을 때의 TTL대로 줄어듭니다.
- 새 이름을 만들 계획이 있다면, 그 영역의 SOA MINIMUM과 SOA TTL 중 작은 값이 “미리 조회된 경우 최악으로 얼마나 기다릴 수 있는가”의 기준이 됩니다. 리졸버가 따로 상한을 두면 그보다 짧아질 수 있습니다.
SOA가 없는 부정 응답은 캐시하지 않아야 합니다(SHOULD NOT, 5절). TTL이 줄어드는 근거가 없으면, 서로를 포워더로 지정한 서버 둘 사이에서 부정 응답이 TTL이 계속 초기화된 채 돌 수 있기 때문입니다.
리졸버 쪽 상한
RFC 2308 5절은 리졸버가 네거티브 캐시 기간에 상한을 두는 것이 합리적이라고 하면서, 그 이유로 프로토콜이 최대 68년까지의 캐시를 허용한다는 점을 들었습니다. 같은 절은 그 상한이 일반 응답에 두는 상한보다 크지 않아야 하며 조정할 수 있는 편이 좋다고 적고, 1~3시간이 잘 동작해 왔고 기본값으로 적당하며 하루를 넘는 값은 문제가 있었다는 경험도 남겼습니다. 이는 1998년 문서의 권고입니다.
68년이라는 전제는 그 뒤에 바뀌었습니다. 2020년의 RFC 8767 4절은 RFC 1035의 TTL 정의를 고쳐, 최상위 비트가 켜진 TTL 값을 0이 아니라 양수로 해석하게 하고, TTL 값에는 며칠에서 몇 주 수준의 상한을 두어야 한다(SHOULD)고 정했습니다. 권장 상한은 604,800초(7일)입니다. 같은 절은 RFC 2181 8절의 31비트가 허용하던 68년 대신 7일을 제안한 것이 주요 최신 리졸버의 관행을 반영한 것이라고 설명합니다. 지금의 각 리졸버가 실제로 어떤 상한을 쓰는지는 제품과 버전, 설정마다 다르므로 이 글에서는 다루지 않습니다.
NXDOMAIN은 그 아래 이름까지 없다는 뜻이다
RFC 8020은 NXDOMAIN의 의미를 분명히 했습니다. 도메인 이름은 트리이므로, 어떤 노드가 없으면 그 아래 서브트리 전체도 없습니다. 2절은 반복 조회를 하는 캐싱 리졸버가 NXDOMAIN을 받으면 그것을 캐시하고, 그 노드와 그 아래의 모든 이름을 도달할 수 없는 것으로 보고, 이후 그 아래 이름에 대한 질문에도 NXDOMAIN으로 답해야 한다(SHOULD)고 정합니다. 이를 NXDOMAIN cut이라고 부릅니다. 3.2절은 RFC 2308 5절의 문장을 고쳐, 원래 QNAME의 하위 이름에 대한 질문에도 캐시된 NXDOMAIN을 돌려줄 수 있게 했습니다.
RFC 8020의 예시는 이렇습니다. 존재하지 않는 foo.example을 물어 NXDOMAIN을 받은 리졸버는, 이어서 들어온 bar.foo.example 질문을 권한 서버에 보내지 않고 바로 NXDOMAIN으로 답할 수 있습니다. 반대로 처음 질문이 bar.foo.example이었다면 그 NXDOMAIN은 baz.foo.example에 대해 아무것도 알려 주지 않습니다. 함께 온 SOA의 소유자 이름으로 더 위쪽 이름까지 없다고 추론해서도 안 됩니다(부록 A). SOA는 영역의 꼭대기를 가리킬 뿐, 존재하는 가장 가까운 이름을 가리키지 않기 때문입니다.
이것이 실제로 문제가 되는 경우를 하나 들면 다음과 같습니다. example.com 영역에 dev.example.com이 없던 시점에 누군가 dev.example.com을 조회했고, 그 뒤에 api.dev.example.com 레코드를 추가했다고 합시다. NXDOMAIN cut을 따르는 리졸버는 dev.example.com의 NXDOMAIN이 캐시에 남아 있는 동안 api.dev.example.com에도 NXDOMAIN으로 답할 수 있습니다. 새로 만든 이름을 한 번도 조회한 적이 없는데도 그렇습니다.
몇 가지 단서가 있습니다.
- 이 규칙은 MUST가 아니라 SHOULD입니다. RFC 8020 6절은 캐시를 트리가 아닌 구조(예: 딕셔너리)로 구현한 리졸버에는 이 규칙을 따르지 않을 이유가 있다고 설명합니다. 1절은 2016년 발행 당시 알려진 대부분의 리졸버가 캐시된 부재를 하위 이름의 부재 증거로 보지 않았다고 적고 있습니다. 위의 상황이 모든 리졸버에서 일어난다고 볼 수는 없습니다.
- NXDOMAIN cut 아래에 이미 캐시된 데이터가 있으면 리졸버는 그 TTL이 끝날 때까지 계속 답할 수 있습니다(MAY, 2절).
- DNSSEC 검증을 하는 리졸버는 응답이 DNSSEC으로 검증된 경우에만 NXDOMAIN cut을 적용할 수 있습니다(MAY, 2절).
- 하위 이름은 있지만 자신에게는 레코드가 없는 이름(Empty Non-Terminal, 위 예에서 레코드를 추가한 뒤의
dev.example.com)에 대한 올바른 답은 NXDOMAIN이 아니라 NODATA입니다(3.1절). 5절은 이런 이름에 NXDOMAIN으로 답하는 권한 서버는 처음부터 잘못된 것이라고 말합니다.
서버 실패는 따로 캐시된다
NXDOMAIN과 NODATA는 서버가 “없다”는 쓸모 있는 답을 준 경우입니다. RFC 9520 2절은 이 둘이 해석 실패(resolution failure) 가 아니라고 분명히 하고, 해석 실패를 따로 정의합니다. 사용할 수 있는 어느 서버도 특정 <name, type, class>에 대해 쓸모 있는 답을 주지 않은 경우입니다. SERVFAIL, REFUSED, 시간 초과나 도달 불가, 위임 순환, 별칭(CNAME) 순환, DNSSEC 검증 실패, FORMERR 등이 예로 나옵니다.
RFC 2308 7절은 이런 응답에는 원하는 TTL을 담을 방법이 없다고 지적하고, 서버 실패와 응답 없는 서버의 캐싱을 선택 사항으로 두되 5분을 넘기지 말라고 정했습니다. 2023년의 RFC 9520은 이를 강화했습니다.
- 리졸버는 해석 실패를 캐시해야 합니다(MUST, 3.2절). 캐시된 실패와 일치하는 질문이 오면 항목이 만료될 때까지 관련된 외부 질의를 보내면 안 됩니다.
- 캐시 기간은 최소 1초, 최대 5분입니다(3.2절). 최소 기간은 운영자가 설정할 수 있어야 하고(SHOULD), 실패가 계속되면 지수 또는 선형 백오프로 기간을 늘려야 합니다(SHOULD). 예로 5초에서 시작해 늘리는 방식이 나오지만 이것은 예시입니다.
- 같은 서버 주소와 같은 전송 방식으로 같은 질의를 재시도하는 것은 두 번까지, 즉 총 세 번까지입니다(3.1절). 다른 서버나 다른 전송 방식으로 다시 묻는 것은 막지 않습니다(2절, 3.1절).
정리하면 “없는 이름”은 SOA로 정해지는 시간 동안, “서버가 답을 못 한 상태”는 리졸버가 정하는 1초~5분 동안 캐시됩니다. 권한 서버를 고친 뒤에도 잠시 SERVFAIL이 이어진다면 후자일 수 있고, 그 시간은 RFC상 5분을 넘지 않습니다.
오래된 데이터 제공(serve-stale)과의 관계
RFC 8767은 권한 서버에 닿지 못할 때 만료된 데이터를 예외적으로 쓸 수 있게 한 문서입니다. 4절은 권한 서버가 AA 비트를 켠 NoError 또는 NXDomain 응답을 주면 그것으로 데이터가 갱신된 것으로 봐야 한다(MUST)고 정합니다. 다른 RCODE는 갱신 실패로 보고 기존 상태를 유지해야 합니다(SHOULD). 그래서 이름을 지운 뒤 권한 서버가 AA 비트를 켠 NXDOMAIN을 돌려주면, serve-stale을 쓰는 리졸버도 그 응답을 갱신으로 받아들입니다. 6절은 예전에 있던 이름에 대한 NXDOMAIN이 실수일 수도 있지만, 운영자의 의도를 구별할 방법이 없어 오류로 다루지 않는다고 설명합니다.
바꾸기 전에 확인할 것
- 새 이름을 만들 영역의 SOA MINIMUM과 SOA 레코드 TTL을 봅니다. 둘 중 작은 값이 부정 응답이 캐시될 수 있는 최대 시간입니다.
- 이름을 공개하기 전에 미리 조회해 보는 일 자체가 그 시간만큼의 NXDOMAIN을 캐시에 남길 수 있습니다. 상위 이름이 없는 상태에서 조회했다면, NXDOMAIN cut을 따르는 리졸버에서는 그 아래 이름까지 영향을 받을 수 있습니다.
- 기존 이름에 타입만 추가하는 경우 영향을 받는 것은 그 타입의 NODATA뿐입니다.
- SERVFAIL이나 시간 초과는 네거티브 캐시가 아니라 해석 실패 캐시의 문제이고, RFC상 상한은 5분입니다.
참고한 RFC
- RFC 2308 — Negative Caching of DNS Queries (DNS NCACHE) (1998): 1절(NXDOMAIN, NODATA 용어), 2절(부정 응답의 형태), 3절(권한 서버의 SOA 포함 의무와 TTL), 4절(SOA MINIMUM 필드의 재정의,
$TTL), 5절(캐시 키, TTL 감소, SOA 없는 응답, 리졸버 상한), 6절(캐시에서 답할 때의 TTL), 7절(서버 실패, 응답 없는 서버), 10절(예시). - RFC 8020 — NXDOMAIN: There Really Is Nothing Underneath (2016): 1절(배경), 2절(NXDOMAIN cut 규칙과 예외), 3.1절(Empty Non-Terminal은 NODATA), 3.2절(RFC 2308 5절 개정), 5절, 6절, 부록 A.
- RFC 9520 — Negative Caching of DNS Resolution Failures (2023): 2절(해석 실패의 정의와 예), 3.1절(재시도 횟수), 3.2절(실패 캐시의 의무, 1초~5분, 백오프).
- RFC 8767 — Serving Stale Data to Improve DNS Resiliency (2020): 4절(TTL 정의 개정과 604,800초 권장 상한, NoError, NXDomain 응답을 갱신으로 보는 규칙), 6절.
이 글은 표준 문서의 규칙만 다룹니다. 실제 리졸버의 네거티브 캐시 상한, NXDOMAIN cut 지원 여부, 실패 캐시 기간은 제품과 버전, 설정에 따라 다르므로 사용하는 리졸버의 문서를 확인하세요. 링크와 내용은 2026년 10월 9일에 확인했습니다.