조회수: 121

406 Not Acceptable 해결

406 Not Acceptable는 서버가 Accept 헤더에 맞는 응답을 만들지 못한 것입니다. 415와 구분하고 4단계로 헤더를 고칩니다. 무료 즉시 진단으로 바로 확인.

내 도메인에 이 문제가 있는지 지금 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.

Problem

요청이 406 Not Acceptable로 돌아옵니다. URL은 맞고, 리소스는 존재하고, 브라우저에서는 잘 열립니다 — 그런데 API 클라이언트, curl 명령, 또는 서버 간 호출에서 406이 나옵니다. 서버가 당신이 Accept 헤더에서 요청한 형식에 맞는 이 리소스의 표현을 갖고 있지 않다고 말하는 겁니다.

Symptoms

  • 같은 URL이 브라우저에서는 200인데 curl이나 HTTP 라이브러리에서는 406입니다.
  • Accept 헤더를 바꾸거나 빼는 순간 요청이 됩니다.
  • Accept: application/json(또는 application/xml)을 추가하니 실패하기 시작했습니다.
  • 쓰기 메서드는 정상인데 GET이 406을 반환합니다 — 415와 정반대 패턴입니다.

What 406 Actually Means

RFC 9110(§15.5.7)은 406을 대상 리소스가 “선제적 협상 헤더 필드에 따라 사용자 에이전트가 받아들일 만한 현재 표현”을 갖고 있지 않고, 서버가 “기본 표현 제공을 원치 않는” 것으로 정의합니다. 두 가지가 동시에 참이어야 합니다: 당신의 Accept(또는 Accept-Charset·Accept-Language·Accept-Encoding)가 서버가 생성할 수 있는 모든 걸 배제하고, 그리고 서버가 기본값으로 물러서길 거부합니다. 둘 다 중요하고, 두 번째가 406이 예상보다 드문 이유입니다.

이건 415 Unsupported Media Type의 정확한 거울상입니다. 415는 서버가 요청 본문의 Content-Type소비하지 못한다는 것. 406은 서버가 당신의 Accept에 맞는 응답을 생성하지 못한다는 것. 하나는 당신이 보낸 것, 다른 하나는 당신이 돌려받고 싶은 것에 관한 겁니다. 둘 다 미디어 타입 얘기라 헷갈리지만, 요청의 정반대 끝에 있습니다 — 어느 쪽 끝인지 알면 어느 헤더를 고칠지 알게 됩니다.

여기서 사람들이 걸립니다: RFC 9110은 Accept 준수를 MUST가 아니라 SHOULD로 둡니다. 서버는 당신의 Accept 헤더를 무시하고 기본 형식을 반환해도 됩니다. 대부분 그렇게 합니다 — 그게 406이 흔치 않은 바로 그 이유입니다. 서버가 그냥 기본값을 주는 대신 굳이 406을 보낸다면, 누군가 엄격한 콘텐츠 협상을 설정했고 당신의 요청이 정말로 메뉴에 없는 걸 요구한 겁니다.

Top 3 Causes

  1. 엔드포인트가 안 만드는 형식을 요청하는 Accept 헤더 - 전형. 클라이언트가 HTML만 렌더링하는 라우트에 Accept: application/json을, 또는 JSON만 주는 API에 Accept: application/xml을 보냅니다. 서버가 콘텐츠 협상을 돌리고, 가진 표현 중 맞는 게 없어서 406을 반환합니다. 압도적으로 흔한 원인이고, 거의 항상 클라이언트가 그 경로에서 서버가 애초에 내보내도록 만들어지지 않은 형식을 요구한 겁니다.

  2. 엄격한 프레임워크 콘텐츠 협상 - 동작은 전적으로 프레임워크에 달렸습니다. Ruby on Rails는 respond_to 블록이 요청된 형식을 처리하지 않으면 — /report.pdf 같은 형식 확장자가 있는데 대응하는 format.pdf 절이 없는 경우 포함 — ActionController::UnknownFormat을 던지고 406으로 답합니다. Spring은 컨트롤러의 produces= 목록이 Accept 헤더를 만족 못 하면 HttpMediaTypeNotAcceptableException(406)을 반환합니다. Express의 res.format()은 매칭되는 분기가 없고 default도 정의 안 했으면 406을 보냅니다. 같은 상태 코드, 프레임워크별 방아쇠.

  3. 미들박스 또는 잘못된 Accept 헤더 - 콘텐츠 협상이 개입될 리 없는데도 406이 나온다면, 당신과 오리진 사이의 무언가나 헤더 자체를 의심하세요. 프록시·CDN·WAF가 전송 중에 Accept를 주입하거나 다시 쓸 수 있고, 보안 규칙이 차단 응답으로 406을 반환할 수 있습니다. 클라이언트 라이브러리가 깨진 헤더를 내보낼 수도 있습니다 — 잘못된 품질 값(application/json;q=), 서브타입 오타, 서버가 압축하는 걸 배제하는 과도하게 제한적인 Accept-Encoding. 당신에겐 멀쩡해 보이는 헤더가 서버에겐 헛소리인 겁니다.

