DNS가 레코드를 찾지 못해도 응답의 의미는 같지 않습니다. NXDOMAIN은 이름 자체가 없다는 응답이고, NODATA는 이름은 질의되었지만 요청한 유형의 데이터가 없다는 응답입니다. RFC 2308은 두 응답의 캐시 키와 SOA 기반 만료를 다르게 정합니다. 응답 코드, QNAME·QTYPE, authority의 SOA와 남은 TTL을 함께 확인해야 합니다.

NXDOMAIN과 NODATA 응답의 캐시 키와 SOA 기반 만료를 설명하는 DNS 개념도

NXDOMAIN과 NODATA는 다른 질문에 답한다

NXDOMAIN은 이름 오류 RCODE로 이름이 존재하지 않는다고 알립니다. NODATA는 별도 RCODE가 아니라, 일반적으로 NOERROR인데 answer 영역에 요청한 유형의 관련 레코드가 없는 응답입니다. 같은 이름에 다른 유형은 있어도 A 레코드는 없을 수 있습니다. 따라서 “도메인이 없다”와 “이 이름에 지금 요청한 데이터 유형이 없다”를 구분합니다.

RFC 2308은 NXDOMAIN을 같은 이름과 클래스(QNAME, QCLASS)에 대해 캐시하고, NODATA는 이름·유형·클래스(QNAME, QTYPE, QCLASS)에 대해 캐시합니다. A 조회의 NODATA가 MX나 AAAA 조회까지 대신하지 않으므로, 문제를 재현할 때 이름과 질문 유형을 모두 기록하세요.

SOA가 부정 응답의 만료를 전달한다

answer 영역에 레코드가 없는 부정 응답은 보통 권한 zone의 SOA를 authority에 포함해 TTL을 전달합니다. RFC 2308에 따르면 이 SOA 레코드의 TTL은 SOA 자체 TTL과 SOA.MINIMUM 중 작은 값입니다. 캐시 시간은 감소하고 0이 되면 부정 응답을 다시 사용하면 안 됩니다. SOA.MINIMUM이 zone 모든 레코드의 TTL이라는 뜻은 아닙니다.

SOA가 없다면 RFC가 설명하는 정상 음성 캐시와 다른 응답일 수 있습니다. RFC 2308은 만료 정보가 없어 전달 루프가 생길 수 있으므로 SOA 없는 부정 응답은 캐시하지 않도록 권고합니다. RCODE 외에도 authority의 SOA zone, TTL, MINIMUM, 리졸버가 돌려준 잔여 TTL을 살핍니다.

새 레코드를 추가해도 이전 답이 남는 이유

권한 DNS에 레코드를 추가해도 재귀 리졸버가 이전 NXDOMAIN이나 NODATA를 보유하면 TTL이 끝날 때까지 예전 답을 돌려줄 수 있습니다. NXDOMAIN은 이름 단위로 기억될 수 있고, NODATA는 질문 유형까지 키에 포함합니다. 단말의 stub 리졸버, 재귀 서비스, 애플리케이션도 각기 다른 캐시와 만료 시점을 가질 수 있습니다.

읽기 중심으로 권한 응답, SOA와 TTL, 재귀 응답의 RCODE·잔여 TTL, 클라이언트가 보낸 정확한 이름·유형을 대조하세요. 오래된 값만 보고 zone을 재변경하거나 TTL을 낮추지 말고 어느 계층이 응답을 보유하는지 먼저 구분합니다. 이 글은 진단 관점이며 운영 캐시 삭제나 설정 변경 지시가 아닙니다.

SERVFAIL과 시간 초과는 이름 부재가 아니다

RFC 9520은 NXDOMAIN과 NOERROR/NODATA를 해석 실패와 구분합니다. SERVFAIL, REFUSED, 시간 초과, 서버 도달 불가 등은 사용 가능한 서버에서 유용한 답을 얻지 못한 실패일 수 있습니다. 리졸버는 반복 상위 질의를 줄이도록 별도의 resolution-failure cache를 구현해야 하며, RFC 9520은 최소 1초, 최대 5분을 정하고 지속 실패에는 backoff를 권고합니다.

따라서 SERVFAIL을 “이름이 없다”는 캐시로 해석하지 마세요. 권한 서버 문제, DNSSEC 검증, 네트워크 도달성 등 원인이 다를 수 있습니다. 응답 코드, 재시도, 응답한 상위 서버를 이름·유형과 함께 기록하면 두 종류를 나눠 볼 수 있습니다.

BIND 9.20.2는 해당 버전 정책으로 읽는다

