415 Unsupported Media Type 해결
415 Unsupported Media Type는 서버가 요청 본문의 Content-Type를 지원하지 않아 거부한 것입니다. 400·406·422와 구분하고 헤더를 고칩니다. 무료 즉시 진단으로 바로 확인.
내 도메인에 이 문제가 있는지 지금 확인
무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.
Problem
요청이 415 Unsupported Media Type로 돌아옵니다. 엔드포인트는 존재하고, 인증도 멀쩡하고, 보낸 본문도 맞아 보입니다. 그런데 서버가 페이로드를 읽기도 전에 거부합니다. 당신이 선언한 Content-Type가 이 라우트에서 받아들이는 타입이 아니기 때문입니다.
Symptoms
- 같은 URL에
GET은 200인데POST·PUT·PATCH가 415를 반환합니다. - 똑같은 JSON이 Postman에서는 되는데
curl -d나 직접 짠fetch에서는 실패합니다. - 요청 본문에 gzip 압축을 추가한 뒤부터 실패하기 시작했습니다.
- 프레임워크 로그가 “no acceptable representation” 또는 “Content-Type ‘text/plain’ not supported” 비슷한 말을 합니다.
What 415 Actually Means
RFC 9110(§15.5.16)은 415를 오리진 서버가 “콘텐츠가 대상 리소스에서 이 메서드가 지원하지 않는 형식이라 요청 처리를 거부하는” 것으로 정의합니다. 잘 읽으세요: 데이터가 틀렸다는 게 아니라 데이터의 형식 선언에 관한 겁니다. 서버는 당신의 Content-Type 헤더(때로는 Content-Encoding)를 보고, 여기서 그 미디어 타입을 처리할 수 없다고 판단하고, 멈췄습니다.
이건 요청 측 오류이고, 그게 415에 대해 가장 유용한 사실입니다. 406 Not Acceptable은 거울상입니다 — 서버가 당신의 Accept 헤더에 맞는 응답을 만들 수 없다는 뜻이죠. 400은 본문이 잘못된 형식이란 뜻입니다. 422는 본문이 잘 파싱됐지만 검증에 실패했다는 뜻입니다. 415는 그 어느 것도 아닙니다. 본문은 애초에 문제가 아니었습니다. 봉투에 붙은 라벨이 문제였습니다. 서버가 타입 협상 단계에서 당신을 거절했다는 걸 체득하는 순간, JSON 디버깅을 멈추고 헤더 디버깅을 시작하게 됩니다.
명세는 415를 내보내는 서버가 무엇을 받아들이는지 알려줘야(SHOULD) 한다고도 합니다. PATCH라면 Accept-Patch 헤더(RFC 5789)입니다. 어떤 API는 지원 타입을 에러 본문이나 Accept-Post 계열 헤더에 광고합니다. 예의 바른 415는 답을 손에 쥐여줍니다. 게으른 415는 그냥 “Unsupported Media Type”라고만 하고 추측하게 남겨둡니다 — 대개 당신이 이 글을 읽는 이유죠.
Top 3 Causes
-
누락되거나 잘못된 Content-Type 헤더 - 압도적 1위. 본문에 JSON 문자열을 보내면서
Content-Type: application/json을 설정하지 않으면 클라이언트가 다른 걸 기본값으로 씁니다 —curl -d는application/x-www-form-urlencoded를, 문자열 본문의 맨fetch는text/plain을 보냅니다. 서버의 바인딩 계층(Spring의consumes, ASP.NET의[Consumes], JSON에만 걸린 Expressjson()파서)이 처리하라고 지시받지 않은 타입을 보고, 데이터가 파싱되기도 전에 415로 답합니다. -
서버가 매칭하지 않는 서브타입/charset -
application/json; charset=utf-8을 보냈는데 엄격한 엔드포인트는 맨application/json만 등록했습니다. 또는 API가application/vnd.api+json같은 벤더 타입을 기대하는데 그냥application/json을 보냈습니다. 바이트는 동일한 JSON인데, 미디어 타입 문자열이 서버의 허용 목록과 안 맞아 문자열 비교만으로 거부됩니다. -
서버가 디코드 못 하는 Content-Encoding - 대역폭을 아끼려 요청 본문을 gzip하고
Content-Encoding: gzip을 설정했는데, 서버나 프레임워크가 압축된 요청 본문을 지원하지 않습니다(대부분 그렇습니다 — 압축은 보통 응답 전용 기능입니다). 페이로드를 선언된 미디어 타입으로 디코드할 수 없으니 415를 반환합니다. 클라이언트 압축을 추가하고 서버도 업로드 해제를 옵트인해야 한다는 걸 잊은 사람들이 여기 걸립니다.
Diagnose with DechoNet
- HTTP 진단으로 그 엔드포인트가 깨끗한 요청에 반환하는 정확한 상태 줄과 헤더를 보세요. 415는 특정
Content-Type를 가진 특정 쓰기에 묶여 있으므로, 중립적 진단은 보통 200이나 405로 돌아옵니다 — 그리고 그 갈림이 핵심입니다. 라우트가 평범한 요청에 정상 응답한다면, 415는 망가진 배포가 아니라 당신의 헤더 대 엔드포인트가 소비하는 것에 있습니다. 응답 헤더에서Accept-Patch나 광고된 미디어 타입을 눈여겨보세요 — 서버가 실제로 원하는 걸 알려주는 겁니다.
Resolution Checklist
- 당신이 보낸다고 생각하는 게 아니라 클라이언트가 실제로 보내는
Content-Type를 출력하세요.curl -v나 브라우저 네트워크 탭이 진짜 헤더를 보여줍니다. 열에 아홉은 누락됐거나,text/plain이거나, API가 JSON을 원하는데 form-encoded입니다. - 헤더를 엔드포인트가 문서화한 값 — 보통
application/json— 으로 명시하세요. API가 벤더 타입(application/vnd.api+json,application/merge-patch+json)을 쓰면 접미사까지 정확히 맞추세요. - charset를 확인하세요. 서버가 맨
application/json을 등록했다면; charset=utf-8을 빼는 게 해결책 전부일 수 있습니다. 원한다면 추가하세요. 허용 목록과 글자 하나까지 맞추세요. - 요청 본문을 압축했다면
Content-Encoding: gzip을 제거하세요(또는 서버가 업로드 해제를 지원하는지 확인). 응답 압축과 요청 압축은 별개 기능이고 대부분 서버는 전자만 합니다. - 변장한 406이 아닌지 확인하세요.
Accept헤더가 불일치이고 응답 본문은 멀쩡하다면 당신이 원하는 건 415가 아니라 406입니다 — 다른 헤더, 다른 해결. - 요청을 다시 돌려 엔드포인트가 415 대신 2xx를 반환하는지 확인하세요.
When to Escalate
- 어제까지 되던 요청이 클라이언트 변경 없이 오늘 415를 반환하기 시작했다면, 전송 중에
Content-Type헤더를 다시 쓰거나 벗기는 프록시·CDN·WAF를 의심하세요. 직접 요청엔 엔드포인트가 건강하고 엣지를 통하면 415가 나온다는 HTTP 진단 결과는 곧장 미들박스를 가리킵니다. - API를 직접 운영하는데 정당한 클라이언트가 지원하려던 타입에서 415를 맞고 있다면, 엔드포인트의 허용 미디어 타입을 넓히고 415 본문이 무엇을 소비하는지 나열하게 하세요 —
Accept-Patch도 허용 목록도 없는 맨 “Unsupported Media Type”는 지원 티켓을 예약하는 셈입니다.
관련 도구
관련 가이드
가이드 공유