Diagnose with DechoNet

  • HTTP 진단으로 엔드포인트가 깨끗하고 관대한 요청에 어떻게 응답하는지 보세요. 중립적 진단은 브라우저 같은 Accept를 보내고 보통 200으로 돌아옵니다 — 그리고 그 갈림이 진단의 전부입니다. 라우트가 관대한 요청엔 정상 응답하고 당신의 좁은 Accept엔 406을 준다면, 문제는 망가진 배포가 아니라 당신의 헤더 대 엔드포인트가 생성하는 것입니다. 깨끗한 요청에도 406을 반환한다면, 헤더를 다시 쓰거나 요청을 차단하는 프록시·CDN·WAF를 상류에서 보세요.

Resolution Checklist

  • 클라이언트가 실제로 보내는 Accept 헤더를 출력하세요. curl -v나 브라우저 네트워크 탭이 진짜 값을 보여줍니다. 열에 아홉은 라우트가 다른 걸 생성하는데 좁은 application/json이나 application/xml입니다.
  • Accept: */*(또는 Accept 없이) 요청을 시도하세요. 성공하면 불일치가 당신의 헤더임이 확인된 것이고, 해결책은 엔드포인트가 실제로 주는 형식을 요청하는 겁니다.
  • 미디어 타입을 API가 반환한다고 문서화한 것과 맞추세요. JSON 전용이면 Accept: application/json을 보내고, HTML이면 JSON 강요를 멈추세요. URL도 확인하세요 — .xml이나 .pdf 같은 형식 확장자가 그 형식을 렌더링 안 하는 라우트에서 406을 유발할 수 있습니다.
  • Accept뿐 아니라 다른 협상 헤더도 확인하세요. 과도하게 제한적인 Accept-Charset·Accept-Language·Accept-Encoding도 서버가 만족 못 하고 기본값을 안 주면 406을 낼 수 있습니다.
  • 변장한 415가 아닌지 확인하세요. 실패가 당신이 요청하는 응답이 아니라 보내는 본문에 관한 거라면 당신이 원하는 건 406이 아니라 415입니다 — 다른 헤더, 다른 해결.
  • 다시 돌려 엔드포인트가 2xx를 반환하는지 확인하세요.

When to Escalate

  • 어제까지 되던 요청이 클라이언트 변경 없이 오늘 406을 반환한다면, Accept 헤더를 다시 쓰거나 벗기기 시작한 프록시·CDN·WAF나 차단으로 406을 반환하는 보안 규칙을 의심하세요. 직접 요청엔 오리진이 건강하고 엣지를 통하면 406이 나온다는 HTTP 진단 결과는 곧장 미들박스를 가리킵니다.
  • API를 직접 운영하는데 정당한 클라이언트가 406을 맞고 있다면, 엄격한 협상을 재고하세요. RFC 9110은 406 대신 합리적 기본값 반환을 허용하고, 대부분 API엔 그게 더 친절한 동작입니다 — JSON 서비스에 XML을 요청한 클라이언트에겐 본문 없는 맨 “Not Acceptable”보다 명확한 JSON 오류가 대개 낫습니다.

관련 도구

관련 가이드

가이드 공유

[Ad] Guide Detail Inline
← 전체 가이드 보기