ISC의 BIND 9.20.2 문서에서 min-ncache-ttl 기본값은 0초이며 90초보다 큰 값은 90초로 잘립니다. max-ncache-ttl 기본값은 10,800초(3시간), 상한은 7일입니다. 이는 SOA가 제시한 TTL과 리졸버의 로컬 보관 정책이 함께 작동할 수 있음을 보여 줍니다. 다른 버전이나 DNS 구현에 이 수치를 일반화하지 마세요.

같은 버전의 servfail-ttl은 DNSSEC 검증 실패 또는 일반 서버 실패 캐시를 다룹니다. 기본값은 1초, 상한은 30초이며 0이면 비활성화됩니다. RFC 9520의 실패 캐시 상한 5분과는 별개의 제품별 범위입니다. 문서의 기본값만으로 실제 서버 설정을 추정할 수도 없습니다.

전문가 관점: 표준 만료와 구현 정책을 나눈다

RFC 2308의 SOA TTL은 부정 응답이 언제 만료되는지를, RFC 9520은 답을 얻지 못한 실패의 반복 조회를 줄이는 별도 캐시를 다룹니다. BIND 9.20.2 수치는 특정 제품 버전의 보관 한도입니다. 세 층위를 구분하면 SOA 값, 리졸버 정책, 서버 장애를 하나의 “DNS TTL 문제”로 뭉뚱그리지 않고 오래된 응답이 생긴 위치를 좁힐 수 있습니다.

출처

가상 DNS 응답을 직접 판독하기

다음은 CNAME과 위임이 없는 example. 영역을 가정해 사이트가 만든 축약 예시입니다. 실제 dig 실행이나 운영 네트워크 실측 결과가 아닙니다. 이름과 주소는 설명용이며, 세 응답의 status, ANSWER와 SOA를 비교해 보세요.

A. QUESTION: missing.example. IN A
   status: NXDOMAIN; ANSWER: 0
   AUTHORITY: example. 300 IN SOA ns.example. hostmaster.example.
              2026093001 3600 600 86400 300

B. QUESTION: www.example. IN AAAA
   status: NOERROR; ANSWER: 0
   AUTHORITY: example. 300 IN SOA ns.example. hostmaster.example.
              2026093001 3600 600 86400 300

C. QUESTION: www.example. IN A
   status: NOERROR; ANSWER: 1
   ANSWER: www.example. 600 IN A 192.0.2.10

세 응답의 차이와 캐시 키

A의 NXDOMAIN은 질의 이름이 존재하지 않는다는 응답입니다. 기본 음수 캐시 키는 이름과 클래스입니다. B는 이름은 존재하지만 요청한 AAAA 자료가 없다는 NODATA 예시입니다. NODATA는 별도의 RCODE가 아니라 NOERROR와 답의 내용으로 판독하는 상태이며, 캐시 키에는 요청한 유형도 포함됩니다. 따라서 B의 AAAA 부재만으로 A 레코드도 없다고 결론내릴 수 없습니다. C는 같은 www.example.에 A 레코드가 있는 별도 가상 응답입니다.

300초는 어디서 나왔나

권한 서버 영역의 원래 SOA TTL을 900초, SOA MINIMUM을 300초로 가정했습니다. RFC 2308의 기본 음수 캐싱 규칙에서 min(900, 300) = 300초이며, 음수 응답 A와 B에 실리는 SOA TTL은 이미 300초로 줄어 있습니다. SOA 안의 refresh, retry, expire 값을 이 TTL로 오인하지 마세요. 수신 후 120초가 지난 가상 캐시 응답의 남은 SOA TTL은 180초입니다. 실제 리졸버의 제한·정책과 추가 규격에 따라 동작이 달라질 수 있으므로 이 숫자는 운영 보장 시간이 아닙니다.

변경 후 확인 순서

  • 권한 서버 응답과 사용하는 재귀 리졸버 응답을 구분합니다.
  • 질의 이름, 유형과 클래스를 같게 유지해 비교합니다.
  • status만 보지 말고 답과 authority의 SOA도 확인합니다. NOERROR의 빈 답만으로 위임이나 CNAME을 무시한 채 NODATA라 단정하지 않습니다.
  • TTL이 줄어드는지 살펴보고, 레코드를 새로 만들었다고 이미 저장된 음수 응답이 즉시 사라진다고 가정하지 않습니다.

출처: RFC 2308 §2.2, §3, §5, §6: NODATA 판독, SOA TTL 계산, NXDOMAIN/NODATA의 캐시 키와 남은 TTL. 응답 필드와 수치는 자체 구성한 가상 예시이며 외부의 실제 로그를 복사하지 않았습니다.