text/html;level=3 품질 값 0.7 → 0.3).ranges-specifier의 “=” 뒤에 OWS 추가, 원문 규칙은 주석).역주: 용어는 glossary.md, 번역 방식은 번역 가이드를 따른다.
HTTP(Hypertext Transfer Protocol)는 분산·협업 하이퍼텍스트 정보 시스템을 위한 무상태(stateless, 요청 사이의 상태 정보를 유지하지 않는) 애플리케이션 수준 프로토콜이다. 이 문서는 HTTP의 전체 아키텍처를 설명하고, 공통 용어를 정하며, 모든 HTTP 버전이 공유하는 프로토콜의 측면을 정의한다. 이 정의에는 핵심 프로토콜 요소, 확장 메커니즘, “http”와 “https” URI(Uniform Resource Identifier) 스킴이 포함된다.
이 문서는 RFC 3864를 갱신하며, RFC 2818, 7231, 7232, 7233, 7235, 7538, 7615, 7694와 RFC 7230의 일부를 대체한다.
이 문서는 인터넷 표준화 트랙(Standards Track) 문서다.
이 문서는 IETF(Internet Engineering Task Force)의 결과물이며 IETF 커뮤니티의 합의를 나타낸다. 이 문서는 공개 검토를 거쳤고 IESG(Internet Engineering Steering Group)의 발행 승인을 받았다. 인터넷 표준에 대한 자세한 정보는 RFC 7841의 2절에 있다.
이 문서의 현재 상태, 정오표, 의견을 보내는 방법은 https://www.rfc-editor.org/info/rfc9110 에서 확인할 수 있다.
Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust’s Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.
This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.
Copyright (c) 2022 IETF Trust 및 문서 저자로 명시된 사람들. 모든 권리 보유.
이 문서에는 BCP 78과, 이 문서의 발행일에 유효한 IETF 문서 관련 IETF Trust 법적 조항(https://trustee.ietf.org/license-info)이 적용된다. 이 문서들은 이 문서에 대한 독자의 권리와 제한을 설명하므로 주의 깊게 검토하기 바란다. 이 문서에서 추출한 코드 구성 요소(Code Components)에는 Trust 법적 조항 4.e절에 설명된 Revised BSD License 문구를 포함해야 하며, 이 코드 구성 요소는 Revised BSD License에 설명된 대로 보증 없이 제공된다.
이 문서에는 2008년 11월 10일 이전에 발행되었거나 공개된 IETF 문서 또는 IETF 기고(Contribution)의 내용이 포함되어 있을 수 있다. 그 내용 일부의 저작권을 가진 사람은 IETF 표준화 절차 밖에서 그 내용을 수정하도록 허락할 권리를 IETF Trust에 부여하지 않았을 수 있다. 그런 내용의 저작권자에게서 적절한 허락을 받지 않는 한, 이 문서는 IETF 표준화 절차 밖에서 수정될 수 없고 IETF 표준화 절차 밖에서 이 문서의 2차 저작물을 만들 수 없다. 단, RFC로 발행하기 위해 서식을 맞추는 것과 영어 이외의 언어로 번역하는 것은 예외다.
역주: 원문이 따르는 IETF Trust 법적 조항에 따라 원문의 Copyright Notice를 그대로 두고 그 아래에 한국어 번역을 덧붙였으며, 법적 효력은 원문에 있다. 마지막 문단에 따라 이 문서의 번역은 허용된다.
HTTP(Hypertext Transfer Protocol)는 무상태(stateless) 애플리케이션 수준의 요청/응답 프로토콜 계열로, 범용 인터페이스(generic interface), 확장 가능한 의미론(semantics), 자기 서술적(self-descriptive) 메시지를 공유하여 네트워크 기반 하이퍼텍스트 정보 시스템과 유연하게 상호 작용할 수 있게 한다.
HTTP는 제공되는 리소스(resource)의 유형과 무관한 일관된 인터페이스(uniform interface)를 클라이언트에 제시함으로써 서비스가 어떻게 구현되었는지에 대한 세부 사항을 감춘다. 마찬가지로 서버는 각 클라이언트의 목적을 알 필요가 없다. 요청은 특정 유형의 클라이언트나 미리 정해진 애플리케이션 단계의 순서와 연관 짓지 않고 따로 떼어서 다룰 수 있다. 이 덕분에 범용 구현을 다양한 맥락에서 효과적으로 쓸 수 있고, 상호 작용의 복잡도가 줄며, 시간이 지나면서 각자 독립적으로 발전할 수 있다.
HTTP는 중개(intermediation) 프로토콜로도 쓰이도록 설계되었다. 이때 프록시(proxy)와 게이트웨이(gateway)는 HTTP가 아닌 정보 시스템을 더 범용적인 인터페이스로 변환할 수 있다.
이런 유연성의 결과 중 하나는, 프로토콜을 인터페이스 뒤에서 일어나는 일의 관점으로 정의할 수 없다는 것이다. 그 대신 우리는 통신의 구문, 받은 통신의 의도, 수신자의 기대 동작을 정의하는 데 그친다. 통신을 따로 떼어서 본다면, 성공한 동작은 서버가 제공하는 관찰 가능한 인터페이스에 그에 상응하는 변화로 반영되어야 마땅하다. 그러나 여러 클라이언트가 동시에, 어쩌면 서로 엇갈린 목적으로 동작할 수 있으므로, 그런 변화가 단일 응답의 범위를 넘어서까지 관찰 가능해야 한다고 요구할 수는 없다.
HTTP는 1990년에 등장한 이래 월드 와이드 웹의 주된 정보 전송 프로토콜이었다. 처음에는 지연 시간이 짧은 요청을 위한 단순한 메커니즘으로 시작했으며, 주어진 경로 이름으로 식별되는, 하이퍼텍스트 문서로 추정되는 대상의 전송을 요청하는 메서드(GET) 하나만 있었다. 웹이 성장하면서 HTTP는 요청과 응답을 메시지로 감싸고, MIME과 비슷한 미디어 유형(media type)을 사용해 임의의 데이터 형식을 전송하며, 중개자(intermediary)를 거쳐 요청을 라우팅하도록 확장되었다. 이 프로토콜들은 결국 HTTP/0.9와 HTTP/1.0으로 정의되었다([HTTP/1.0] 참고).
HTTP/1.1은 기존의 텍스트 기반 메시지 구문과의 호환성을 유지하면서 프로토콜의 기능을 다듬고, 인터넷 전반에서 상호 운용성, 확장성, 강건성을 높이도록 설계되었다. 여기에는 고정 길이와 동적(청크, chunked) 콘텐츠 모두를 위한 길이 기반 데이터 구분자, 콘텐츠 협상(content negotiation)을 위한 일관된 프레임워크, 조건부 요청(conditional request)을 위한 불투명한(opaque) 검증자(validator), 캐시 일관성을 높이기 위한 캐시 제어, 부분 갱신을 위한 범위 요청(range request), 기본 지속 연결(persistent connection)이 포함되었다. HTTP/1.1은 1995년에 도입되어 1997년에 표준화 트랙으로 발행되었고([RFC2068]), 1999년에 개정되었으며([RFC2616]), 2014년에 다시 개정되었다([RFC7230]부터 [RFC7235]까지).
HTTP/2([HTTP/2])는 기존 TLS와 TCP 프로토콜 위에 다중화된(multiplexed) 세션 계층을 도입하여, 효율적인 필드 압축과 서버 푸시로 동시에 여러 HTTP 메시지를 교환할 수 있게 했다. HTTP/3([HTTP/3])는 TCP 대신 UDP 위에서 동작하는 안전한 다중화 전송 프로토콜인 QUIC을 사용하여 동시 메시지 사이의 독립성을 더 높였다.
세 가지 주요 HTTP 버전은 모두 이 문서가 정의하는 의미론에 의존한다. 각 버전은 사용 맥락에 따라 고유한 장점과 한계가 있으므로 서로를 대체하지 않았다. 구현은 각자의 맥락에 가장 적합한 전송 방식과 메시지 구문을 선택할 것으로 예상된다.
이번 HTTP 개정판은 각 주요 프로토콜 버전이 같은 핵심 의미론을 참조하면서 독립적으로 발전할 수 있도록, 의미론(이 문서)과 캐싱([CACHING])의 정의를 현재의 HTTP/1.1 메시지 구문([HTTP/1.1])과 분리한다.
HTTP는 표현(representation, 3.2절)을 조작하거나 전송하는 메시지를 보냄으로써, 리소스(3.1절)의 유형, 성격, 구현과 상관없이 리소스와 상호 작용할 수 있는 일관된 인터페이스를 제공한다.
각 메시지는 요청이거나 응답이다. 클라이언트는 자신의 의도를 전달하는 요청 메시지를 만들어, 식별된 원본 서버(origin server)를 향해 라우팅한다. 서버는 요청을 기다리다가 받은 각 메시지를 파싱하고, 식별된 대상 리소스와 관련지어 메시지의 의미를 해석한 뒤, 그 요청에 하나 이상의 응답 메시지로 응답한다. 클라이언트는 받은 응답을 살펴 자신의 의도가 수행되었는지 확인하고, 받은 상태 코드와 콘텐츠를 바탕으로 다음에 할 일을 정한다.
HTTP 의미론에는 각 요청 메서드가 정의하는 의도(9장), 요청 헤더 필드에 기술될 수 있는 그 의미론의 확장, 응답을 설명하는 상태 코드(15장), 그리고 응답 필드에 담길 수 있는 그 밖의 제어 데이터와 리소스 메타데이터가 포함된다.
의미론에는 또한 콘텐츠를 수신자가 어떻게 해석하도록 의도되었는지 설명하는 표현 메타데이터, 콘텐츠 선택에 영향을 줄 수 있는 요청 헤더 필드, 그리고 통틀어 “콘텐츠 협상(content negotiation)”(12장)이라고 부르는 다양한 선택 알고리즘이 포함된다.
| 제목 | 참조 | 참고 |
|---|---|---|
| HTTP Over TLS | [RFC2818] | B.1 |
| HTTP/1.1 Message Syntax and Routing [*] | [RFC7230] | B.2 |
| HTTP/1.1 Semantics and Content | [RFC7231] | B.3 |
| HTTP/1.1 Conditional Requests | [RFC7232] | B.4 |
| HTTP/1.1 Range Requests | [RFC7233] | B.5 |
| HTTP/1.1 Authentication | [RFC7235] | B.6 |
| HTTP Status Code 308 (Permanent Redirect) | [RFC7538] | B.7 |
| HTTP Authentication-Info and Proxy-Authentication-Info Response Header Fields | [RFC7615] | B.8 |
| HTTP Client-Initiated Content-Encoding | [RFC7694] | B.9 |
표 1
[*] 이 문서는 RFC 7230 가운데 HTTP/1.1 메시지 구문 및 연결 관리와 무관한 부분만 대체한다. RFC 7230의 나머지 부분은 “HTTP/1.1”[HTTP/1.1]이 대체한다.
역주: 대체되는 명세의 제목은 문서 이름이므로 원문 그대로 두었다. 1.1절의 “ought to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
이 명세는 [RFC5234]의 ABNF(Augmented Backus-Naur Form) 표기법을 사용하며, [RFC7405]에 정의된 문자열의 대소문자 구분 표기를 더해 확장한다.
또한 5.6.1절에 정의된 목록 확장을 사용한다. 이 확장은 “#” 연산자를 써서 쉼표로 구분된 목록을 간결하게 정의할 수 있게 한다(“*” 연산자가 반복을 나타내는 것과 비슷하다). 부록 A에는 모든 목록 연산자를 표준 ABNF 표기로 펼친 전체 문법이 모여 있다.
관례상 “obs-“로 시작하는 ABNF 규칙 이름은 역사적인 이유로 남아 있는 폐기된(obsolete) 문법 규칙을 나타낸다.
다음 핵심 규칙은 [RFC5234]의 부록 B.1에 정의된 대로 참조로 포함한다: ALPHA(영문자), CR(캐리지 리턴), CRLF(CR LF), CTL(제어 문자), DIGIT(십진수 0-9), DQUOTE(큰따옴표), HEXDIG(16진수 0-9/A-F/a-f), HTAB(수평 탭), LF(라인 피드), OCTET(임의의 8비트 데이터 시퀀스), SP(공백), VCHAR(눈에 보이는 모든 US-ASCII 문자).
5.6절은 필드 값을 위한 몇 가지 범용 구문 요소를 정의한다.
이 명세는 “문자(character)”, “문자 인코딩 스킴(character encoding scheme)”, “문자 집합(charset)”, “프로토콜 요소(protocol element)”라는 용어를 [RFC6365]에 정의된 뜻으로 사용한다.
이 문서의 키워드 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, “OPTIONAL”은 여기에 보인 것처럼 모두 대문자로 쓰였을 때, 그리고 그때에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석한다.
이 명세는 HTTP 통신 참여자의 역할에 따라 적합성 기준을 정한다. 따라서 요구 사항이 어떤 동작을 제약하느냐에 따라 송신자, 수신자, 클라이언트, 서버, 사용자 에이전트, 중개자, 원본 서버, 프록시, 게이트웨이, 캐시에 요구 사항이 부과된다. 요구 사항이 단일 통신의 범위를 넘어서 적용될 때는 구현, 리소스 소유자, 프로토콜 요소 등록에도 추가 요구 사항이 부과된다.
요구 사항이 받은 요소를 다운스트림(downstream)으로 전달하는 구현이 아니라 프로토콜 요소를 만들어 내는 구현에만 적용될 때는 “보내다(send)” 대신 “생성하다(generate)”라는 동사를 쓴다.
구현은 HTTP에서 자신이 맡는 역할에 관련된 모든 요구 사항을 따를 때 적합(conformant)하다고 본다.
송신자는 해당 ABNF 규칙이 정의한 문법에 맞지 않는 프로토콜 요소를 생성해서는 안 된다(MUST NOT). 주어진 메시지 안에서 송신자는 다른 역할(즉, 그 메시지에 대해 송신자가 갖지 않은 역할)의 참여자만 생성할 수 있는 프로토콜 요소나 구문 대안을 생성해서는 안 된다(MUST NOT).
HTTP 적합성에는 사용 중인 프로토콜 버전의 특정 메시지 구문에 대한 적합성과, 보낸 프로토콜 요소의 의미론에 대한 적합성이 모두 포함된다. 예를 들어 HTTP/1.1을 따른다고 주장하면서 HTTP/1.1 수신자에게 요구되는 기능을 인식하지 못하는 클라이언트는, 그 주장에 맞춰 응답을 조정하는 서버와 상호 운용하지 못할 것이다. 콘텐츠 협상이나 사용자가 고른 확장처럼 사용자의 선택을 반영하는 기능은 프로토콜 스트림을 넘어서 애플리케이션 동작에 영향을 줄 수 있다. 사용자의 선택을 부정확하게 반영하는 프로토콜 요소를 보내면 사용자가 혼란스러워하고 선택이 방해받는다.
어떤 구현이 의미론적 적합성을 지키지 못하면, 그 구현이 보낸 메시지의 수신자들은 결국 그에 맞춰 동작을 조정하는 우회책(workaround)을 만들게 된다. 수신자는 그 우회책을 문제가 있는 구현에만 한정한다면, 이 프로토콜에 대한 적합성을 유지하면서 그런 우회책을 사용할 수 있다(MAY). 예를 들어 서버는 흔히 User-Agent 필드 값의 일부를 살펴보고, 사용자 에이전트는 흔히 Server 필드 값을 살펴보아, 알려진 버그나 잘못 정해진 기본값에 맞춰 자신의 동작을 조정한다.
수신자는 받은 프로토콜 요소가 ABNF 문법에 맞고 적당한 크기의 버퍼에 들어가리라는 기대를 최소한으로만 하면서, 받은 프로토콜 요소를 방어적으로 파싱해야 한다(SHOULD).
HTTP는 여러 프로토콜 요소에 구체적인 길이 제한을 두지 않는다. 적절한 길이는 배포 환경과 구현의 목적에 따라 크게 다르기 때문이다. 따라서 송신자와 수신자 사이의 상호 운용성은 각 프로토콜 요소의 적당한 길이에 대한 공통된 기대에 달려 있다. 더구나 일부 프로토콜 요소에 대해 흔히 적당하다고 여기는 길이는 HTTP를 사용해 온 지난 30년 동안 바뀌어 왔고, 앞으로도 계속 바뀔 것으로 예상된다.
최소한, 수신자는 다른 메시지에서 같은 프로토콜 요소에 대해 자신이 생성하는 값만큼 긴 프로토콜 요소 길이를 반드시 파싱하고 처리할 수 있어야 한다(MUST). 예를 들어 자신의 리소스에 대해 매우 긴 URI 참조(URI reference)를 게시하는 원본 서버는, 같은 참조를 대상 URI로 받았을 때 그것을 파싱하고 처리할 수 있어야 한다.
받은 프로토콜 요소 중 상당수는 그 요소를 식별해 다운스트림으로 전달하는 데 필요한 만큼만 파싱된다. 예를 들어 중개자는 받은 필드를 필드 이름과 필드 값으로 나눠 파싱한 다음, 필드 값 내부는 더 파싱하지 않고 그 필드를 전달할 수 있다.
역주: 2.3절 셋째 문단의 두 번째 문장 “needs to be able to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
수신자는 받은 프로토콜 요소를 이 명세(이 명세의 확장 포함)가 그 요소에 정의한 의미론에 따라 반드시 해석해야 한다(MUST). 단, 수신자가 (경험이나 설정을 통해) 송신자가 그 의미론이 뜻하는 바를 잘못 구현했다고 판단한 경우는 예외다. 예를 들어 원본 서버는 User-Agent 헤더 필드를 살펴본 결과 특정 콘텐츠 코딩(content coding)을 받으면 실패하는 것으로 알려진 특정 구현 버전임이 드러나면, 받은 Accept-Encoding 헤더 필드의 내용을 무시할 수 있다.
달리 언급하지 않는 한, 수신자는 유효하지 않은 구문에서 쓸 수 있는 프로토콜 요소를 복구하려고 시도할 수 있다(MAY). HTTP는 보안에 직접 영향을 주는 경우를 제외하고는 구체적인 오류 처리 메커니즘을 정의하지 않는다. 프로토콜을 사용하는 애플리케이션마다 서로 다른 오류 처리 전략이 필요하기 때문이다. 예를 들어 웹 브라우저는 Location 헤더 필드가 ABNF에 맞게 파싱되지 않는 응답에서도 사용자 모르게 복구하고 싶을 수 있지만, 시스템 제어 클라이언트는 어떤 형태의 오류 복구든 위험하다고 여길 수 있다.
9.2.2절에 설명된 것처럼, 일부 요청은 기반이 되는 연결이 실패했을 때 클라이언트가 자동으로 재시도할 수 있다.
HTTP의 버전 번호는 “.”(마침표 또는 소수점)으로 구분된 두 개의 십진 숫자로 이루어진다. 첫째 숫자(주 버전, major version)는 메시지 구문을 나타내고, 둘째 숫자(부 버전, minor version)는 그 주 버전 안에서 송신자가 지원하는(이후 통신에서 이해할 수 있는) 가장 높은 부 버전을 나타낸다.
HTTP의 핵심 의미론은 프로토콜 버전이 바뀌어도 달라지지 않지만, 그것을 “와이어상(on the wire)”에서 표현하는 방식은 달라질 수 있다. 그래서 와이어 포맷(wire format)에 호환되지 않는 변경이 생기면 HTTP 버전 번호가 바뀐다. 또한 HTTP는 정의된 확장 지점(16장)을 사용해, 버전을 바꾸지 않고도 하위 호환되는 변경을 점진적으로 프로토콜에 더할 수 있게 한다.
프로토콜 버전은 전체적으로, 그 버전에 해당하는 명세(들)에 제시된 요구 사항 집합에 대한 송신자의 적합성을 나타낸다. 예를 들어 “HTTP/1.1” 버전은 이 문서, “HTTP Caching”[CACHING], “HTTP/1.1”[HTTP/1.1]을 합친 명세로 정의된다.
HTTP의 주 버전 번호는 호환되지 않는 메시지 구문이 도입될 때 올라간다. 부 버전 번호는 프로토콜 변경이 메시지 의미론에 무언가를 더하거나 송신자의 추가 능력을 암시하는 효과가 있을 때 올라간다.
부 버전은 송신자가 프로토콜의 하위 호환 부분 집합만 사용하고 있을 때에도 송신자의 통신 능력을 알린다. 이를 통해 수신자는 (서버라면) 응답에서, (클라이언트라면) 이후 요청에서 더 발전된 기능을 쓸 수 있음을 알게 된다.
HTTP의 어떤 주 버전이 부 버전을 정의하지 않으면, 부 버전은 “0”으로 간주한다. 부 버전 식별자가 필요한 요소 안에서 그 프로토콜을 가리킬 때 “0”을 사용한다.
HTTP는 월드 와이드 웹(WWW) 아키텍처를 위해 만들어졌고, 전 세계적인 하이퍼텍스트 시스템의 확장성 요구를 뒷받침하도록 시간이 지나면서 발전해 왔다. 그 아키텍처의 상당 부분이 HTTP를 정의하는 데 쓰이는 용어에 반영되어 있다.
HTTP 요청의 대상을 “리소스(resource)”라고 한다. HTTP는 리소스의 성격을 제한하지 않으며, 리소스와 상호 작용하는 데 쓸 수 있는 인터페이스를 정의할 뿐이다. 대부분의 리소스는 4장에 설명된 대로 URI(Uniform Resource Identifier)로 식별된다.
HTTP의 설계 목표 중 하나는 리소스 식별을 요청 의미론과 분리하는 것이다. 요청 의미론을 요청 메서드(9장)와 요청을 수정하는 몇몇 헤더 필드에 부여함으로써 이 분리가 가능해진다. 리소스는 요청 메서드의 의미론과 어긋나는 방식으로 요청을 처리할 수 없다. 예를 들어 리소스의 URI가 안전하지 않은 의미를 암시하더라도, 클라이언트는 안전한 메서드로 보낸 요청을 처리할 때 리소스가 안전하지 않은 동작을 피하리라고 기대할 수 있다(9.2.1절 참고).
HTTP는 대상 리소스(7.1절)와 리소스 사이의 관계를 나타내기 위해 URI 표준[URI]에 의존한다.
“표현(representation)”은 주어진 리소스의 과거, 현재 또는 원하는 상태를 반영하도록 의도된 정보로, 프로토콜을 통해 쉽게 전달할 수 있는 형식을 갖는다. 표현은 표현 메타데이터의 집합과, 길이에 제한이 없을 수도 있는 표현 데이터의 스트림으로 이루어진다(8장).
HTTP는 리소스 자체를 전송하는 대신 전송 가능한 리소스 상태의 표현을 기준으로 통신을 정의함으로써, 일관된 인터페이스 뒤에서 “정보 은닉(information hiding)”을 가능하게 한다. 이 덕분에 URI로 식별되는 리소스는 “라구나 비치의 현재 날씨”처럼 시간에 따라 달라지는 함수를 포함해 무엇이든 될 수 있으며, 메시지가 생성되는 시점에 그 리소스를 나타내는 정보를 제공할 수 있다[REST].
일관된 인터페이스는 일종의 창과 같다. 그 창을 통해서는 반대편에 있는 독립된 행위자와 메시지를 주고받는 방법으로만 대상을 관찰하고 대상에 작용할 수 있다. 우리의 통신에서 그 대상의 현재 상태나 원하는 상태를 표현하는(“대신하는”) 공유된 추상화가 필요하다. 표현이 하이퍼텍스트일 때는 리소스 상태의 표현과, 수신자의 이후 상호 작용을 안내하는 처리 지침을 함께 제공할 수 있다.
대상 리소스는 각각 리소스의 현재 상태를 반영하도록 의도된 여러 표현을 제공받거나 생성할 수 있다. 보통 콘텐츠 협상(12장)에 기반한 알고리즘을 사용해, 그 표현들 가운데 주어진 요청에 가장 적합한 하나를 고른다. 이 “선택된 표현(selected representation)”은 조건부 요청(13장)을 평가하고 GET(9.3.1절)에 대한 200 (OK), 206 (Partial Content), 304 (Not Modified) 응답의 콘텐츠를 구성하는 데 필요한 데이터와 메타데이터를 제공한다.
HTTP는 신뢰할 수 있는 전송 계층 또는 세션 계층의 “연결(connection)” 위에서 동작하는 클라이언트/서버 프로토콜이다.
HTTP “클라이언트(client)”는 하나 이상의 HTTP 요청을 보내기 위해 서버와 연결을 맺는 프로그램이다. HTTP “서버(server)”는 HTTP 응답을 보내 HTTP 요청을 처리하기 위해 연결을 받아들이는 프로그램이다.
클라이언트와 서버라는 용어는 특정 연결에서 이 프로그램들이 맡는 역할만을 가리킨다. 같은 프로그램이 어떤 연결에서는 클라이언트로, 다른 연결에서는 서버로 동작할 수 있다.
HTTP는 무상태 프로토콜로 정의된다. 즉, 각 요청 메시지의 의미는 따로 떼어서 이해할 수 있으며, 연결과 그 위의 메시지 사이의 관계는 메시지 해석에 아무 영향을 주지 않는다. 예를 들어 CONNECT 요청(9.3.6절)이나 Upgrade 헤더 필드가 있는 요청(7.8절)은 연결의 첫 메시지뿐 아니라 언제든지 나타날 수 있다. 많은 구현이 프록시를 거친 연결을 재사용하거나 여러 서버에 요청을 동적으로 부하 분산(load balancing)하기 위해 HTTP의 무상태 설계에 의존한다.
그 결과, 서버는 연결이 보안 처리되어 있고 그 사용자 에이전트 전용인 경우가 아니라면, 같은 연결로 들어온 두 요청이 같은 사용자 에이전트에서 왔다고 가정해서는 안 된다(MUST NOT). 일부 비표준 HTTP 확장(예: [RFC4559])은 이 요구 사항을 어겨 보안 문제와 상호 운용성 문제를 일으킨 것으로 알려져 있다.
HTTP는 연결을 통해 “메시지(message)”를 교환하는 무상태 요청/응답 프로토콜이다. “송신자(sender)”와 “수신자(recipient)”라는 용어는 각각 주어진 메시지를 보내거나 받는 모든 구현을 가리킨다.
클라이언트는 메서드(9장)와 요청 대상(7.1절)을 담은 “요청(request)” 메시지의 형태로 서버에 요청을 보낸다. 요청에는 요청 수정자, 클라이언트 정보, 표현 메타데이터를 위한 헤더 필드(6.3절), 메서드에 따라 처리되도록 의도된 콘텐츠(6.4절), 콘텐츠를 보내는 동안 수집한 정보를 전달하는 트레일러 필드(6.5절)도 들어 있을 수 있다.
서버는 하나 이상의 “응답(response)” 메시지를 보내 클라이언트의 요청에 응답하며, 각 응답에는 상태 코드(15장)가 들어 있다. 응답에는 서버 정보, 리소스 메타데이터, 표현 메타데이터를 위한 헤더 필드, 상태 코드에 따라 해석되도록 의도된 콘텐츠, 콘텐츠를 보내는 동안 수집한 정보를 전달하는 트레일러 필드도 들어 있을 수 있다.
“사용자 에이전트(user agent)”라는 용어는 요청을 시작하는 다양한 클라이언트 프로그램을 모두 가리킨다.
가장 익숙한 형태의 사용자 에이전트는 범용 웹 브라우저지만, 이는 구현 가운데 일부에 불과하다. 그 밖의 흔한 사용자 에이전트로는 스파이더(spider, 웹을 돌아다니는 로봇), 명령줄 도구, 전광판, 가전제품, 저울, 전구, 펌웨어 업데이트 스크립트, 모바일 앱, 그리고 온갖 모양과 크기의 통신 기기가 있다.
사용자 에이전트라고 해서 요청 시점에 사람 사용자가 그 소프트웨어 에이전트와 직접 상호 작용하고 있다는 뜻은 아니다. 많은 경우 사용자 에이전트는 백그라운드에서 실행되도록 설치되거나 설정되어, 나중에 살펴볼 수 있도록 결과를 저장한다(또는 그 결과 가운데 흥미롭거나 오류가 있는 일부만 저장한다). 예를 들어 스파이더는 보통 시작 URI를 받아, 웹을 하이퍼텍스트 그래프로 보고 크롤링하면서 특정 동작을 따르도록 설정된다.
많은 사용자 에이전트는 사용자에게 대화형 제안을 하거나 보안 또는 개인 정보 문제에 대해 적절한 경고를 할 수 없거나, 하지 않기로 선택한다. 이 명세가 사용자에게 오류를 알리도록 요구하는 몇 안 되는 경우에는, 그 알림을 오류 콘솔이나 로그 파일에서만 볼 수 있어도 된다. 마찬가지로, 자동화된 동작을 진행하기 전에 사용자의 확인을 받으라는 요구 사항은 사전 설정, 실행 시간 옵션, 또는 단순히 안전하지 않은 동작을 피하는 것으로 충족할 수 있다. 사용자가 이미 그 선택을 했다면, 확인이 특정한 사용자 인터페이스나 정상 처리의 중단을 뜻하지는 않는다.
“원본 서버(origin server)”라는 용어는 주어진 대상 리소스에 대해 권한 있는(authoritative) 응답을 만들어 낼 수 있는 프로그램을 가리킨다.
가장 익숙한 형태의 원본 서버는 대형 공개 웹사이트다. 그러나 사용자 에이전트를 브라우저와 동일시하는 것처럼, 모든 원본 서버가 비슷하다고 오해하기 쉽다. 흔한 원본 서버에는 홈 오토메이션 장치, 설정 가능한 네트워크 장비, 사무기기, 자율 로봇, 뉴스 피드, 교통 카메라, 실시간 광고 선택기, 주문형 비디오(VOD) 플랫폼도 포함된다.
대부분의 HTTP 통신은 URI로 식별되는 어떤 리소스의 표현을 가져오는 요청(GET)으로 이루어진다. 가장 단순한 경우, 이는 사용자 에이전트(UA)와 원본 서버(O) 사이의 단일 양방향 연결(===)로 이루어질 수 있다.
request >
UA ======================================= O
< response
그림 1
HTTP에서는 중개자를 사용해 여러 연결의 사슬을 거쳐 요청을 처리할 수 있다. 흔한 HTTP “중개자(intermediary)”에는 프록시, 게이트웨이, 터널의 세 가지 형태가 있다. 경우에 따라 하나의 중개자가 각 요청의 성격에 따라 동작을 바꾸어 원본 서버, 프록시, 게이트웨이, 터널로 동작할 수 있다.
> > > >
UA =========== A =========== B =========== C =========== O
< < < <
그림 2
위 그림은 사용자 에이전트와 원본 서버 사이에 있는 세 중개자(A, B, C)를 보여 준다. 사슬 전체를 지나는 요청 또는 응답 메시지는 서로 다른 네 개의 연결을 거친다. 일부 HTTP 통신 옵션은 터널이 아닌 가장 가까운 이웃과의 연결에만 적용되거나, 사슬의 양쪽 엔드포인트에만 적용되거나, 사슬을 따라 있는 모든 연결에 적용될 수 있다. 그림은 일직선이지만, 각 참여자는 여러 통신에 동시에 관여할 수 있다. 예를 들어 B는 A의 요청을 처리하는 동시에 A 외의 많은 클라이언트로부터 요청을 받거나, C 외의 서버로 요청을 전달하고 있을 수 있다. 마찬가지로 이후의 요청은 다른 경로의 연결을 통해 보내질 수 있으며, 이는 보통 부하 분산을 위한 동적 설정에 따른다.
“업스트림(upstream)”과 “다운스트림(downstream)”이라는 용어는 메시지 흐름에 대한 방향성 요구 사항을 설명하는 데 쓰인다. 모든 메시지는 업스트림에서 다운스트림으로 흐른다. “인바운드(inbound)”와 “아웃바운드(outbound)”라는 용어는 요청 경로에 대한 방향성 요구 사항을 설명하는 데 쓰인다. 인바운드는 “원본 서버 쪽으로”, 아웃바운드는 “사용자 에이전트 쪽으로”를 뜻한다.
“프록시(proxy)”는 클라이언트가 보통 로컬 설정 규칙을 통해 선택하는 메시지 전달 에이전트로, 특정 유형의 절대 URI에 대한 요청을 받아 HTTP 인터페이스를 통한 변환으로 그 요청을 처리하려고 시도한다. 어떤 변환은 “http” URI에 대한 프록시 요청처럼 최소한에 그치지만, 어떤 요청은 완전히 다른 애플리케이션 수준 프로토콜과의 상호 변환이 필요할 수 있다. 프록시는 보안 서비스, 주석 서비스, 공유 캐싱을 위해 조직의 HTTP 요청을 공통 중개자로 모으는 데 자주 쓰인다. 일부 프록시는 7.7절에 설명된 대로, 선택된 메시지나 콘텐츠를 전달하는 동안 변형을 가하도록 설계된다.
“게이트웨이(gateway)”(“리버스 프록시(reverse proxy)”라고도 한다)는 아웃바운드 연결에 대해서는 원본 서버로 동작하지만, 받은 요청을 변환하여 하나 이상의 다른 서버로 인바운드 방향으로 전달하는 중개자다. 게이트웨이는 레거시 정보 서비스나 신뢰할 수 없는 정보 서비스를 캡슐화하거나, “가속기(accelerator)” 캐싱으로 서버 성능을 높이거나, 여러 기계에 걸쳐 HTTP 서비스를 분할하거나 부하 분산하는 데 자주 쓰인다.
원본 서버에 적용되는 모든 HTTP 요구 사항은 게이트웨이의 아웃바운드 통신에도 적용된다. 게이트웨이는 이 명세의 범위를 벗어나는 HTTP 비공개 확장을 포함해 원하는 어떤 프로토콜로든 인바운드 서버와 통신한다. 그러나 제3자 HTTP 서버와 상호 운용하려는 HTTP-HTTP 게이트웨이는 게이트웨이의 인바운드 연결에서 사용자 에이전트 요구 사항을 따라야 한다.
“터널(tunnel)”은 메시지를 바꾸지 않고 두 연결 사이를 맹목적으로 중계(blind relay)한다. 터널은 HTTP 요청으로 시작되었을 수 있지만, 일단 활성화되면 HTTP 통신의 당사자로 간주하지 않는다. 터널은 중계되는 연결의 양 끝이 모두 닫히면 사라진다. 터널은 공유 방화벽 프록시를 통해 기밀 통신을 맺기 위해 TLS(Transport Layer Security, [TLS13])를 사용할 때처럼, 중개자를 통해 가상 연결을 연장하는 데 쓰인다.
위의 중개자 분류는 HTTP 통신에 참여자로 동작하는 것만 고려한다. 네트워크 프로토콜 스택의 더 낮은 계층에서 동작하면서 메시지 송신자가 알지 못하거나 허락하지 않은 채 HTTP 트래픽을 걸러 내거나 경로를 바꾸는 중개자도 있다. 네트워크 중개자는 (프로토콜 수준에서) 경로상 공격자(on-path attacker)와 구별할 수 없으며, 실수로 HTTP 의미론을 어겨 보안 결함이나 상호 운용성 문제를 일으키는 경우가 많다.
예를 들어 “가로채기 프록시(interception proxy)”[RFC3040](흔히 “투명 프록시(transparent proxy)”[RFC1919]라고도 한다)는 클라이언트가 선택하지 않는다는 점에서 HTTP 프록시와 다르다. 가로채기 프록시는 나가는 TCP 80번 포트 패킷(때로는 다른 흔한 포트의 트래픽도)을 걸러 내거나 경로를 바꾼다. 가로채기 프록시는 공용 네트워크 접속 지점에서 로컬이 아닌 인터넷 서비스를 쓰기 전에 계정 가입을 강제하는 수단으로, 그리고 기업 방화벽 안에서 네트워크 사용 정책을 강제하는 데 흔히 쓰인다.
“캐시(cache)”는 이전 응답 메시지를 저장하는 로컬 저장소와, 메시지의 저장·조회·삭제를 제어하는 하위 시스템을 말한다. 캐시는 이후의 동등한 요청에 대한 응답 시간과 네트워크 대역폭 소비를 줄이기 위해 캐시 가능한 응답을 저장한다. 모든 클라이언트나 서버는 캐시를 사용할 수 있다(MAY). 단, 터널로 동작하는 동안에는 캐시를 사용할 수 없다.
캐시의 효과는, 사슬을 따라 있는 참여자 중 하나가 그 요청에 적용할 수 있는 캐시된 응답을 가지고 있으면 요청/응답 사슬이 짧아진다는 것이다. 다음 그림은 UA나 A가 캐시하지 않은 요청에 대해 B가 (C를 거쳐) O에서 받은 이전 응답의 캐시된 사본을 가지고 있을 때 결과로 생기는 사슬을 보여 준다.
> >
UA =========== A =========== B - - - - - - C - - - - - - O
< <
그림 3
캐시가 이후 요청에 응답하는 데 쓰려고 응답 메시지의 사본을 저장하는 것이 허용되면, 그 응답은 “캐시 가능(cacheable)”하다. 응답이 캐시 가능하더라도, 캐시된 응답을 특정 요청에 언제 쓸 수 있는지에 대해 클라이언트나 원본 서버가 추가 제약을 둘 수 있다. 캐시 동작과 캐시 가능한 응답에 대한 HTTP 요구 사항은 [CACHING]에 정의되어 있다.
월드 와이드 웹 전반과 대규모 조직 내부에는 매우 다양한 구조와 구성의 캐시가 배치되어 있다. 대역폭을 절약하고 지연 시간을 줄이기 위한 국가 단위의 프록시 캐시 계층, 인기 사이트의 지역적·전 세계적 배포를 최적화하기 위해 게이트웨이 캐싱을 사용하는 콘텐츠 전송망(CDN), 캐시 항목을 브로드캐스트하거나 멀티캐스트하는 협업 시스템, 오프라인 또는 지연 시간이 긴 환경에서 쓰기 위해 미리 가져온 캐시 항목의 보관소 등이 있다.
다음 예는 URI “http://www.example.com/hello.txt”에 대한 GET 요청(9.3.1절)의 전형적인 HTTP/1.1 메시지 교환을 보여 준다.
클라이언트 요청:
GET /hello.txt HTTP/1.1
User-Agent: curl/7.64.1
Host: www.example.com
Accept-Language: en, mi
서버 응답:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
Hello World! My content includes a trailing CRLF.
역주: 그림과 메시지 예는 번역하지 않고 원문 그대로 두었다. 응답 본문의 영어 문장도 메시지의 일부(
Content-Length: 51)이므로 바꾸지 않았다. 3.7절의 “needs to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
URI(Uniform Resource Identifier)[URI]는 HTTP 전반에서 리소스(3.1절)를 식별하는 수단으로 쓰인다.
URI 참조는 요청의 대상을 지정하고, 리다이렉트를 가리키고, 관계를 정의하는 데 쓰인다.
“URI-reference”, “absolute-URI”, “relative-part”, “authority”, “port”, “host”, “path-abempty”, “segment”, “query”의 정의는 URI 범용 구문에서 가져온다. “absolute-path” 규칙은 비어 있지 않은 경로 구성 요소를 담을 수 있는 프로토콜 요소를 위해 정의한다. (이 규칙은 빈 경로를 허용하는 RFC 3986의 path-abempty 규칙, 그리고 “//”로 시작하는 경로를 허용하지 않는 path-absolute 규칙과 조금 다르다.) “partial-URI” 규칙은 상대 URI를 담을 수 있지만 프래그먼트 구성 요소는 담을 수 없는 프로토콜 요소를 위해 정의한다.
URI-reference = <URI-reference, see [URI], Section 4.1>
absolute-URI = <absolute-URI, see [URI], Section 4.3>
relative-part = <relative-part, see [URI], Section 4.2>
authority = <authority, see [URI], Section 3.2>
uri-host = <host, see [URI], Section 3.2.2>
port = <port, see [URI], Section 3.2.3>
path-abempty = <path-abempty, see [URI], Section 3.3>
segment = <segment, see [URI], Section 3.3>
query = <query, see [URI], Section 3.4>
absolute-path = 1*( "/" segment )
partial-URI = relative-part [ "?" query ]
URI 참조를 허용하는 HTTP의 각 프로토콜 요소는 그 요소가 어떤 형태의 참조든 허용하는지(URI-reference), 절대 형식의 URI만 허용하는지(absolute-URI), 경로와 선택적인 쿼리 구성 요소만 허용하는지(partial-URI), 또는 이들의 조합을 허용하는지를 ABNF 생성 규칙에서 나타낸다. 달리 언급하지 않는 한, URI 참조는 대상 URI(7.1절)를 기준으로 파싱한다.
모든 송신자와 수신자는 프로토콜 요소에서 최소 8000옥텟 길이의 URI를 지원할 것을 권고한다(RECOMMENDED). 이는 경우에 따라 일부 구조와 와이어상 표현(예: HTTP/1.1의 요청 줄)이 그만큼 커질 수밖에 없다는 뜻이기도 하다.
IANA는 URI 스킴 레지스트리[BCP35]를 https://www.iana.org/assignments/uri-schemes/에서 관리한다. 요청은 어떤 URI 스킴이든 대상으로 할 수 있지만, 다음 스킴은 HTTP 서버에 고유하다.
| URI 스킴 | 설명 | 절 |
|---|---|---|
| http | Hypertext Transfer Protocol | 4.2.1 |
| https | Hypertext Transfer Protocol Secure | 4.2.2 |
표 2
“http”나 “https” URI가 있다고 해서 식별된 출처에서 연결을 기다리는 HTTP 서버가 항상 있다는 뜻은 아니다. 서버가 있든 없든, 그리고 그 서버가 현재 그 식별자를 리소스에 대응시키고 있든 아니든, 누구나 URI를 만들 수 있다. 등록된 이름과 IP 주소는 위임되는 성격이 있어서, HTTP 서버가 있든 없든 연합 네임스페이스(federated namespace)를 이룬다.
이 문서는 “http” URI 스킴을, 주어진 포트에서 TCP([TCP]) 연결을 기다리는 잠재적인 HTTP 원본 서버가 관리하는 계층적 네임스페이스 안에서 식별자를 만들기 위한 스킴으로 정의한다.
http-URI = "http" "://" authority path-abempty [ "?" query ]
“http” URI의 원본 서버는 authority 구성 요소로 식별되며, 이 구성 요소에는 호스트 식별자([URI]의 3.2.2절)와 선택적인 포트 번호([URI]의 3.2.3절)가 들어 있다. 포트 하위 구성 요소가 비어 있거나 주어지지 않으면 TCP 80번 포트(WWW 서비스용 예약 포트)가 기본값이다. 출처는 식별된 리소스를 대상으로 하는 요청에 누가 권한 있게 응답할 권리가 있는지 정한다. 이는 4.3.2절에 정의되어 있다.
송신자는 호스트 식별자가 비어 있는 “http” URI를 생성해서는 안 된다(MUST NOT). 그런 URI 참조를 처리하는 수신자는 그 참조를 유효하지 않은 것으로 반드시 거부해야 한다(MUST).
계층적인 경로 구성 요소와 선택적인 쿼리 구성 요소는 그 원본 서버의 네임스페이스 안에서 대상 리소스를 식별한다.
역주: authority는 URI 구성 요소 이름([URI]의 3.2절)이라 “authority 구성 요소”처럼 원어로 두었다. 직역하면 “권한 구성 요소”로, 17.1절의 권한(authority)과 같은 단어다.
이 문서는 “https” URI 스킴을, 주어진 포트에서 TCP 연결을 기다리고 HTTP 통신을 위해 보안 처리된 TLS([TLS13]) 연결을 맺을 수 있는 잠재적인 원본 서버가 관리하는 계층적 네임스페이스 안에서 식별자를 만들기 위한 스킴으로 정의한다. 이 맥락에서 “보안 처리된(secured)”은 구체적으로, 서버가 식별된 authority를 대신해 동작한다는 것이 인증되었고, 그 서버와의 모든 HTTP 통신이 클라이언트와 서버 양쪽 모두 받아들일 수 있는 기밀성과 무결성 보호를 받는다는 뜻이다.
https-URI = "https" "://" authority path-abempty [ "?" query ]
“https” URI의 원본 서버는 authority 구성 요소로 식별되며, 이 구성 요소에는 호스트 식별자([URI]의 3.2.2절)와 선택적인 포트 번호([URI]의 3.2.3절)가 들어 있다. 포트 하위 구성 요소가 비어 있거나 주어지지 않으면 TCP 443번 포트(HTTP over TLS용 예약 포트)가 기본값이다. 출처는 식별된 리소스를 대상으로 하는 요청에 누가 권한 있게 응답할 권리가 있는지 정한다. 이는 4.3.3절에 정의되어 있다.
송신자는 호스트 식별자가 비어 있는 “https” URI를 생성해서는 안 된다(MUST NOT). 그런 URI 참조를 처리하는 수신자는 그 참조를 유효하지 않은 것으로 반드시 거부해야 한다(MUST).
계층적인 경로 구성 요소와 선택적인 쿼리 구성 요소는 그 원본 서버의 네임스페이스 안에서 대상 리소스를 식별한다.
클라이언트는 “https” 리소스에 대한 HTTP 요청이 전달되기 전에 보안 처리되도록, 그리고 그 요청에 대해 보안 처리된 응답만 받아들이도록 반드시 보장해야 한다(MUST). 클라이언트와 서버가 받아들일 수 있는 암호 메커니즘의 정의는 보통 협상을 거쳐 정해지며 시간이 지나면서 바뀔 수 있다는 점에 유의한다.
“https” 스킴으로 제공되는 리소스는 “http” 스킴과 정체성을 공유하지 않는다. 둘은 별개의 네임스페이스를 가진 서로 다른 출처다. 그러나 쿠키 프로토콜[COOKIE]처럼 같은 호스트를 가진 모든 출처에 적용되도록 정의된 HTTP 확장은, 한 서비스가 설정한 정보가 같은 호스트 도메인 그룹 안의 다른 서비스와의 통신에 영향을 주게 한다. 이런 확장은 보안 처리된 연결에서 얻은 정보가 보안 처리되지 않은 맥락에서 실수로 교환되지 않도록 매우 신중하게 설계되어야 마땅하다.
“http”나 “https” 스킴의 URI는 위에서 설명한 스킴별 기본값을 사용해 [URI]의 6장에 정의된 방법에 따라 정규화하고 비교한다.
HTTP는 동등성을 판단하는 데 특정 방법을 쓰도록 요구하지 않는다. 예를 들어 캐시 키는 단순 문자열로, 구문 기반 정규화 후에, 또는 스킴 기반 정규화 후에 비교할 수 있다.
“http”와 “https” URI의 스킴 기반 정규화([URI]의 6.2.3절)에는 다음 규칙이 추가된다.
OPTIONS 요청의 대상으로 쓰이는 경우가 아니라면, 빈 경로 구성 요소는 절대 경로 “/”와 동등하므로 대신 “/” 경로를 주는 것이 정규형이다.예를 들어 다음 세 URI는 동등하다.
http://example.com:80/~smith/home.html
http://EXAMPLE.com/%7Esmith/home.html
http://EXAMPLE.com:/%7esmith/home.html
(어떤 방법을 쓰든) 정규화 후 동등한 두 HTTP URI는 같은 리소스를 식별한다고 가정할 수 있으며, 모든 HTTP 구성 요소는 정규화를 수행할 수 있다(MAY). 따라서 서로 다른 리소스를, ([URI]의 6.2절에 정의된 어떤 방법으로든) 정규화 후 동등한 HTTP URI로 식별하지 않아야 한다(SHOULD NOT).
authority에 대한 URI 범용 구문에는 URI에 사용자 인증 정보를 넣기 위한 userinfo 하위 구성 요소([URI]의 3.2.1절)도 있다. 그 하위 구성 요소에서 “user:password” 형식은 사용이 중단되었다(deprecated).
일부 구현은 명령 실행 옵션, 설정 파일, 북마크 목록처럼 인증 정보를 내부적으로 설정하는 데 userinfo 구성 요소를 사용한다. 이런 사용 방식은 사용자 식별자나 비밀번호를 노출할 수 있는데도 그렇다.
송신자는 메시지 안에서 “http”나 “https” URI 참조를 대상 URI나 필드 값으로 생성할 때 userinfo 하위 구성 요소(와 그 구분자 “@”)를 생성해서는 안 된다(MUST NOT).
신뢰할 수 없는 곳에서 받은 “http”나 “https” URI 참조를 사용하기 전에, 수신자는 userinfo가 있는지 파싱하고 있으면 오류로 처리해야 한다(SHOULD). userinfo는 피싱 공격을 위해 authority를 감추는 데 쓰이고 있을 가능성이 높다.
프래그먼트 식별자(fragment identifier)는 [URI]의 3.5절에 정의된 대로, URI 스킴과 무관하게 2차 리소스를 간접적으로 식별할 수 있게 한다. URI를 참조하는 프로토콜 요소 중 일부는 프래그먼트를 포함할 수 있고 일부는 포함할 수 없다. 이들은 프래그먼트가 허용되는 요소에는 그 ABNF 규칙을 쓰고, 그렇지 않으면 프래그먼트를 제외하는 별도 규칙을 쓰는 것으로 구별한다.
참고: 프래그먼트 식별자 구성 요소는 URI 스킴의 스킴 정의에 속하지 않으므로([URI]의 4.3절 참고), 위의 “http”와 “https” URI 스킴 ABNF 정의에 나타나지 않는다.
권한 있는 접근(authoritative access)이란 식별된 리소스에 접근하기 위해, 클라이언트가 권한 있다고(리소스 소유자가 통제한다고) 믿는 방식으로 주어진 식별자를 역참조(dereference)하는 것을 말한다. 접근이 허용되는지 판단하는 과정은 URI 스킴이 정의하며, 범용 구문을 쓸 때의 authority 구성 요소처럼 URI 구성 요소 안의 데이터를 사용하는 경우가 많다. 그러나 권한 있는 접근이 그 식별된 메커니즘으로만 한정되지는 않는다.
4.3.1절은 이런 용도를 돕기 위해 출처의 개념을 정의하고, 그 뒤의 하위 절들은 피어가 출처를 대표할 권한이 있다는 것을 어떻게 확인하는지 설명한다.
권한 확인에 관련된 보안 고려 사항은 17.1절을 참고한다.
주어진 URI의 “출처(origin)”는 스킴과 호스트를 소문자로 정규화하고 포트의 앞자리 0을 없애 정규화한 뒤의 스킴, 호스트, 포트 세 값의 묶음이다. URI에서 포트가 생략되었으면 그 스킴의 기본 포트를 쓴다. 예를 들어 다음 URI는
https://Example.Com/happy.js
다음 출처를 가지며,
{ "https", "example.com", "443" }
이는 포트를 항상 표시한 정규화된 URI 접두사로도 나타낼 수 있다.
https://example.com:443
각 출처는 자기 네임스페이스를 정의하고, 그 네임스페이스 안의 식별자가 리소스에 어떻게 대응되는지 통제한다. 그리고 출처가 유효한 요청에 시간이 지나도 일관되게 어떻게 응답하느냐가 사용자가 URI에 연관 짓는 의미를 결정한다. 그 의미의 유용성이야말로 결국 이런 메커니즘을 사용자가 나중에 참조하고 접근할 수 있는 리소스로 바꾸어 준다.
두 출처는 스킴, 호스트, 포트 중 하나라도 다르면 서로 다르다. 같은 주체가 서로 다른 두 출처를 통제한다는 것을 확인할 수 있더라도, 그 출처에 대해 권한 있는 서버가 명시적으로 별칭을 지정하지 않는 한 두 출처 아래의 네임스페이스는 서로 다르다.
출처는 이 문서의 범위를 넘어서, [RFC6454]에 설명된 것처럼 HTML과 관련 웹 프로토콜에서도 쓰인다.
HTTP는 전송 프로토콜과 무관하지만, “http” 스킴(4.2.1절)은 authority 구성 요소 안에서 식별되는 호스트의 지정된 포트에서 TCP 연결을 기다리는 원본 서버를 통제하는 누구에게든 권한을 연관 짓는 데 특화되어 있다. 이는 클라이언트별 이름 해석 메커니즘과, 경로상 공격자로부터 보호되지 않을 수 있는 통신 양쪽에 의존하므로 매우 약한 의미의 권한이다. 그렇더라도 신뢰할 수 있는 환경 안에서 일관되게 해석되도록 “http” 식별자를 원본 서버에 묶는 데는 충분한 최소한이다.
호스트 식별자가 IP 주소로 주어지면, 원본 서버는 그 IP 주소의 지정된 TCP 포트에서 연결을 기다리는 쪽(있다면)이다. 호스트가 등록된 이름이면, 그 등록된 이름은 적절한 원본 서버의 주소를 찾기 위해 DNS 같은 이름 해석 서비스와 함께 쓰는 간접 식별자다.
“http” URI가 지정된 리소스에 접근해야 하는 맥락에서 쓰이면, 클라이언트는 호스트 식별자를 IP 주소로 해석하고, 그 주소의 지정된 포트로 TCP 연결을 맺고, 그 연결로 클라이언트의 대상 URI와 일치하는 요청 대상을 담은 HTTP 요청 메시지를 보내 접근을 시도할 수 있다(MAY)(7.1절).
서버가 그 요청에 15장에 설명된 중간 응답이 아닌(non-interim) HTTP 응답 메시지로 응답하면, 그 응답은 클라이언트의 요청에 대한 권한 있는 답으로 간주한다.
그러나 위 방법만이 권한 있는 응답을 얻는 유일한 수단은 아니며, 권한 있는 응답이 항상 필요하다는 뜻도 아니라는 점에 유의한다([CACHING] 참고). 예를 들어 Alt-Svc 헤더 필드[ALTSVC]를 사용하면 원본 서버는 그 출처에 대해 마찬가지로 권한 있는 다른 서비스를 알려 줄 수 있다. “http”로 식별되는 리소스에 대한 접근은 이 문서의 범위 밖에 있는 프로토콜로 제공될 수도 있다.
“https” 스킴(4.2.2절)은 서버가, 식별된 원본 서버에 대해 클라이언트가 신뢰할 만하다고 여기는 인증서에 대응하는 개인 키를 사용할 수 있는지에 기반해 권한을 연관 짓는다. 클라이언트는 보통 인증서를 신뢰할 만하다고 판단하기 위해, 미리 정해 두었거나 설정해 둔 신뢰 앵커(trust anchor)에서 이어지는 신뢰 체인에 의존한다(4.3.4절).
HTTP/1.1 이하에서 클라이언트는 그 URI 출처의 호스트로 특정해 성공적으로 맺어지고 보안 처리된 연결로 통신할 때만 서버에 권한이 있다고 인정한다. 연결 수립과 인증서 검증이 권한의 증거로 쓰인다.
HTTP/2와 HTTP/3에서 클라이언트는 성공적으로 맺어지고 보안 처리된 연결로 통신할 때, URI 출처의 호스트가 서버 인증서에 있는 호스트 중 하나와 일치하고, 그 URI를 위해 그 호스트로 연결을 열 수 있다고 클라이언트가 믿으면 서버에 권한이 있다고 인정한다. 실제로는 클라이언트가 DNS 질의를 해서 그 출처의 호스트가 이미 맺은 연결과 같은 서버 IP 주소를 가리키는지 확인한다. 이 제약은 원본 서버가 동등한 ORIGIN 프레임[RFC8336]을 보내면 없앨 수 있다.
요청 대상의 호스트와 포트 값은 각 HTTP 요청 안에 담겨 전달되며, 출처를 식별하고 같은 서버가 통제할 수 있는 다른 네임스페이스와 구별한다(7.2절). 출처의 인증서 개인 키에 대한 통제권을 받은 모든 서비스가 해당 “https” 네임스페이스를 똑같이 책임지고 관리하거나, 최소한 잘못 전달된 것으로 보이는 요청을 거부할 준비가 되어 있도록 보장하는 것은 출처의 책임이다(7.4절).
원본 서버는 권한이 있더라도 특정 대상 URI에 대한 요청을 처리하지 않으려 할 수 있다. 예를 들어 한 호스트가 서로 다른 포트(예: 443과 8000)에서 별개의 서비스를 운영하면, 네트워크 공격자가 한 포트로 가는 연결을 다른 포트에서 받게 만들 수 있으므로 (연결이 보안 처리된 뒤에도) 원본 서버에서 대상 URI를 확인해야 한다. 대상 URI를 확인하지 않으면 그런 공격자가 한 대상 URI(예: “https://example.com/foo”)에 대한 응답을 다른 포트에서 온, 겉보기에 권한 있는 응답(예: “https://example.com:8000/foo”)으로 바꿔치기할 수 있다.
“https” 스킴은 권한을 연관 짓는 데 TCP에도, 연결에 쓰인 포트 번호에도 의존하지 않는다는 점에 유의한다. 둘 다 보안 처리된 통신 바깥에 있어서 확정적인 것으로 신뢰할 수 없기 때문이다. 따라서 HTTP 통신은 4.2.2절에 정의된 대로 보안 처리된 어떤 채널에서든, TCP를 쓰지 않는 프로토콜을 포함해 이루어질 수 있다.
“https” URI가 지정된 리소스에 접근해야 하는 맥락에서 쓰이면, 클라이언트는 호스트 식별자를 IP 주소로 해석하고, 그 주소의 지정된 포트로 TCP 연결을 맺고, 기밀성과 무결성 보호를 갖춘 TLS를 TCP 위에서 성공적으로 시작해 연결을 종단 간 보안 처리하고, 그 연결로 클라이언트의 대상 URI와 일치하는 요청 대상을 담은 HTTP 요청 메시지를 보내 접근을 시도할 수 있다(MAY)(7.1절).
서버가 그 요청에 15장에 설명된 중간 응답이 아닌 HTTP 응답 메시지로 응답하면, 그 응답은 클라이언트의 요청에 대한 권한 있는 답으로 간주한다.
그러나 위 방법만이 권한 있는 응답을 얻는 유일한 수단은 아니며, 권한 있는 응답이 항상 필요하다는 뜻도 아니라는 점에 유의한다([CACHING] 참고).
URI를 역참조하기 위해 보안 처리된 연결을 맺으려면, 클라이언트는 서비스의 신원이 그 URI의 원본 서버와 받아들일 수 있을 만큼 일치하는지 반드시 검증해야 한다(MUST). 인증서 검증은 경로상 공격자나 이름 해석을 통제하는 공격자가 서버를 위장(impersonation)하는 것을 막는 데 쓰인다. 이 과정을 위해서는 클라이언트에 신뢰 앵커 집합이 설정되어 있어야 한다.
일반적으로 클라이언트는 [RFC6125]의 6장에 정의된 검증 과정을 사용해 서비스 신원을 반드시 검증해야 한다(MUST). 클라이언트는 서비스의 호스트로부터 참조 식별자(reference identity)를 반드시 구성해야 한다(MUST). 호스트가 IP 주소 리터럴이면(4.3.5절) 참조 식별자는 IP-ID이고, 그렇지 않으면 호스트는 이름이며 참조 식별자는 DNS-ID다.
클라이언트는 CN-ID 유형의 참조 식별자를 사용해서는 안 된다(MUST NOT). [RFC6125]의 6.2.1절에서 언급하듯, CN-ID 유형의 참조 식별자는 오래된 클라이언트가 사용했던 방식이다.
클라이언트는 다른 형태의 서버 신원 검증을 받아들이도록 특별히 설정될 수 있다. 예를 들어 클라이언트가 주소와 호스트 이름이 동적으로 바뀌는 서버에 연결하면서, 서비스가 대상 URI의 출처와 일치하는 인증서가 아니라 특정 인증서(또는 외부에서 정의한 어떤 참조 식별자와 일치하는 인증서)를 제시하리라고 기대할 수 있다.
특별한 경우에는 클라이언트가 서버의 신원을 그냥 무시하는 것이 적절할 수도 있지만, 그렇게 하면 연결이 능동적 공격에 노출된다는 점을 알아 두어야 한다.
인증서가 대상 URI의 출처에 대해 유효하지 않으면, 사용자 에이전트는 진행하기 전에 사용자의 확인을 받거나(3.5절 참고) 잘못된 인증서(bad certificate) 오류로 연결을 반드시 끝내야 한다(MUST). 자동화된 클라이언트는 오류를 적절한 감사 로그(있다면)에 반드시 기록해야 하며(MUST), (잘못된 인증서 오류로) 연결을 끝내야 한다(SHOULD). 자동화된 클라이언트는 이 검사를 끄는 설정을 제공할 수 있지만(MAY), 이 검사를 켜는 설정은 반드시 제공해야 한다(MUST).
역주: 4.3.4절 다섯째 문단의 “it must be understood”는 소문자 must이므로 규범 키워드가 아니다. MUST의 고정 표현(“반드시 ~해야 한다”)과 구별되도록 “알아 두어야 한다”로 옮기고 괄호를 붙이지 않았다. 4.2.2절의 “ought to”도 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
“https” URI의 “host” 필드에 IP 주소 리터럴을 써서 식별되는 서버는 IP-ID 유형의 참조 식별자를 갖는다. IP 버전 4 주소는 “IPv4address” ABNF 규칙을 쓰고, IP 버전 6 주소는 “IPv6address” 옵션이 있는 “IP-literal” 생성 규칙을 쓴다([URI]의 3.2.2절 참고). IP-ID 참조 식별자에는 IP 주소를 디코딩한 바이트가 들어 있다.
IP 버전 4 주소는 4옥텟이고, IP 버전 6 주소는 16옥텟이다. IP-ID의 사용은 그 밖의 IP 버전에 대해서는 정의되지 않는다. 인증서 subjectAltName 확장의 iPAddress 선택지는 IP 버전을 명시적으로 담지 않으므로 주소의 길이로 버전을 구분한다([RFC5280]의 4.2.1.6절 참고).
IP-ID 유형의 참조 식별자는 그 주소가 인증서 subjectAltName 확장의 iPAddress 값과 같으면 일치한다.
HTTP는 등록된 키 네임스페이스를 가진 확장 가능한 이름/값 쌍의 형태로 데이터를 제공하기 위해 “필드(field)”를 사용한다. 필드는 메시지의 헤더 섹션과 트레일러 섹션 안에서 보내고 받는다(6장).
필드 이름은 그에 대응하는 필드 값이 그 이름으로 정의된 의미를 갖는다는 것을 나타낸다. 예를 들어 Date 헤더 필드는 6.6.1절에서, 그 필드가 나타나는 메시지의 생성 시각 타임스탬프를 담는 것으로 정의된다.
field-name = token
필드 이름은 대소문자를 구분하지 않으며, “HTTP 필드 이름 레지스트리(Hypertext Transfer Protocol (HTTP) Field Name Registry)”에 등록되어야 마땅하다. 16.3.1절을 참고한다.
필드의 해석은 같은 HTTP 주 버전의 부 버전 사이에서 바뀌지 않는다. 다만 그런 필드가 없을 때 수신자의 기본 동작은 바뀔 수 있다. 달리 명시하지 않는 한, 필드는 모든 HTTP 버전에 대해 정의된다. 특히 Host와 Connection 필드는 HTTP/1.1 적합성을 알리는지와 상관없이 모든 HTTP 구현이 인식해야 마땅하다.
정의된 의미가 그 필드를 인식하지 못하는 수신자가 안전하게 무시할 수 있는 것이라면, 프로토콜 버전을 바꾸지 않고도 새 필드를 도입할 수 있다. 16.3절을 참고한다.
프록시는 인식하지 못한 헤더 필드를 반드시 전달해야 한다(MUST). 단, 그 필드 이름이 Connection 헤더 필드(7.6.1절)에 나열되어 있거나, 프록시가 그런 필드를 차단하거나 달리 변형하도록 특별히 설정된 경우는 예외다. 그 밖의 수신자는 인식하지 못한 헤더 필드와 트레일러 필드를 무시해야 한다(SHOULD). 이 요구 사항을 지키면 이미 배치된 중개자를 갱신하거나 없애지 않고도 HTTP의 기능을 확장할 수 있다.
필드 섹션은 임의 개수의 “필드 줄(field line)”로 이루어진다. 각 필드 줄에는 필드를 식별하는 “필드 이름(field name)”(5.1절 참고)과, 그 필드 인스턴스의 데이터를 전달하는 “필드 줄 값(field line value)”이 있다.
어떤 필드 이름이 한 섹션에 한 번만 나오면, 그 필드의 결합된 “필드 값(field value)”은 해당 필드 줄 값으로 이루어진다. 어떤 필드 이름이 한 섹션 안에서 반복되면, 그 필드의 결합된 필드 값은 그 섹션 안의 해당 필드 줄 값들을 순서대로 이어 붙이고 각 필드 줄 값을 쉼표로 구분한 목록으로 이루어진다.
예를 들어 다음 섹션은
Example-Field: Foo, Bar
Example-Field: Baz
필드 이름이 모두 “Example-Field”인 필드 줄 두 개를 담고 있다. 첫째 필드 줄의 필드 줄 값은 “Foo, Bar”이고, 둘째 필드 줄 값은 “Baz”다. “Example-Field”의 필드 값은 “Foo, Bar, Baz” 목록이다.
역주: 정오표 9162(Reported, 미확인)는 이 절에서 “쉼표로 구분한다”는 정의와 예시의 “Foo, Bar, Baz”(쉼표 뒤 공백)가 어긋난다고 지적한다. 확인되지 않은 정오표이므로 번역에는 반영하지 않았다. 5.3절은 결합할 때 쉼표와 선택적 공백을 쓰고, 일관성을 위해 쉼표
SP를 쓰라고 한다.
수신자는 필드 섹션 안에서 필드 이름이 같은 여러 필드 줄을, 이후의 각 필드 줄 값을 처음 필드 줄 값 뒤에 순서대로 덧붙이고 쉼표(“,”)와 선택적 공백(OWS, 5.6.3절에 정의)으로 구분하는 방식으로, 메시지의 의미를 바꾸지 않고 하나의 필드 줄로 결합할 수 있다(MAY). 일관성을 위해 쉼표 SP를 쓴다.
따라서 같은 이름의 필드 줄을 받은 순서는 필드 값의 해석에 중요하다. 프록시는 메시지를 전달할 때 이 필드 줄 값들의 순서를 바꿔서는 안 된다(MUST NOT).
이는 아래에 언급하는 잘 알려진 예외를 제외하면, 송신자가 한 메시지 안에(헤더에서든 트레일러에서든) 같은 이름의 필드 줄을 여러 개 생성하거나, 같은 이름의 필드 줄이 이미 메시지에 있을 때 필드 줄을 덧붙여서는 안 된다(MUST NOT)는 뜻이다. 단, 그 필드의 정의가 여러 필드 줄 값을 쉼표로 구분된 목록으로 다시 결합하는 것을 허용하는 경우(즉, 그 필드 정의의 대안 중 적어도 하나가 5.6.1절에 정의된 #(values) 같은 ABNF 규칙처럼 쉼표로 구분된 목록을 허용하는 경우)는 예외다.
참고: 실제로
Set-Cookie헤더 필드([COOKIE])는 응답 메시지에서 흔히 여러 필드 줄에 걸쳐 나타나며 목록 구문을 쓰지 않아, 필드 이름이 같은 여러 필드 줄에 대한 위의 요구 사항을 어긴다. 하나의 필드 값으로 결합할 수 없으므로, 수신자는 필드를 처리할 때Set-Cookie를 특별한 경우로 다뤄야 마땅하다. (자세한 내용은 [Kri2001]의 부록 A.2.3을 참고한다.)
필드 이름이 서로 다른 필드 줄을 한 섹션 안에서 받는 순서는 중요하지 않다. 그러나 구현이 메시지를 처리하지 않기로 가능한 한 일찍 결정할 수 있도록, 요청의 Host나 응답의 Date처럼 추가 제어 데이터를 담은 헤더 필드를 먼저 보내는 것이 좋은 관행이다.
서버는 요청 헤더 섹션 전체를 받기 전까지 요청을 대상 리소스에 적용해서는 안 된다(MUST NOT). 뒤에 오는 헤더 필드 줄에 조건, 인증 자격 증명, 또는 요청 처리에 영향을 줄 수 있는 의도적으로 오해를 부르는 중복 헤더 필드가 들어 있을 수 있기 때문이다.
2장에서 설명했듯이, HTTP는 각 필드 줄이나 필드 값의 길이, 또는 헤더 섹션이나 트레일러 섹션 전체의 길이에 미리 정한 제한을 두지 않는다. 실제로는 개별 길이에 대한 다양한 임시방편 제한이 있으며, 그 제한은 특정 필드의 의미에 따라 달라지는 경우가 많다.
처리하려는 것보다 큰 요청 헤더 필드 줄, 필드 값, 또는 필드 집합을 받은 서버는 반드시 적절한 4xx (Client Error) 상태 코드로 응답해야 한다(MUST). 그런 헤더 필드를 무시하면 서버가 요청 스머글링(request smuggling) 공격([HTTP/1.1]의 11.2절)에 더 취약해진다.
클라이언트는 필드의 의미상 버려진 값을 메시지 프레이밍이나 응답 의미를 바꾸지 않고 안전하게 무시할 수 있다면, 처리하려는 것보다 큰 필드 줄을 받았을 때 그 필드 줄을 버리거나 잘라 낼 수 있다(MAY).
HTTP 필드 값은 필드의 문법이 정의한 형식을 따르는 문자 시퀀스로 이루어진다. 각 필드의 문법은 보통 ABNF([RFC5234])로 정의한다.
field-value = *field-content
field-content = field-vchar
[ 1*( SP / HTAB / field-vchar ) field-vchar ]
field-vchar = VCHAR / obs-text
obs-text = %x80-FF
필드 값에는 앞뒤 공백이 포함되지 않는다. 특정 HTTP 버전이 메시지에 그런 공백이 나타나는 것을 허용하는 경우, 필드를 파싱하는 구현은 필드 값을 평가하기 전에 그 공백을 반드시 제외해야 한다(MUST).
필드 값은 보통 US-ASCII 문자[USASCII] 범위로 제한된다. 더 넓은 범위의 문자가 필요한 필드는 [RFC8187]에 정의된 것 같은 인코딩을 쓸 수 있다. 역사적으로 HTTP는 ISO-8859-1 문자 집합[ISO-8859-1]으로 된 텍스트를 필드 내용으로 허용했고, 다른 문자 집합은 [RFC2047] 인코딩을 통해서만 지원했다. 새로 정의하는 필드의 명세는 값을 눈에 보이는 US-ASCII 옥텟(VCHAR), SP, HTAB으로 제한해야 한다(SHOULD). 수신자는 필드 내용에 허용된 그 밖의 옥텟(즉, obs-text)을 불투명한 데이터로 다뤄야 한다(SHOULD).
CR, LF, NUL 문자를 담은 필드 값은 구현마다 그 문자를 파싱하고 해석하는 방식이 다를 수 있어 유효하지 않으며 위험하다. 필드 값 안에서 CR, LF, NUL을 받은 수신자는 그 메시지를 더 처리하거나 전달하기 전에 반드시 메시지를 거부하거나, 그 문자들을 각각 SP로 바꿔야 한다(MUST). 그 밖의 CTL 문자를 담은 필드 값도 유효하지 않다. 그러나 수신자는 그런 문자가 안전한 맥락(예: 어떤 다운스트림 HTTP 파서도 처리하지 않을 애플리케이션 전용의 따옴표 문자열)에 나타나면 강건성을 위해 그 문자를 유지할 수 있다(MAY).
필드 값으로 구성원 하나만 예상하는 필드를 “단일 값 필드(singleton field)”라고 한다.
필드 값으로 여러 구성원을 허용하는 필드를 “목록 기반 필드(list-based field)”라고 한다. 여러 구성원을 담을 수 있는 필드 값을 정의하는 공통 표기법으로 5.6.1절의 목록 연산자 확장을 쓴다.
쉼표(“,”)는 구성원 사이의 구분자로 쓰이므로, 구성원 안의 데이터로 쉼표를 허용한다면 주의해서 다뤄야 한다. 이는 목록 기반 필드와 단일 값 필드 모두에 해당한다. 단일 값 필드도 잘못해서 여러 구성원과 함께 보내질 수 있고, 그런 오류를 감지하면 상호 운용성이 좋아지기 때문이다. HTTP-date나 URI-reference 요소 안처럼 구성원 안에 쉼표를 담을 것으로 예상되는 필드는, 그 데이터 안의 쉼표를 목록 구분자일 수 있는 쉼표와 구별하도록 그 요소를 감싸는 구분자를 두어 정의해야 마땅하다.
예를 들어 텍스트 형식의 날짜와 URI(둘 다 쉼표를 담을 수 있음)는 다음과 같은 목록 기반 필드 값으로 안전하게 전달할 수 있다.
Example-URIs: "http://example.com/a.html,foo",
"http://without-a-comma.example.com/"
Example-Dates: "Sat, 04 May 1996", "Wed, 14 Sep 2005"
큰따옴표 구분자는 거의 항상 quoted-string 생성 규칙(5.6.4절)과 함께 쓰인다는 점에 유의한다. 큰따옴표 안에서 다른 구문을 쓰면 불필요한 혼란을 일으킬 가능성이 높다.
(8.3절에 정의된 Content-Type 같은) 많은 필드는 매개 변수 값에 따옴표 없는 구문(token)과 따옴표 구문(quoted-string)을 모두 허용하는 매개 변수 공통 구문(5.6.6절)을 사용한다. 공통 구문을 쓰면 수신자가 기존 파서 구성 요소를 재사용할 수 있다. 두 형식을 모두 허용할 때, 매개 변수 값의 의미는 토큰으로 받았든 따옴표 문자열로 받았든 같아야 마땅하다.
참고: 이 명세는 필드 값 구문을 정의할 때, 필드 이름을 딴 ABNF 규칙으로 그 필드 값에 허용되는 문법을 정의한다(이때 필드 값은 하위의 메시지 구문에서 추출되고 여러 인스턴스가 하나의 목록으로 결합된 뒤의 값이다).
일부 목록 기반 필드 값의 정의를 읽기 쉽게 하기 위해 [RFC5234]의 ABNF 규칙에 #rule 확장을 쓴다.
요소들을 쉼표로 구분한 목록을 정의하기 위해 “*“와 비슷한 “#” 구문을 정의한다. 전체 형식은 “
목록 구문을 사용하는 모든 생성 규칙에서, 송신자는 빈 목록 요소를 생성해서는 안 된다(MUST NOT). 다시 말해, 송신자는 다음 구문을 만족하는 목록을 생성해야 한다.
1#element => element *( OWS "," OWS element )
그리고
#element => [ 1#element ]
그리고 n >= 1이고 m > 1일 때
<n>#<m>element => element <n-1>*<m-1>( OWS "," OWS element )
부록 A에는 목록 구문을 펼친 뒤의, 송신자를 위한 전체 ABNF가 모여 있다.
빈 요소는 요소 개수에 포함되지 않는다. 수신자는 적당한 수의 빈 목록 요소를 반드시 파싱하고 무시해야 한다(MUST). 값을 합치는 송신자의 흔한 실수를 처리할 수 있을 만큼은 되되, 서비스 거부(denial-of-service) 수단으로 쓰일 수 있을 만큼 많지는 않은 수다. 다시 말해, 수신자는 다음 구문을 만족하는 목록을 반드시 받아들여야 한다(MUST).
#element => [ element ] *( OWS "," OWS [ element ] )
빈 목록 요소가 있을 수 있기 때문에 RFC 5234 ABNF는 목록 요소의 원소 개수(cardinality)를 강제할 수 없고, 따라서 모든 경우를 원소 개수가 지정되지 않은 것처럼 대응시킨다는 점에 유의한다.
예를 들어 다음 ABNF 생성 규칙이 주어지면
example-list = 1#example-list-elmt
example-list-elmt = token ; see Section 5.6.2
다음은 example-list에 대해 유효한 값이다(큰따옴표는 구분을 위해서만 표시한 것이며 값에 포함되지 않는다).
"foo,bar"
"foo ,bar,"
"foo , ,bar,charlie"
반면, 다음 값은 example-list 생성 규칙이 비어 있지 않은 요소를 적어도 하나 요구하므로 유효하지 않다.
""
","
", ,"
토큰(token)은 공백이나 구분자를 포함하지 않는 짧은 텍스트 식별자다.
token = 1*tchar
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
; any VCHAR, except delimiters
많은 HTTP 필드 값은 공백이나 특정 구분자로 구분된 공통 구문 요소를 사용해 정의된다. 구분자는 토큰에 허용되지 않는 US-ASCII 가시 문자 집합(DQUOTE와 "(),/:;<=>?@[\]{}")에서 고른다.
이 명세는 선형 공백(linear whitespace)의 사용을 나타내기 위해 세 가지 규칙을 쓴다. OWS(선택적 공백, optional whitespace), RWS(필수 공백, required whitespace), BWS(“나쁜” 공백, “bad” whitespace)다.
OWS 규칙은 선형 공백 옥텟이 0개 이상 나타날 수 있는 곳에 쓴다. 가독성을 높이기 위해 선택적 공백을 두는 것이 바람직한 프로토콜 요소에서는, 송신자가 선택적 공백을 SP 하나로 생성해야 한다(SHOULD). 그렇지 않은 경우 송신자는 메시지를 제자리에서 필터링하는 동안 유효하지 않거나 원치 않는 프로토콜 요소를 덮어쓰는 데 필요한 경우를 제외하고는 선택적 공백을 생성하지 않아야 한다(SHOULD NOT).
RWS 규칙은 필드 토큰을 구분하기 위해 선형 공백 옥텟이 적어도 하나 필요할 때 쓴다. 송신자는 RWS를 SP 하나로 생성해야 한다(SHOULD).
OWS와 RWS는 SP 하나와 같은 의미를 갖는다. OWS나 RWS로 정의된 것으로 알려진 내용은 해석하거나 메시지를 다운스트림으로 전달하기 전에 SP 하나로 바꿀 수 있다(MAY).
BWS 규칙은 역사적인 이유로만 문법이 선택적 공백을 허용하는 곳에 쓴다. 송신자는 메시지에 BWS를 생성해서는 안 된다(MUST NOT). 수신자는 그런 나쁜 공백이 있는지 파싱하고 프로토콜 요소를 해석하기 전에 반드시 제거해야 한다(MUST).
BWS에는 의미가 없다. BWS로 정의된 것으로 알려진 내용은 해석하거나 메시지를 다운스트림으로 전달하기 전에 제거할 수 있다(MAY).
OWS = *( SP / HTAB )
; optional whitespace
RWS = 1*( SP / HTAB )
; required whitespace
BWS = OWS
; "bad" whitespace
텍스트 문자열을 큰따옴표로 감싸면 하나의 값으로 파싱한다.
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
백슬래시 옥텟(“")은 quoted-string과 comment 구문 안에서 한 옥텟짜리 인용 메커니즘으로 쓸 수 있다. quoted-string의 값을 처리하는 수신자는 quoted-pair를 백슬래시 뒤의 옥텟으로 바뀐 것처럼 반드시 처리해야 한다(MUST).
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
송신자는 quoted-string 안에서, 그 문자열 안에 있는 DQUOTE와 백슬래시 옥텟을 인용하는 데 필요한 경우를 제외하고는 quoted-pair를 생성하지 않아야 한다(SHOULD NOT). 송신자는 comment 안에서, 그 주석 안에 있는 괄호[”(“와 “)”]와 백슬래시 옥텟을 인용하는 데 필요한 경우를 제외하고는 quoted-pair를 생성하지 않아야 한다(SHOULD NOT).
일부 HTTP 필드에는 주석 텍스트를 괄호로 감싸 주석(comment)을 넣을 수 있다. 주석은 필드 값 정의에 “comment”가 포함된 필드에서만 허용된다.
comment = "(" *( ctext / quoted-pair / comment ) ")"
ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
매개 변수(parameter)는 이름/값 쌍의 인스턴스이며, 필드 값에서 어떤 항목에 보조 정보를 덧붙이는 공통 구문으로 자주 쓰인다. 각 매개 변수는 보통 바로 앞의 세미콜론으로 구분한다.
parameters = *( OWS ";" OWS [ parameter ] )
parameter = parameter-name "=" parameter-value
parameter-name = token
parameter-value = ( token / quoted-string )
매개 변수 이름은 대소문자를 구분하지 않는다. 매개 변수 값은 매개 변수 이름의 의미에 따라 대소문자를 구분할 수도, 구분하지 않을 수도 있다. 매개 변수와 몇 가지 동등한 형식의 예는 미디어 유형(8.3.1절)과 Accept 헤더 필드(12.5.1절)에서 볼 수 있다.
token 생성 규칙에 맞는 매개 변수 값은 토큰으로 보내도 되고 quoted-string 안에 넣어 보내도 된다. 따옴표로 감싼 값과 감싸지 않은 값은 동등하다.
참고: 매개 변수는 “=” 문자 주위에 공백(“나쁜” 공백 포함)을 허용하지 않는다.
1995년 이전에는 서버가 타임스탬프를 전달하는 데 흔히 쓰는 형식이 세 가지 있었다. 오래된 구현과의 호환성을 위해 세 가지 모두를 여기에 정의한다. 권장되는 형식은 인터넷 메시지 형식(Internet Message Format)[RFC5322]이 사용하는 날짜와 시간 명세의 고정 길이, 단일 시간대 부분 집합이다.
HTTP-date = IMF-fixdate / obs-date
권장 형식의 예는 다음과 같다.
Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
폐기된 두 형식의 예는 다음과 같다.
Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
HTTP 필드의 타임스탬프 값을 파싱하는 수신자는 세 가지 HTTP-date 형식을 모두 반드시 받아들여야 한다(MUST). 송신자가 HTTP-date로 정의된 타임스탬프를 하나 이상 담은 필드를 생성할 때, 송신자는 그 타임스탬프를 반드시 IMF-fixdate 형식으로 생성해야 한다(MUST).
HTTP-date 값은 시간을 협정 세계시(UTC)의 한 시점으로 나타낸다. 앞의 두 형식은 UTC라는 이름 이전에 쓰이던 그리니치 표준시의 세 글자 약어 “GMT”로 UTC를 나타낸다. asctime 형식의 값은 UTC로 가정한다.
“시계(clock)”는 UTC 기준의 현재 시점을 적당히 근사해 제공할 수 있는 구현이다. 시계 구현은 UTC와 동기화하기 위해 NTP([RFC5905])나 그와 비슷한 프로토콜을 사용해야 마땅하다.
권장 형식:
IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
; fixed length/zone/capitalization subset of the format
; see Section 3.3 of [RFC5322]
day-name = %s"Mon" / %s"Tue" / %s"Wed"
/ %s"Thu" / %s"Fri" / %s"Sat" / %s"Sun"
date1 = day SP month SP year
; e.g., 02 Jun 1982
day = 2DIGIT
month = %s"Jan" / %s"Feb" / %s"Mar" / %s"Apr"
/ %s"May" / %s"Jun" / %s"Jul" / %s"Aug"
/ %s"Sep" / %s"Oct" / %s"Nov" / %s"Dec"
year = 4DIGIT
GMT = %s"GMT"
time-of-day = hour ":" minute ":" second
; 00:00:00 - 23:59:60 (leap second)
hour = 2DIGIT
minute = 2DIGIT
second = 2DIGIT
폐기된 형식:
obs-date = rfc850-date / asctime-date
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
date2 = day "-" month "-" 2DIGIT
; e.g., 02-Jun-82
day-name-l = %s"Monday" / %s"Tuesday" / %s"Wednesday"
/ %s"Thursday" / %s"Friday" / %s"Saturday"
/ %s"Sunday"
asctime-date = day-name SP date3 SP time-of-day SP year
date3 = month SP ( 2DIGIT / ( SP 1DIGIT ))
; e.g., Jun 2
HTTP-date는 대소문자를 구분한다. [CACHING]의 4.2절은 캐시 수신자에 대해 이 조건을 완화한다는 점에 유의한다.
송신자는 HTTP-date 안에 문법에서 SP로 명시한 것 외의 추가 공백을 생성해서는 안 된다(MUST NOT). day-name, day, month, year, time-of-day의 의미는 인터넷 메시지 형식에서 같은 이름의 구문에 대해 정의된 의미([RFC5322]의 3.3절)와 같다.
두 자리 연도를 쓰는 rfc850-date 형식의 타임스탬프 값을 받은 수신자는, 50년 넘게 미래인 것처럼 보이는 타임스탬프를 끝 두 자리가 같은 과거의 가장 최근 연도로 반드시 해석해야 한다(MUST).
타임스탬프 값을 받는 수신자는 필드 정의가 달리 제한하지 않는 한 타임스탬프를 강건하게 파싱할 것이 권장된다. 예를 들어 메시지가 인터넷 메시지 형식에 정의된 어떤 날짜·시간 명세든 생성할 수 있는, HTTP가 아닌 원천으로부터 HTTP를 통해 전달되는 경우가 가끔 있다.
참고: 타임스탬프 형식에 대한 HTTP 요구 사항은 프로토콜 스트림 안에서 쓰일 때에만 적용된다. 구현이 사용자에게 보여 주거나 요청을 로그에 기록하는 등의 용도에 이 형식을 써야 하는 것은 아니다.
역주: 5장의 “ought to”(5.1·5.3·5.5·5.6.7절), “need to”(5.5절), “has to”(5.6.1.1절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 5.6.7절의 “are encouraged to”도 대문자 키워드가 아니므로 “권장된다”로 옮기되 괄호를 붙이지 않았다. ABNF 안의 영어 주석(
; e.g., …,; see Section …)은 문법의 일부이므로 그대로 두었다.
HTTP의 각 주 버전은 메시지를 전달하기 위한 자체 구문을 정의한다. 이 절은 그런 메시지의 특성, 공통 구조, 의미를 전달하는 능력을 일반화하여 HTTP 메시지에 대한 추상 데이터형을 정의한다. 이 추상화는 HTTP 버전과 무관한 송신자·수신자 요구 사항을 정의하는 데 쓰이며, 한 버전의 메시지가 의미를 바꾸지 않고 다른 버전을 거쳐 중계될 수 있게 한다.
“메시지(message)”는 다음으로 이루어진다.
프레이밍과 제어 데이터를 먼저 보내고, 그 뒤에 헤더 테이블에 들어갈 필드를 담은 헤더 섹션을 보낸다. 메시지에 콘텐츠가 있으면 콘텐츠는 헤더 섹션 뒤에 보내며, 그 뒤에 트레일러 테이블에 들어갈 필드를 담을 수 있는 트레일러 섹션이 올 수 있다.
메시지는 스트림으로 처리되는 것을 전제로 하며, 그 스트림의 목적과 이후 처리 방법은 읽는 동안 드러난다. 따라서 제어 데이터는 수신자가 즉시 알아야 할 것을, 헤더 필드는 콘텐츠를 받기 전에 알아야 할 것을 설명하고, 콘텐츠(있다면)는 아마도 수신자가 메시지의 의미를 이루기 위해 원하거나 필요로 하는 것을 담으며, 트레일러 필드는 콘텐츠를 보내기 전에는 알 수 없었던 선택적 메타데이터를 제공한다.
메시지는 “자기 서술적(self-descriptive)”이도록 의도된다. 즉, 전송 중에 압축되거나 생략된 부분을 디코딩하거나 재구성한 뒤라면, 수신자가 그 메시지에 대해 알아야 할 모든 것을 메시지 자체만 보고 판단할 수 있으며, (이전 메시지를 통해 맺어진) 송신자의 현재 애플리케이션 상태를 이해할 필요가 없다. 그러나 클라이언트는 그에 대응하는 응답을 파싱하거나, 해석하거나, 캐시할 때 요청에 대한 정보를 반드시 유지해야 한다(MUST). 예를 들어 HEAD 메서드에 대한 응답은 GET에 대한 응답의 앞부분과 똑같아 보이지만 같은 방식으로 파싱할 수는 없다.
이 메시지 추상화는 여러 HTTP 버전에 걸친 일반화이므로, 일부 버전에는 없는 기능도 포함한다는 점에 유의한다. 예를 들어 트레일러는 HTTP/1.1의 청크 전송 코딩(chunked transfer coding)에서 콘텐츠 뒤에 오는 트레일러 섹션으로 처음 도입되었다. HTTP/2와 HTTP/3에는 각 스트림을 끝내는 헤더 블록 안에 이와 동등한 기능이 있다.
메시지 프레이밍(message framing)은 각 메시지가 어디서 시작하고 어디서 끝나는지를 나타내어, 같은 연결 위의 다른 메시지나 잡음과 구별할 수 있게 한다. HTTP의 각 주 버전은 자체 프레이밍 메커니즘을 정의한다.
HTTP/0.9와 초기에 배포된 HTTP/1.0은 기반이 되는 연결을 닫는 것으로 응답을 끝냈다. 하위 호환성을 위해 HTTP/1.1에서도 이런 암묵적 프레이밍이 허용된다. 그러나 암묵적 프레이밍은 연결이 일찍 닫히면 불완전한 응답을 구별하지 못할 수 있다. 그래서 거의 모든 현대 구현은 길이로 경계를 정한 메시지 데이터 시퀀스 형태의 명시적 프레이밍을 사용한다.
메시지는 프레이밍이 나타내는 모든 옥텟을 사용할 수 있을 때 “완전하다(complete)”고 본다. 명시적 프레이밍을 쓰지 않을 때, 기반이 되는 연결이 닫혀서 끝난 응답 메시지는 불완전한 응답과 구별되지 않을 수도 있지만, 전송 계층 오류가 완전하지 않음을 나타내지 않는 한 완전한 것으로 본다는 점에 유의한다.
메시지는 그 주된 목적을 설명하는 제어 데이터로 시작한다. 요청 메시지의 제어 데이터에는 요청 메서드(9장), 요청 대상(7.1절), 프로토콜 버전(2.5절)이 들어 있다. 응답 메시지의 제어 데이터에는 상태 코드(15장), 선택적인 이유 구문(reason phrase), 프로토콜 버전이 들어 있다.
HTTP/1.1([HTTP/1.1]) 이하에서는 제어 데이터를 메시지의 첫 줄로 보낸다. HTTP/2([HTTP/2])와 HTTP/3([HTTP/3])에서는 제어 데이터를 예약된 이름 접두사(예: :authority)를 가진 의사 헤더 필드로 보낸다.
모든 HTTP 메시지에는 프로토콜 버전이 있다. 사용 중인 버전에 따라 메시지 안에 명시적으로 표시되거나, 메시지를 받은 연결로부터 추론될 수 있다. 수신자는 그 버전 정보를 사용해 그 송신자와의 이후 통신에서의 제약이나 가능성을 판단한다.
중개자가 메시지를 전달할 때는 프로토콜 버전을 그 중개자가 사용한 버전을 반영하도록 갱신한다. Via 헤더 필드(7.6.3절)는 전달되는 메시지 안에서 업스트림의 프로토콜 정보를 전달하는 데 쓰인다.
클라이언트는 자신이 지원하는 가장 높은 버전이면서, 서버가 지원하는 가장 높은 버전을 알 경우 주 버전이 그보다 높지 않은 버전을 요청 버전으로 보내야 한다(SHOULD). 클라이언트는 자신이 지원하지 않는 버전을 보내서는 안 된다(MUST NOT).
클라이언트는 서버가 HTTP 명세를 잘못 구현했다는 것이 알려진 경우 더 낮은 요청 버전을 보낼 수 있다(MAY). 단, 적어도 한 번은 정상적인 요청을 시도해 보고, 응답 상태 코드나 헤더 필드(예: Server)로부터 서버가 더 높은 요청 버전을 제대로 처리하지 못한다고 판단한 뒤에만 그럴 수 있다.
서버는 자신이 지원하는 가장 높은 버전이면서, 요청에서 받은 주 버전보다 주 버전이 같거나 낮은 버전을 응답 버전으로 보내야 한다(SHOULD). 서버는 자신이 지원하지 않는 버전을 보내서는 안 된다(MUST NOT). 서버는 어떤 이유로든 클라이언트의 주 프로토콜 버전에 대한 서비스를 거부하려면 505 (HTTP Version Not Supported) 응답을 보낼 수 있다.
자신이 구현한 주 버전 번호와, 자신이 구현한 것보다 높은 부 버전 번호를 가진 메시지를 받은 수신자는, 그 메시지를 그 주 버전 안에서 자신이 지원하는 가장 높은 부 버전인 것처럼 처리해야 한다(SHOULD). 수신자는 더 높은 부 버전을 지원한다고 아직 알리지 않은 수신자에게 보내진, 더 높은 부 버전의 메시지가, 같은 주 버전의 어떤 구현이든 안전하게 처리할 수 있을 만큼 하위 호환된다고 가정할 수 있다.
콘텐츠보다 먼저 보내거나 받는 필드(5장)를 “헤더 필드(header field)”(흔히 줄여서 “헤더”)라고 한다.
메시지의 “헤더 섹션(header section)”은 헤더 필드 줄의 시퀀스로 이루어진다. 각 헤더 필드는 메시지 의미를 수정하거나 확장하고, 송신자를 설명하고, 콘텐츠를 정의하거나, 추가 맥락을 제공할 수 있다.
참고: 헤더 섹션에서만 보낼 수 있는 이름 있는 필드를 특별히 “헤더 필드”라고 부른다.
HTTP 메시지는 흔히 완전한 표현이나 부분 표현을 메시지의 “콘텐츠(content)”로 전송한다. 콘텐츠는 메시지 프레이밍이 경계를 정한, 헤더 섹션 뒤에 보내는 옥텟 스트림이다.
콘텐츠의 이 추상적 정의는 메시지 프레이밍에서 추출한 뒤의 데이터를 반영한다. 예를 들어 HTTP/1.1 메시지 본문([HTTP/1.1]의 6장)은 청크 전송 코딩으로 인코딩된 데이터 스트림, 즉 데이터 청크의 시퀀스, 길이가 0인 청크 하나, 트레일러 섹션으로 이루어질 수 있다. 반면 같은 메시지의 콘텐츠에는 전송 코딩을 디코딩한 뒤의 데이터 스트림만 들어 있으며, 청크 길이, 청크 프레이밍 구문, 트레일러 필드(6.5절)는 들어 있지 않다.
참고: 일부 필드 이름에는 “Content-“ 접두사가 있다. 이는 비공식적인 관례다. 이런 필드 중 일부는 위에서 정의한 메시지 콘텐츠를 가리키지만, 일부는 선택된 표현(3.2절)을 범위로 한다. 각 필드의 정의를 보고 구별한다.
요청에서 콘텐츠의 목적은 메서드의 의미(9장)가 정의한다.
예를 들어 PUT 요청(9.3.4절)의 콘텐츠에 담긴 표현은 요청이 성공적으로 적용된 뒤 대상 리소스가 가질 원하는 상태를 나타내는 반면, POST 요청(9.3.3절)의 콘텐츠에 담긴 표현은 대상 리소스가 처리할 정보를 나타낸다.
응답에서 콘텐츠의 목적은 요청 메서드, 응답 상태 코드(15장), 그리고 그 콘텐츠를 설명하는 응답 필드가 정의한다. 예를 들어 GET(9.3.1절)에 대한 200 (OK) 응답의 콘텐츠는 메시지 생성 일시(6.6.1절) 시점에 관찰한 대상 리소스의 현재 상태를 나타내는 반면, POST에 대한 응답에서 같은 상태 코드의 콘텐츠는 처리 결과나, 처리를 적용한 뒤 대상 리소스의 새 상태를 나타낼 수 있다.
GET에 대한 206 (Partial Content) 응답의 콘텐츠에는 15.3.7절에 설명된 대로 선택된 표현의 한 부분, 또는 그 표현의 여러 부분을 담은 멀티파트 메시지 본문이 들어 있다.
오류 상태 코드를 가진 응답 메시지에는 보통 오류 상황을 나타내는 콘텐츠가 들어 있으며, 그 콘텐츠는 오류 상태와 해결을 위해 권하는 단계를 설명한다.
HEAD 요청 메서드(9.3.2절)에 대한 응답에는 콘텐츠가 전혀 없다. 관련 응답 헤더 필드는 요청 메서드가 GET이었다면 가졌을 값만 나타낸다(9.3.1절).
CONNECT 요청 메서드(9.3.6절)에 대한 2xx (Successful) 응답은 콘텐츠를 갖는 대신 연결을 터널 모드로 바꾼다.
모든 1xx (Informational), 204 (No Content), 304 (Not Modified) 응답에는 콘텐츠가 없다.
그 밖의 모든 응답에는 콘텐츠가 있다. 다만 그 콘텐츠의 길이가 0일 수는 있다.
완전한 표현이나 부분 표현을 메시지 콘텐츠로 전송할 때, 송신자가 그 특정 표현에 대응하는 리소스의 식별자를 제공하거나 수신자가 그것을 판단하는 것이 바람직한 경우가 많다. 예를 들어 “현재 날씨 예보” 리소스에 GET 요청을 하는 클라이언트는 반환된 콘텐츠에 특정한 식별자(예: “20210720T1711의 라구나 비치 날씨 예보”)를 원할 수 있다. 이는 시간이 지나면서 표현이 바뀔 것으로 예상되는 리소스의 콘텐츠를 공유하거나 북마크할 때 유용할 수 있다.
요청 메시지의 경우:
Content-Location 헤더 필드가 있으면, 송신자는 콘텐츠가 Content-Location 필드 값으로 식별되는 리소스의 표현이라고 주장하는 것이다. 그러나 그런 주장은 (이 명세가 정의하지 않은) 다른 수단으로 검증할 수 없다면 신뢰할 수 없다. 그래도 그 정보는 개정 이력 링크에는 유용할 수 있다.응답 메시지의 경우, 다음 규칙을 일치하는 것이 나올 때까지 차례로 적용한다.
HEAD이거나 응답 상태 코드가 204 (No Content) 또는 304 (Not Modified)이면, 응답에 콘텐츠가 없다.GET이고 응답 상태 코드가 200 (OK)이면, 콘텐츠는 대상 리소스(7.1절)의 표현이다.GET이고 응답 상태 코드가 203 (Non-Authoritative Information)이면, 콘텐츠는 중개자가 제공한, 수정되었거나 보강되었을 수 있는 대상 리소스의 표현이다.GET이고 응답 상태 코드가 206 (Partial Content)이면, 콘텐츠는 대상 리소스 표현의 하나 이상의 부분이다.Content-Location 헤더 필드가 있고 그 필드 값이 대상 URI와 같은 URI에 대한 참조이면, 콘텐츠는 대상 리소스의 표현이다.Content-Location 헤더 필드가 있고 그 필드 값이 대상 URI와 다른 URI에 대한 참조이면, 송신자는 콘텐츠가 Content-Location 필드 값으로 식별되는 리소스의 표현이라고 주장하는 것이다. 그러나 그런 주장은 (이 명세가 정의하지 않은) 다른 수단으로 검증할 수 없다면 신뢰할 수 없다.“트레일러 섹션(trailer section)” 안에 있는 필드(5장)를 “트레일러 필드(trailer field)”(흔히 줄여서 “트레일러”)라고 한다. 트레일러 필드는 메시지 무결성 검사, 전자 서명, 전달 지표, 후처리 상태 정보를 제공하는 데 유용할 수 있다.
트레일러 필드는 헤더 섹션이 완료된 시점에 알려진 메시지 의미와 모순되지 않도록, 헤더 섹션의 필드와 따로 처리하고 저장해야 마땅하다. 트레일러를 받기 전에, 특정 헤더 필드가 있는지 없는지가 메시지 전체의 라우팅이나 처리에 관한 선택에 영향을 줄 수 있다. 나중에 트레일러 필드를 발견해도 그 선택을 되돌릴 수는 없다.
트레일러 섹션은 사용 중인 HTTP 버전이 지원하고 명시적 프레이밍 메커니즘이 가능하게 할 때에만 쓸 수 있다. 예를 들어 HTTP/1.1의 청크 전송 코딩은 콘텐츠 뒤에 트레일러 섹션을 보낼 수 있게 한다([HTTP/1.1]의 7.1.2절).
메시지 프레이밍, 라우팅, 인증, 요청 수정자, 응답 제어, 콘텐츠 형식을 설명하는 필드처럼, 많은 필드는 콘텐츠를 받기 전에 평가해야 하므로 헤더 섹션 밖에서는 처리할 수 없다. 송신자는 해당 헤더 필드 이름의 정의가 그 필드를 트레일러로 보내는 것을 허용한다는 것을 알지 못하는 한 트레일러 필드를 생성해서는 안 된다(MUST NOT).
메시지를 한 프로토콜 버전에서 다른 버전으로 전달하는 중개자는 트레일러 필드를 처리하기 어려울 수 있다. 전송 중에 메시지 전체를 버퍼에 담을 수 있다면, 일부 중개자는 전달하기 전에 트레일러 필드를 (적절한 경우) 헤더 섹션에 병합할 수 있다. 그러나 대부분의 경우 트레일러는 그냥 버려진다. 수신자는 해당 헤더 필드 정의를 이해하고, 그 정의가 트레일러 필드 값을 안전하게 병합할 수 있음을 명시적으로 허용하고 그 방법을 정의하지 않는 한, 트레일러 필드를 헤더 섹션에 병합해서는 안 된다(MUST NOT).
요청의 TE 헤더 필드(10.1.4절)에 키워드 “trailers”가 있으면, 클라이언트가 자신과 모든 다운스트림 클라이언트를 대신해 트레일러 필드를 받을 의향이 있음을 나타낸다. 중개자가 보낸 요청이라면, 이는 모든 다운스트림 클라이언트가 전달된 응답에서 트레일러 필드를 받을 의향이 있다는 뜻이다. “trailers”가 있다고 해서 클라이언트가 응답의 특정 트레일러 필드를 처리한다는 뜻은 아니며, 어떤 클라이언트도 트레일러 섹션을 버리지 않는다는 뜻일 뿐이라는 점에 유의한다.
트레일러 필드는 전송 중에 버려질 수 있으므로, 서버는 사용자 에이전트가 꼭 받아야 한다고 생각하는 트레일러 필드를 생성하지 않아야 한다(SHOULD NOT).
Trailer 헤더 필드(6.6.2절)는 트레일러 섹션에서 보낼 가능성이 있는 필드를 알리기 위해 보낼 수 있으며, 이를 통해 수신자는 콘텐츠를 처리하기 전에 그 필드를 받을 준비를 할 수 있다. 예를 들어 어떤 필드 이름이, 콘텐츠를 받는 동안 동적 체크섬을 계산했다가 트레일러 필드 값을 받는 즉시 확인해야 함을 나타낸다면 이 방식이 유용할 수 있다.
헤더 필드와 마찬가지로, 같은 이름의 트레일러 필드는 받은 순서대로 처리한다. 같은 이름의 트레일러 필드 줄 여러 개는 여러 값을 구성원 목록으로 덧붙인 것과 같은 의미를 갖는다. 한 메시지 동안 두 번 이상 생성될 수 있는 트레일러 필드는, 받은 필드 줄마다 각 구성원 값을 한 번만 처리하더라도 반드시 목록 기반 필드로 정의해야 한다(MUST).
메시지가 끝나면 수신자는 받은 트레일러 필드 집합을 헤더 필드와 비슷한 (그러나 헤더 필드와는 별개인) 이름/값 쌍 데이터 구조로 다룰 수 있다(MAY). 추가 처리 방식이 필요하다면, 트레일러에서 쓰도록 의도된 필드의 명세 안에서 정의할 수 있다.
메시지가 언제, 어떻게 생성되었는지처럼 메시지 자체를 설명하는 필드는 요청과 응답 모두에 나타날 수 있다.
“Date” 헤더 필드는 메시지가 생성된 날짜와 시간을 나타내며, [RFC5322]의 3.6.1절에 정의된 생성 일시 필드(Origination Date Field, orig-date)와 같은 의미를 갖는다. 필드 값은 5.6.7절에 정의된 HTTP-date다.
Date = HTTP-date
예:
Date: Tue, 15 Nov 1994 08:12:31 GMT
Date 헤더 필드를 생성하는 송신자는 그 필드 값을 메시지 생성 날짜와 시간에 대해 얻을 수 있는 가장 좋은 근삿값으로 생성해야 한다(SHOULD). 이론적으로 날짜는 메시지 콘텐츠를 생성하기 직전의 순간을 나타내야 마땅하다. 실제로는 송신자가 메시지를 생성하는 동안 언제든 날짜 값을 생성할 수 있다.
시계(5.6.7절에 정의)가 있는 원본 서버는 모든 2xx (Successful), 3xx (Redirection), 4xx (Client Error) 응답에 Date 헤더 필드를 반드시 생성해야 하며(MUST), 1xx (Informational)와 5xx (Server Error) 응답에는 Date 헤더 필드를 생성할 수 있다(MAY).
시계가 없는 원본 서버는 Date 헤더 필드를 생성해서는 안 된다(MUST NOT).
Date 헤더 필드가 없는 응답 메시지를 받은 시계가 있는 수신자는, 그 메시지를 캐시하거나 다운스트림으로 전달한다면 받은 시간을 반드시 기록하고 그에 해당하는 Date 헤더 필드를 메시지의 헤더 섹션에 덧붙여야 한다(MUST).
유효하지 않은 Date 헤더 필드 값을 가진 응답을 받은 시계가 있는 수신자는 그 값을 응답을 받은 시간으로 바꿀 수 있다(MAY).
사용자 에이전트는 요청에 Date 헤더 필드를 보낼 수 있다(MAY). 다만 서버에 유용한 정보를 전달한다고 판단되지 않는 한 보통은 보내지 않는다. 예를 들어 사용자 에이전트와 서버의 시계 차이에 따라 서버가 사용자의 요청 해석을 조정할 것으로 예상되는 맞춤형 HTTP 애플리케이션은 Date를 전달할 수 있다.
“Trailer” 헤더 필드는 송신자가 그 메시지 안에서 트레일러 필드로 보낼 것으로 예상하는 필드 이름의 목록을 제공한다. 이를 통해 수신자는 콘텐츠 처리를 시작하기 전에 지정된 메타데이터를 받을 준비를 할 수 있다.
Trailer = #field-name
예를 들어 송신자는 콘텐츠를 스트리밍하는 동안 서명을 계산하고, 최종 서명을 트레일러 필드로 제공할 것임을 알릴 수 있다. 이를 통해 수신자는 콘텐츠를 받으면서 같은 검사를 즉석에서 수행할 수 있다.
메시지에 하나 이상의 트레일러 필드를 생성하려는 송신자는 그 메시지의 헤더 섹션에 Trailer 헤더 필드를 생성하여 어떤 필드가 트레일러에 있을 수 있는지 알려야 한다(SHOULD).
중개자가 전송 중에 트레일러 섹션을 버리면, Trailer 필드는 어떤 메타데이터가 사라졌는지 알려 주는 단서가 될 수 있다. 다만 Trailer를 보낸 송신자가 언제나 지정한 필드를 실제로 보낸다는 보장은 없다.
역주: 6장의 “ought to”(6.5절, 6.6.1절)는 대문자 키워드가 아니므로 “~해야 마땅하다”로 옮기고 괄호를 붙이지 않았다. 6.6.1절과 6.6.2절의 제목은 헤더 필드 이름이므로 원어 그대로 두었다.
HTTP 요청 메시지의 라우팅은 각 클라이언트가 대상 리소스, 클라이언트의 프록시 설정, 인바운드 연결의 수립이나 재사용에 따라 정한다. 그에 대응하는 응답의 라우팅은 같은 연결 사슬을 거슬러 클라이언트로 돌아간다.
HTTP는 매우 다양한 애플리케이션에서 쓰이지만, 대부분의 클라이언트는 범용 웹 브라우저와 같은 리소스 식별 메커니즘과 설정 기법에 의존한다. 통신 옵션이 클라이언트 설정에 하드코딩되어 있더라도, 그 옵션들이 합쳐진 효과는 URI 참조(4.1절)로 생각할 수 있다.
URI 참조는 “대상 URI(target URI)”를 얻기 위해 절대 형식으로 해석된다. 프래그먼트 식별자는 클라이언트 측 처리를 위해 예약되어 있으므로([URI]의 3.5절), 대상 URI에서는 참조의 프래그먼트 구성 요소(있다면)를 제외한다.
“대상 리소스(target resource)”에 어떤 동작을 수행하기 위해, 클라이언트는 파싱한 대상 URI의 구성 요소 중 수신자가 같은 리소스를 식별할 수 있을 만큼을 담은 요청 메시지를 보낸다. 역사적인 이유로, 파싱한 대상 URI 구성 요소들은 통틀어 “요청 대상(request target)”이라 부르며, 메시지의 제어 데이터와 Host 헤더 필드(7.2절)에 담아 보낸다.
요청 대상 구성 요소가 메서드 고유의 형식을 갖는 특이한 경우가 두 가지 있다.
CONNECT(9.3.6절)에서 요청 대상은 콜론으로 구분한, 터널 목적지의 호스트 이름과 포트 번호다.OPTIONS(9.3.7절)에서 요청 대상은 별표(“*”) 하나일 수 있다.자세한 내용은 각 메서드의 정의를 참고한다. 이 형식은 다른 메서드와 함께 사용해서는 안 된다(MUST NOT).
서버는 클라이언트의 요청을 받으면, 자신의 로컬 설정과 들어온 연결의 맥락에 따라 받은 구성 요소로부터 대상 URI를 재구성한다. 이 재구성 방식은 주 프로토콜 버전마다 다르다. 예를 들어 [HTTP/1.1]의 3.3절은 서버가 HTTP/1.1 요청의 대상 URI를 어떻게 정하는지 정의한다.
참고: 이전 명세들은 재구성된 대상 URI를 “유효 요청 URI(effective request URI)”라는 별개의 개념으로 정의했다.
요청의 “Host” 헤더 필드는 대상 URI의 호스트와 포트 정보를 제공하여, 원본 서버가 여러 호스트 이름에 대한 요청을 처리하면서 리소스를 구별할 수 있게 한다.
HTTP/2[HTTP/2]와 HTTP/3[HTTP/3]에서는 경우에 따라 Host 헤더 필드 대신 요청 제어 데이터의 :authority 의사 헤더 필드를 쓴다.
Host = uri-host [ ":" port ] ; Section 4
대상 URI의 authority 정보는 요청을 처리하는 데 매우 중요하다. 사용자 에이전트는 그 정보를 :authority 의사 헤더 필드로 보내는 경우가 아니라면 요청에 Host 헤더 필드를 반드시 생성해야 한다(MUST). Host를 보내는 사용자 에이전트는 요청 헤더 섹션의 첫 필드로 보내야 한다(SHOULD).
예를 들어 http://www.example.org/pub/WWW/에 대해 원본 서버로 보내는 GET 요청은 다음과 같이 시작한다.
GET /pub/WWW/ HTTP/1.1
Host: www.example.org
호스트와 포트 정보는 애플리케이션 수준의 라우팅 메커니즘으로 작용하므로, 공유 캐시를 중독시키거나 요청을 의도하지 않은 서버로 리다이렉트하려는 악성 소프트웨어의 단골 표적이 된다. 가로채기 프록시가 가로챈 연결이 그 호스트의 유효한 IP 주소를 대상으로 하는지 먼저 확인하지 않은 채, 호스트와 포트 정보를 내부 서버로 요청을 리다이렉트하는 데 쓰거나 공유 캐시의 캐시 키로 쓴다면 특히 취약하다.
대상 URI와 그 출처가 정해지면, 클라이언트는 원하는 의미를 이루기 위해 네트워크 요청이 필요한지, 필요하다면 그 요청을 어디로 보낼지 정한다.
클라이언트에 캐시[CACHING]가 있고 그 캐시로 요청을 처리할 수 있다면, 보통 요청을 먼저 캐시로 보낸다.
캐시로 요청을 처리할 수 없으면, 일반적인 클라이언트는 설정을 확인해 요청을 처리하는 데 프록시를 쓸지 정한다. 프록시 설정은 구현에 따라 다르지만, URI 접두사 일치, 선택적인 authority 일치, 또는 둘 다에 기반하는 경우가 많으며, 프록시 자체는 보통 “http”나 “https” URI로 식별된다.
“http”나 “https” 프록시를 적용할 수 있으면, 클라이언트는 그 프록시로 연결을 맺거나 기존 연결을 재사용하여 인바운드로 연결한 다음, 클라이언트의 대상 URI와 일치하는 요청 대상을 담은 HTTP 요청 메시지를 그 프록시로 보낸다.
적용할 수 있는 프록시가 없으면, 일반적인 클라이언트는 식별된 리소스에 접근하기 위해 (대상 URI의 스킴에 특화된) 처리 루틴을 호출한다. 그 방법은 대상 URI 스킴에 따라 다르며, 그 스킴에 관련된 명세가 정의한다.
4.3.2절은 식별된 원본 서버로 인바운드 연결을 맺고(또는 재사용하고), 클라이언트의 대상 URI와 일치하는 요청 대상을 담은 HTTP 요청 메시지를 보내 “http” 리소스에 접근하는 방법을 정의한다.
4.3.3절은 식별된 출처에 대해 권한 있는 원본 서버로 보안 처리된 인바운드 연결을 맺고(또는 재사용하고), 클라이언트의 대상 URI와 일치하는 요청 대상을 담은 HTTP 요청 메시지를 보내 “https” 리소스에 접근하는 방법을 정의한다.
서버가 요청을 받아 대상 URI를 정할 수 있을 만큼 파싱하면, 서버는 그 요청을 직접 처리할지, 다른 서버로 전달할지, 클라이언트를 다른 리소스로 리다이렉트할지, 오류로 응답할지, 연결을 끊을지 결정한다. 이 결정은 요청이나 연결 맥락의 어떤 요소에든 영향을 받을 수 있지만, 특히 서버가 그 대상 URI에 대한 요청을 처리하도록 설정되어 있는지, 그리고 연결 맥락이 그 요청에 적절한지를 따진다.
예를 들어 요청이 의도적으로든 실수로든 잘못 전달되어, 받은 Host 헤더 필드의 정보가 연결의 호스트나 포트와 다를 수 있다. 연결이 신뢰할 수 있는 게이트웨이에서 온 것이라면 그런 불일치가 예상될 수 있지만, 그렇지 않다면 보안 필터를 우회하거나, 서버를 속여 비공개 콘텐츠를 전달하게 하거나, 캐시를 중독시키려는 시도를 나타낼 수 있다. 메시지 라우팅에 관한 보안 고려 사항은 17장을 참고한다.
연결이 신뢰할 수 있는 게이트웨이에서 온 것이 아니라면, 원본 서버는 대상 URI에 대한 스킴별 요구 사항이 충족되지 않을 때 그 요청을 반드시 거부해야 한다(MUST). 특히 “https” 리소스에 대한 요청은 4.2.2절에 정의된 대로, 그 대상 URI의 출처에 대해 유효한 인증서로 보안 처리된 연결로 받은 것이 아니면 반드시 거부해야 한다(MUST).
응답의 421 (Misdirected Request) 상태 코드는 원본 서버가 요청이 잘못 전달된 것으로 보여 거부했음을 나타낸다(15.5.20절).
하나의 연결은 여러 요청/응답 교환에 쓰일 수 있다. 요청 메시지와 응답 메시지를 대응시키는 메커니즘은 버전마다 다르다. 일부 HTTP 버전은 메시지의 암묵적 순서를 쓰고, 다른 버전은 명시적 식별자를 쓴다.
모든 응답은 상태 코드와 상관없이(중간 응답 포함), 요청을 받은 뒤라면 요청이 아직 완료되지 않았더라도 언제든 보낼 수 있다. 응답이 그에 대응하는 요청보다 먼저 완료될 수 있다(6.1절). 마찬가지로 클라이언트가 응답을 위해 특정 시간 동안 기다릴 것으로 예상되지는 않는다. 클라이언트(중개자 포함)는 적당한 시간 안에 응답을 받지 못하면 요청을 포기할 수 있다.
관련 요청을 아직 보내는 중에 응답을 받은 클라이언트는, 반대로 하라는 명시적 표시(예: [HTTP/1.1]의 9.5절과 [HTTP/2]의 6.4절 참고)를 받지 않는 한 그 요청을 계속 보내야 한다(SHOULD).
3.7절에서 설명했듯이, 중개자는 HTTP 요청과 응답을 처리하는 데 다양한 역할을 할 수 있다. 일부 중개자는 성능이나 가용성을 높이는 데 쓰이고, 다른 중개자는 접근 제어나 콘텐츠 필터링에 쓰인다. HTTP 스트림은 파이프 앤드 필터(pipe-and-filter) 아키텍처와 비슷한 특성을 가지므로, 중개자가 스트림의 어느 방향이든 개선(또는 간섭)할 수 있는 정도에 본질적인 한계는 없다.
중개자는 프로토콜 요소(예: 새 메서드, 상태 코드, 필드 이름)를 인식하지 못하더라도 메시지를 전달할 것으로 예상된다. 그래야 다운스트림 수신자의 확장성이 유지되기 때문이다.
터널로 동작하지 않는 중개자는 7.6.1절에 명시된 대로 Connection 헤더 필드를 반드시 구현해야 하며, 들어온 연결만을 위한 필드는 전달에서 반드시 제외해야 한다(MUST).
중개자는 무한 요청 루프로부터 보호되지 않는 한 메시지를 자기 자신에게 전달해서는 안 된다(MUST NOT). 일반적으로 중개자는 별칭, 로컬 변형, IP 주소 리터럴을 포함해 자기 서버 이름을 인식하고 그런 요청에 직접 응답해야 마땅하다.
HTTP 메시지는 점진적인 처리나 다운스트림 전달을 위해 스트림으로 파싱할 수 있다. 그러나 일부 구현은 네트워크 효율, 보안 검사, 콘텐츠 변형을 위해 메시지 전달을 버퍼에 담거나 지연시키므로, 송신자와 수신자는 부분 메시지가 점진적으로 전달되리라고 기대할 수 없다.
“Connection” 헤더 필드는 송신자가 현재 연결에 원하는 제어 옵션을 나열할 수 있게 한다.
Connection = #connection-option
connection-option = token
연결 옵션은 대소문자를 구분하지 않는다.
Connection 외의 필드를 현재 연결을 위한 또는 현재 연결에 관한 제어 정보를 제공하는 데 쓸 때, 송신자는 해당 필드 이름을 Connection 헤더 필드 안에 반드시 나열해야 한다(MUST). 일부 HTTP 버전은 그런 정보에 필드를 쓰는 것을 금지하므로 Connection 필드를 허용하지 않는다는 점에 유의한다.
중개자는 메시지를 전달하기 전에 받은 Connection 헤더 필드를 반드시 파싱해야 하며, 이 필드의 각 connection-option에 대해 그 연결 옵션과 이름이 같은 헤더 필드나 트레일러 필드를 메시지에서 제거한 다음, Connection 헤더 필드 자체를 제거해야 한다(MUST)(또는 전달하는 메시지를 위한 중개자 자신의 제어 옵션으로 바꿔야 한다).
따라서 Connection 헤더 필드는 바로 다음 수신자만을 위한 필드(“홉 단위(hop-by-hop)”)와 사슬 위의 모든 수신자를 위한 필드(“종단 간(end-to-end)”)를 선언적으로 구별하는 방법을 제공한다. 이로써 메시지가 자기 서술적일 수 있고, 앞으로 연결별 확장을 배치할 때 오래된 중개자가 그 확장을 맹목적으로 전달할까 걱정하지 않아도 된다.
또한 중개자는 전달하기 전에 제거해야 한다고 알려진 필드를, 연결 옵션으로 나타나든 아니든 그 필드의 의미를 적용한 뒤 제거하거나 바꿔야 한다(SHOULD). 여기에는 다음이 포함되며, 이것에 한정되지 않는다.
Proxy-Connection([HTTP/1.1]의 부록 C.2.2)Keep-Alive([RFC2068]의 19.7.1절)TE(10.1.4절)Transfer-Encoding([HTTP/1.1]의 6.1절)Upgrade(7.8절)송신자는 콘텐츠의 모든 수신자를 위한 필드에 해당하는 연결 옵션을 보내서는 안 된다(MUST NOT). 예를 들어 Cache-Control은 결코 연결 옵션으로 적절하지 않다([CACHING]의 5.2절).
연결 옵션이 항상 메시지에 있는 필드에 대응하는 것은 아니다. 연결 옵션에 관련된 매개 변수가 없다면 연결별 필드가 필요하지 않을 수 있기 때문이다. 반면, 대응하는 연결 옵션 없이 받은 연결별 필드는 보통 중개자가 그 필드를 잘못 전달했음을 나타내며, 수신자는 그 필드를 무시해야 마땅하다.
필드에 대응하지 않는 새 연결 옵션을 정의할 때도, 명세 작성자는 나중에 충돌하지 않도록 그에 해당하는 필드 이름을 예약해 두어야 마땅하다. 그렇게 예약한 필드 이름은 “HTTP 필드 이름 레지스트리”(16.3.1절)에 등록한다.
“Max-Forwards” 헤더 필드는 TRACE(9.3.8절)와 OPTIONS(9.3.7절) 요청 메서드에서 요청이 프록시를 거쳐 전달되는 횟수를 제한하는 메커니즘을 제공한다. 이는 클라이언트가 사슬 중간에서 실패하거나 루프에 빠지는 것으로 보이는 요청을 추적하려 할 때 유용할 수 있다.
Max-Forwards = 1*DIGIT
Max-Forwards 값은 이 요청 메시지를 앞으로 전달할 수 있는 남은 횟수를 나타내는 십진 정수다.
Max-Forwards 헤더 필드를 담은 TRACE나 OPTIONS 요청을 받은 각 중개자는 요청을 전달하기 전에 그 값을 반드시 확인하고 갱신해야 한다(MUST). 받은 값이 0이면, 중개자는 요청을 전달해서는 안 되며(MUST NOT), 대신 최종 수신자로서 반드시 응답해야 한다(MUST). 받은 Max-Forwards 값이 0보다 크면, 중개자는 전달하는 메시지에 a) 받은 값에서 1을 뺀 값과 b) 수신자가 지원하는 Max-Forwards 최댓값 중 작은 값을 필드 값으로 하는 갱신된 Max-Forwards 필드를 반드시 생성해야 한다(MUST).
수신자는 그 밖의 요청 메서드와 함께 받은 Max-Forwards 헤더 필드를 무시할 수 있다(MAY).
“Via” 헤더 필드는 이메일의 “Received” 헤더 필드([RFC5322]의 3.6.7절)와 비슷하게, (요청에서는) 사용자 에이전트와 서버 사이 또는 (응답에서는) 원본 서버와 클라이언트 사이에 있는 중간 프로토콜과 수신자의 존재를 나타낸다. Via는 메시지 전달을 추적하고, 요청 루프를 피하고, 요청/응답 사슬 위의 송신자들의 프로토콜 능력을 식별하는 데 쓸 수 있다.
Via = #( received-protocol RWS received-by [ RWS comment ] )
received-protocol = [ protocol-name "/" ] protocol-version
; see Section 7.8
received-by = pseudonym [ ":" port ]
pseudonym = token
Via 필드 값의 각 구성원은 메시지를 전달한 프록시나 게이트웨이 하나를 나타낸다. 각 중개자는 메시지를 어떻게 받았는지에 관한 자신의 정보를 덧붙이므로, 최종 결과는 전달한 수신자들의 순서대로 정렬된다.
프록시는 전달하는 각 메시지에 아래 설명한 대로 적절한 Via 헤더 필드를 반드시 보내야 한다(MUST). HTTP-HTTP 게이트웨이는 각 인바운드 요청 메시지에 적절한 Via 헤더 필드를 반드시 보내야 하며(MUST), 전달하는 응답 메시지에는 Via 헤더 필드를 보낼 수 있다(MAY).
각 중개자에 대해 received-protocol은 메시지의 업스트림 송신자가 사용한 프로토콜과 프로토콜 버전을 나타낸다. 따라서 Via 필드 값은 요청/응답 사슬에서 알린 프로토콜 능력을 다운스트림 수신자에게 보이도록 기록한다. 이는 2.5절에서 설명한 것처럼 응답이나 이후 요청에서 어떤 하위 호환되지 않는 기능을 안전하게 쓸 수 있는지 판단하는 데 유용할 수 있다. 간결하게 하기 위해, 받은 프로토콜이 HTTP이면 protocol-name을 생략한다.
received-by 부분은 보통 메시지를 이어서 전달한 수신 서버나 클라이언트의 호스트와 선택적 포트 번호다. 그러나 실제 호스트가 민감한 정보로 여겨지면 송신자는 그것을 가명(pseudonym)으로 바꿀 수 있다(MAY). 포트가 주어지지 않으면 수신자는 그것을 received-protocol의 기본 포트(있다면)로 받았다는 뜻으로 해석할 수 있다(MAY).
송신자는 User-Agent와 Server 헤더 필드와 비슷하게 각 수신자의 소프트웨어를 식별하는 주석을 생성할 수 있다(MAY). 그러나 Via의 주석은 선택 사항이며, 수신자는 메시지를 전달하기 전에 그 주석을 제거할 수 있다(MAY).
예를 들어 HTTP/1.0 사용자 에이전트가 코드명 “fred”인 내부 프록시로 요청 메시지를 보내고, 그 프록시는 HTTP/1.1을 사용해 p.example.net의 공개 프록시로 요청을 전달하고, 그 공개 프록시는 www.example.com의 원본 서버로 요청을 전달해 마무리할 수 있다. 그러면 www.example.com이 받은 요청에는 다음 Via 헤더 필드가 있다.
Via: 1.0 fred, 1.1 p.example.net
네트워크 방화벽을 통과하는 관문으로 쓰이는 중개자는, 명시적으로 허용되지 않는 한 방화벽 영역 안의 호스트 이름과 포트를 전달하지 않아야 한다(SHOULD NOT). 허용되지 않았다면, 그런 중개자는 방화벽 뒤에 있는 호스트의 각 received-by 호스트를 그 호스트에 맞는 가명으로 바꿔야 한다(SHOULD).
중개자는 received-protocol 값이 같은 항목들이라면, Via 헤더 필드 목록 구성원의 순서 있는 부분 시퀀스를 하나의 구성원으로 결합할 수 있다(MAY). 예를 들어
Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
는 다음과 같이 줄일 수 있다.
Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
송신자는 여러 목록 구성원이 모두 같은 조직의 통제 아래 있고 호스트가 이미 가명으로 바뀐 경우가 아니라면 그 구성원들을 결합하지 않아야 한다(SHOULD NOT). 송신자는 received-protocol 값이 다른 구성원들을 결합해서는 안 된다(MUST NOT).
일부 중개자에는 메시지와 그 콘텐츠를 변형하는 기능이 있다. 예를 들어 프록시는 캐시 공간을 절약하거나 느린 링크의 트래픽을 줄이기 위해 이미지 형식을 변환할 수 있다. 그러나 의료 영상이나 과학 데이터 분석처럼 중요한 애플리케이션을 위한 콘텐츠에 이런 변형을 적용하면 운영상 문제가 생길 수 있으며, 받은 콘텐츠가 원본과 같은지 보장하기 위해 무결성 검사나 전자 서명을 쓰는 경우에는 특히 그렇다.
HTTP-HTTP 프록시가 메시지를 의미상 중요한 방식으로 수정하도록 설계되거나 설정되었다면(즉, 일반적인 HTTP 처리에 필요한 범위를 넘어, 원래 송신자에게 중요하거나 다운스트림 수신자에게 중요할 수 있는 방식으로 메시지를 바꾸는 수정이라면), 그 프록시를 “변형 프록시(transforming proxy)”라고 한다. 예를 들어 변형 프록시는 공유 주석 서버(로컬 주석 데이터베이스에 대한 참조를 넣도록 응답을 수정), 악성 소프트웨어 필터, 형식 트랜스코더, 개인 정보 필터로 동작할 수 있다. 이런 변형은 그 프록시를 선택한 클라이언트(또는 클라이언트 조직)가 원하는 것으로 간주한다.
프록시가 FQDN(fully qualified domain name, 정규화된 도메인 이름)이 아닌 호스트 이름을 가진 대상 URI를 받으면, 요청을 전달할 때 받은 호스트 이름에 자신의 도메인을 덧붙일 수 있다(MAY). 대상 URI에 FQDN이 들어 있으면 프록시는 호스트 이름을 바꿔서는 안 된다(MUST NOT).
프록시는 받은 대상 URI를 다음 인바운드 서버로 전달할 때, 그 전달 프로토콜이 요구하는 경우를 제외하고는 “absolute-path”와 “query” 부분을 수정해서는 안 된다(MUST NOT). 예를 들어 HTTP/1.1로 원본 서버에 요청을 전달하는 프록시는 요청 메서드에 따라 빈 경로를 “/”([HTTP/1.1]의 3.2.1절)나 “*”([HTTP/1.1]의 3.2.4절)로 바꾼다.
프록시는 no-transform 캐시 지시자([CACHING]의 5.2.2.6절)를 담은 응답 메시지의 콘텐츠(6.4절)를 변형해서는 안 된다(MUST NOT). 이는 전송 코딩을 추가하거나 제거하는 것([HTTP/1.1]의 7장)처럼 콘텐츠를 바꾸지 않는 메시지 변형에는 적용되지 않는다는 점에 유의한다.
프록시는 no-transform 캐시 지시자가 없는 메시지의 콘텐츠를 변형할 수 있다(MAY). 200 (OK) 응답의 콘텐츠를 변형하는 프록시는 응답 상태 코드를 203 (Non-Authoritative Information)(15.3.4절)으로 바꾸어 다운스트림 수신자에게 변형이 적용되었음을 알릴 수 있다.
프록시는 통신 사슬의 엔드포인트, 리소스 상태, (콘텐츠 이외의) 선택된 표현에 관한 정보를 제공하는 헤더 필드를, 그 필드의 정의가 그런 수정을 명시적으로 허용하거나 개인 정보 보호나 보안을 위해 수정이 필요하다고 판단되는 경우가 아니라면 수정하지 않아야 한다(SHOULD NOT).
“Upgrade” 헤더 필드는 같은 연결에서 HTTP/1.1을 다른 프로토콜로 전환하는 간단한 메커니즘을 제공하기 위한 것이다.
클라이언트는 서버가 최종 응답을 보내기 전에 나열된 프로토콜 중 하나 이상으로 전환하도록 권하기 위해, 요청의 Upgrade 헤더 필드에 프로토콜 이름 목록을 선호도가 높은 순서대로 보낼 수 있다(MAY). 서버는 그 연결에서 현재 프로토콜을 계속 쓰고 싶다면 받은 Upgrade 헤더 필드를 무시할 수 있다(MAY). Upgrade로 프로토콜 변경을 강요할 수는 없다.
Upgrade = #protocol
protocol = protocol-name ["/" protocol-version]
protocol-name = token
protocol-version = token
프로토콜 이름은 선호하는 대소문자로 등록되지만, 수신자는 각 protocol-name을 지원하는 프로토콜과 비교할 때 대소문자를 구분하지 않는 비교를 해야 한다(SHOULD).
101 (Switching Protocols) 응답을 보내는 서버는 연결이 전환되는 새 프로토콜(들)을 나타내기 위해 Upgrade 헤더 필드를 반드시 보내야 한다(MUST). 여러 프로토콜 계층을 전환한다면, 송신자는 프로토콜을 계층 오름차순으로 반드시 나열해야 한다(MUST). 서버는 해당 요청의 Upgrade 헤더 필드에서 클라이언트가 나타내지 않은 프로토콜로 전환해서는 안 된다(MUST NOT). 서버는 클라이언트가 나타낸 선호 순서를 무시하고, 요청의 성격이나 서버의 현재 부하 같은 다른 요인에 따라 새 프로토콜(들)을 고를 수 있다(MAY).
426 (Upgrade Required) 응답을 보내는 서버는 받아들일 수 있는 프로토콜을 선호도가 높은 순서대로 나타내기 위해 Upgrade 헤더 필드를 반드시 보내야 한다(MUST).
서버는 그 밖의 응답에서도, 이후 요청에 적절할 때 나열된 프로토콜로의 업그레이드를 지원한다는 것을 알리기 위해 선호도가 높은 순서대로 Upgrade 헤더 필드를 보낼 수 있다(MAY).
다음은 클라이언트가 보내는 가상의 예다.
GET /hello HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: websocket, IRC/6.9, RTA/x11
프로토콜 변경 후 애플리케이션 수준 통신의 능력과 성격은 전적으로 새로 선택한 프로토콜(들)에 달려 있다. 그러나 서버는 101 (Switching Protocols) 응답을 보낸 직후, 새 프로토콜 안에서 그와 동등한 요청을 받은 것처럼 원래 요청에 계속 응답할 것으로 예상된다(즉, 프로토콜이 바뀐 뒤에도 서버에는 아직 처리해야 할 요청이 남아 있으며, 요청을 반복하게 하지 않고 그 요청을 처리할 것으로 예상된다).
예를 들어 GET 요청에서 Upgrade 헤더 필드를 받은 서버가 프로토콜을 전환하기로 하면, 먼저 HTTP/1.1로 101 (Switching Protocols) 메시지를 보내 응답한 다음, 곧바로 대상 리소스에 대한 GET의 응답에 해당하는 것을 새 프로토콜로 보낸다. 이렇게 하면 추가 왕복에 따른 지연 시간 비용 없이 HTTP와 의미가 같은 프로토콜로 연결을 업그레이드할 수 있다. 서버는 받은 메시지의 의미를 새 프로토콜이 따를 수 없다면 프로토콜을 전환해서는 안 된다(MUST NOT). OPTIONS 요청은 어떤 프로토콜이든 따를 수 있다.
다음은 위 가상 요청에 대한 응답의 예다.
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: websocket
[... data stream switches to websocket with an appropriate response
(as defined by new protocol) to the "GET /hello" request ...]
Upgrade를 보내는 송신자는 중개자가 이 필드를 전달하지 않도록 알리기 위해 Connection 헤더 필드(7.6.1절)에 “Upgrade” 연결 옵션도 반드시 보내야 한다(MUST). HTTP/1.0 요청에서 Upgrade 헤더 필드를 받은 서버는 그 Upgrade 필드를 반드시 무시해야 한다(MUST).
클라이언트는 요청 메시지를 완전히 보내기 전까지 그 연결에서 업그레이드된 프로토콜을 쓰기 시작할 수 없다(즉, 클라이언트는 메시지를 보내는 도중에 보내는 프로토콜을 바꿀 수 없다). 서버가 Upgrade 헤더 필드와 “100-continue” 기대(10.1.1절)를 담은 Expect 헤더 필드를 모두 받으면, 서버는 101 (Switching Protocols) 응답을 보내기 전에 100 (Continue) 응답을 반드시 보내야 한다(MUST).
Upgrade 헤더 필드는 기존 연결 위에서 프로토콜을 전환하는 데에만 적용된다. 기반이 되는 연결(전송) 프로토콜을 전환하거나, 기존 통신을 다른 연결로 옮기는 데는 쓸 수 없다. 그런 목적에는 3xx (Redirection) 응답(15.4절)을 쓰는 것이 더 적절하다.
이 명세는 2.5절의 HTTP 버전 규칙과 이후의 이 명세 갱신에 정의된 대로, Hypertext Transfer Protocol 계열이 쓸 프로토콜 이름 “HTTP”만 정의한다. 추가 프로토콜 이름은 16.7절에 정의된 등록 절차를 사용해 등록해야 마땅하다.
역주: 7.8절 응답 예 안의 대괄호 설명(
[... data stream switches …])은 원문 그대로 두었다. “ought to”(7.6·7.6.1·7.8절)는 대문자 키워드가 아니므로 “~해야 마땅하다”로 옮기고 괄호를 붙이지 않았다.
HTTP 메시지와 연관된 표현 데이터는 메시지의 콘텐츠로 제공되거나, 메시지의 의미와 대상 URI가 가리킨다. 표현 데이터는 표현 메타데이터 헤더 필드가 정의한 형식과 인코딩을 따른다.
표현 데이터의 데이터 유형은 Content-Type과 Content-Encoding 헤더 필드로 정해진다. 이 두 필드는 순서가 있는 두 계층의 인코딩 모델을 정의한다.
representation-data := Content-Encoding( Content-Type( data ) )
표현 헤더 필드는 표현에 관한 메타데이터를 제공한다. 메시지에 콘텐츠가 있으면 표현 헤더 필드는 그 데이터를 어떻게 해석할지 설명한다. HEAD 요청에 대한 응답에서 표현 헤더 필드는, 같은 요청이 GET이었다면 콘텐츠에 담겼을 표현 데이터를 설명한다.
“Content-Type” 헤더 필드는 연관된 표현, 즉 메시지 콘텐츠에 담긴 표현이나 메시지 의미에 따라 정해지는 선택된 표현의 미디어 유형을 나타낸다. 지정된 미디어 유형은 데이터 형식과, Content-Encoding이 나타내는 콘텐츠 코딩을 모두 디코딩한 뒤 받은 메시지 의미의 범위 안에서 수신자가 그 데이터를 어떻게 처리하도록 의도되었는지를 함께 정의한다.
Content-Type = media-type
미디어 유형은 8.3.1절에 정의되어 있다. 이 필드의 예는 다음과 같다.
Content-Type: text/html; charset=ISO-8859-4
콘텐츠를 담은 메시지를 생성하는 송신자는, 담긴 표현의 의도된 미디어 유형을 모르는 경우가 아니라면 그 메시지에 Content-Type 헤더 필드를 생성해야 한다(SHOULD). Content-Type 헤더 필드가 없으면, 수신자는 미디어 유형을 “application/octet-stream”([RFC2046]의 4.5.1절)으로 가정하거나, 데이터를 살펴 유형을 판단할 수 있다(MAY).
실제로는 리소스 소유자가 주어진 표현에 맞는 Content-Type을 제공하도록 원본 서버를 항상 올바르게 설정하지는 않는다. 일부 사용자 에이전트는 콘텐츠를 살펴보고, 경우에 따라 받은 유형을 무시하고 다른 유형으로 판단한다(예: [Sniffing] 참고). 이런 “MIME 스니핑”은 데이터에 대해 잘못된 결론을 내릴 위험이 있으며, 사용자를 추가적인 보안 위험(예: “권한 상승”)에 노출할 수 있다. 게다가 서로 다른 미디어 유형이 공통 데이터 형식을 공유하고 데이터를 처리하는 방식만 다른 경우가 많은데, 이는 데이터만 살펴서는 구별할 수 없다. 스니핑을 구현할 때는 사용자가 그것을 끌 수 있는 수단을 제공할 것이 권장된다.
Content-Type은 단일 값 필드로 정의되어 있지만, 가끔 잘못해서 여러 번 생성되어 결합된 필드 값이 목록처럼 보이게 된다. 수신자는 흔히 목록에서 구문상 유효한 마지막 구성원을 사용하는 방식으로 이 오류를 처리하려 하는데, 구현마다 오류 처리 동작이 다르면 상호 운용성 문제와 보안 문제로 이어질 수 있다.
HTTP는 개방적이고 확장 가능한 데이터 유형 지정과 유형 협상을 제공하기 위해 Content-Type(8.3절)과 Accept(12.5.1절) 헤더 필드에서 미디어 유형[RFC2046]을 사용한다. 미디어 유형은 데이터 형식과 다양한 처리 모델, 즉 메시지 맥락에 따라 그 데이터를 어떻게 처리할지를 함께 정의한다.
media-type = type "/" subtype parameters
type = token
subtype = token
type과 subtype 토큰은 대소문자를 구분하지 않는다.
type/subtype 뒤에는 이름/값 쌍 형태의, 세미콜론으로 구분된 매개 변수(5.6.6절)가 올 수 있다(MAY). 매개 변수가 있는지 없는지는 미디어 유형 레지스트리 안의 정의에 따라 그 미디어 유형의 처리에 중요할 수 있다. 매개 변수 값은 매개 변수 이름의 의미에 따라 대소문자를 구분할 수도, 구분하지 않을 수도 있다.
예를 들어 다음 미디어 유형들은 UTF-8 문자 인코딩 스킴으로 인코딩된 HTML 텍스트 데이터를 나타낸다는 점에서 동등하지만, 일관성을 위해 첫째 형태가 권장된다(“charset” 매개 변수 값은 [RFC2046]의 4.1.2절에서 대소문자를 구분하지 않는 것으로 정의되어 있다).
text/html;charset=utf-8
Text/HTML;Charset="utf-8"
text/html; charset="utf-8"
text/html;charset=UTF-8
미디어 유형은 [BCP13]에 정의된 절차에 따라 IANA에 등록해야 마땅하다.
HTTP는 텍스트 표현의 문자 인코딩 스킴([RFC6365]의 2장)을 나타내거나 협상하기 위해 “charset” 이름을 사용한다. 이 문서가 정의하는 필드에서 charset 이름은 매개 변수(Content-Type)에 나타나거나, Accept-EncodingAccept-Charset에서는 단순한 토큰 형태로 나타난다. 두 경우 모두 charset 이름은 대소문자를 구분하지 않고 비교한다.
역주: 정오표 7419(Verified)에 따라
Accept-Encoding을 고친 내용이다.
charset 이름은 [RFC2978]의 2장에 정의된 절차에 따라 IANA “문자 집합(Character Sets)” 레지스트리(https://www.iana.org/assignments/character-sets)에 등록해야 마땅하다.
참고: 이론적으로 charset 이름은 [RFC2978]의 2.3절에 정의된 “mime-charset” ABNF 규칙([Err1912]에서 정정)으로 정의된다. 그 규칙은 “token”에 포함되지 않는 두 문자(“{“와 “}”)를 허용하지만, 이 글을 쓰는 시점에 등록된 charset 이름 중 중괄호를 포함한 것은 없다([Err5433] 참고).
MIME은 여러 “multipart” 유형, 즉 하나의 메시지 본문 안에 하나 이상의 표현을 캡슐화하는 유형을 제공한다. 모든 멀티파트 유형은 [RFC2046]의 5.1.1절에 정의된 공통 구문을 공유하며, 미디어 유형 값의 일부로 boundary 매개 변수를 포함한다. 메시지 본문 자체가 프로토콜 요소이므로, 송신자는 본문 부분 사이의 줄바꿈을 나타내는 데 반드시 CRLF만 생성해야 한다(MUST).
HTTP 메시지 프레이밍은 멀티파트 경계를 메시지 본문 길이의 지표로 쓰지 않는다. 다만 콘텐츠를 생성하거나 처리하는 구현은 그것을 쓸 수 있다. 예를 들어 “multipart/form-data” 유형은 [RFC7578]에 설명된 대로 요청에서 폼 데이터를 전달하는 데 흔히 쓰이며, “multipart/byteranges” 유형은 일부 206 (Partial Content) 응답에서 쓰도록 이 명세가 정의한다(15.3.7절 참고).
“Content-Encoding” 헤더 필드는 미디어 유형에 본래 들어 있는 것 외에 어떤 콘텐츠 코딩이 표현에 적용되었는지, 따라서 Content-Type 헤더 필드가 가리키는 미디어 유형의 데이터를 얻으려면 어떤 디코딩 메커니즘을 적용해야 하는지 나타낸다. Content-Encoding은 주로 표현의 데이터를, 기반이 되는 미디어 유형의 정체성을 잃지 않고 압축할 수 있게 하는 데 쓰인다.
Content-Encoding = #content-coding
사용 예는 다음과 같다.
Content-Encoding: gzip
표현에 하나 이상의 인코딩을 적용했다면, 그 인코딩을 적용한 송신자는 콘텐츠 코딩을 적용한 순서대로 나열한 Content-Encoding 헤더 필드를 반드시 생성해야 한다(MUST). “identity”라는 이름의 코딩은 Accept-Encoding에서의 특별한 역할을 위해 예약되어 있으므로 포함하지 않아야 한다(SHOULD NOT)는 점에 유의한다.
인코딩 매개 변수에 관한 추가 정보는 이 명세가 정의하지 않은 다른 헤더 필드로 제공할 수 있다.
Transfer-Encoding([HTTP/1.1]의 6.1절)과 달리, Content-Encoding에 나열된 코딩은 표현의 특성이다. 표현은 코딩된 형태를 기준으로 정의되며, 메타데이터 정의에서 달리 언급하지 않는 한 표현에 관한 그 밖의 모든 메타데이터는 코딩된 형태에 관한 것이다. 보통 표현은 렌더링이나 그와 비슷한 용도 직전에만 디코딩된다.
항상 압축되는 데이터 형식처럼 미디어 유형에 본래 인코딩이 들어 있다면, 그 인코딩이 우연히 콘텐츠 코딩 중 하나와 같은 알고리즘이더라도 Content-Encoding에 다시 적지 않는다. 그런 콘텐츠 코딩은 어떤 특이한 이유로 표현을 만들 때 두 번째로 적용된 경우에만 나열한다. 마찬가지로 원본 서버는 코딩을 Content-Type의 일부로 정의하는지 Content-Encoding의 일부로 정의하는지만 다른 여러 표현으로 같은 데이터를 게시할 수도 있다. 일부 사용자 에이전트는 각 응답을 다르게 처리하기 때문이다(예: 콘텐츠를 자동으로 압축 해제하고 렌더링하는 대신 “다른 이름으로 저장” 대화 상자를 연다).
원본 서버는 요청 메시지의 표현에 받아들일 수 없는 콘텐츠 코딩이 있으면 415 (Unsupported Media Type) 상태 코드로 응답할 수 있다(MAY).
콘텐츠 코딩 값은 표현에 적용되었거나 적용할 수 있는 인코딩 변형을 나타낸다. 콘텐츠 코딩은 주로 표현을, 기반이 되는 미디어 유형의 정체성을 잃지 않고 정보 손실 없이 압축하거나 그 밖의 유용한 방식으로 변형할 수 있게 하는 데 쓰인다. 흔히 표현은 코딩된 형태로 저장되고, 그대로 전송되며, 최종 수신자만 디코딩한다.
content-coding = token
모든 콘텐츠 코딩은 대소문자를 구분하지 않으며, 16.6절에 설명된 대로 “HTTP 콘텐츠 코딩 레지스트리(HTTP Content Coding Registry)”에 등록해야 마땅하다.
콘텐츠 코딩 값은 Accept-Encoding(12.5.3절)과 Content-Encoding(8.4절) 헤더 필드에서 쓰인다.
“compress” 코딩은 UNIX 파일 압축 프로그램 “compress”가 흔히 만들어 내는 적응형 LZW(Lempel-Ziv-Welch) 코딩[Welch]이다. 수신자는 “x-compress”를 “compress”와 동등한 것으로 여겨야 한다(SHOULD).
“deflate” 코딩은 LZ77(Lempel-Ziv) 압축 알고리즘과 허프만 부호화를 결합해 사용하는 “deflate” 압축 데이터 스트림[RFC1951]을 담은 “zlib” 데이터 형식[RFC1950]이다.
참고: 일부 비적합 구현은 “deflate” 압축 데이터를 zlib 래퍼 없이 보낸다.
“gzip” 코딩은 gzip 파일 압축 프로그램[RFC1952]이 흔히 만들어 내는, 32비트 순환 중복 검사(CRC)를 갖춘 LZ77 코딩이다. 수신자는 “x-gzip”을 “gzip”과 동등한 것으로 여겨야 한다(SHOULD).
“Content-Language” 헤더 필드는 표현이 대상으로 하는 독자의 자연어(들)를 설명한다. 이는 표현 안에서 쓰인 모든 언어와 같지 않을 수 있다는 점에 유의한다.
Content-Language = #language-tag
언어 태그는 8.5.1절에 정의되어 있다. Content-Language의 주된 목적은 사용자가 자신이 선호하는 언어에 따라 표현을 식별하고 구별할 수 있게 하는 것이다. 따라서 콘텐츠가 덴마크어를 읽을 줄 아는 독자만을 대상으로 한다면 적절한 필드는 다음과 같다.
Content-Language: da
Content-Language가 지정되지 않으면, 기본적으로 콘텐츠가 모든 언어의 독자를 대상으로 한다고 본다. 이는 송신자가 그 콘텐츠를 특정 자연어에 한정된 것으로 여기지 않거나, 어떤 언어를 대상으로 하는지 모른다는 뜻일 수 있다.
여러 독자를 대상으로 하는 콘텐츠에는 여러 언어를 나열할 수 있다(MAY). 예를 들어 원래의 마오리어 판과 영어 판을 함께 제시하는 “와이탕이 조약(Treaty of Waitangi)”의 판본에는 다음이 필요하다.
Content-Language: mi, en
그러나 표현 안에 여러 언어가 있다고 해서 여러 언어권의 독자를 대상으로 한다는 뜻은 아니다. 예를 들어 “A First Lesson in Latin” 같은 초보자용 언어 입문서는 분명히 영어를 읽을 줄 아는 독자를 대상으로 한다. 이 경우 Content-Language에는 “en”만 넣는 것이 맞다.
Content-Language는 어떤 미디어 유형에든 적용할 수 있다(MAY). 텍스트 문서에 한정되지 않는다.
[RFC5646]에 정의된 언어 태그는 사람이 다른 사람에게 정보를 전달하기 위해 말하거나 쓰거나 그 밖의 방식으로 전하는 자연어를 식별한다. 컴퓨터 언어는 명시적으로 제외된다.
HTTP는 Accept-Language와 Content-Language 헤더 필드에서 언어 태그를 사용한다. Accept-Language는 12.5.4절에 정의된 더 넓은 language-range 생성 규칙을 쓰고, Content-Language는 아래에 정의된 language-tag 생성 규칙을 쓴다.
language-tag = <Language-Tag, see [RFC5646], Section 2.1>
언어 태그는 대소문자를 구분하지 않는 하나 이상의 하위 태그를 하이픈 문자(“-“, %x2D)로 구분한 시퀀스다. 대부분의 경우 언어 태그는 관련 언어의 넓은 계열을 식별하는 주 언어 하위 태그(예: “en” = 영어)로 이루어지며, 그 뒤에 그 언어의 범위를 다듬거나 좁히는 하위 태그가 선택적으로 이어진다(예: “en-CA” = 캐나다에서 쓰는 영어 변종). 언어 태그 안에는 공백이 허용되지 않는다. 태그의 예는 다음과 같다.
fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN
자세한 내용은 [RFC5646]을 참고한다.
“Content-Length” 헤더 필드는 연관된 표현의 데이터 길이를 음이 아닌 십진 정수 옥텟 수로 나타낸다. 표현을 콘텐츠로 전송할 때 Content-Length는 특히 담긴 데이터의 양을 가리키므로 프레이밍의 경계를 정하는 데 쓸 수 있다(예: [HTTP/1.1]의 6.2절). 그 밖의 경우 Content-Length는 선택된 표현의 현재 길이를 나타내며, 수신자는 이를 전송 시간을 추정하거나 이전에 저장한 표현과 비교하는 데 쓸 수 있다.
Content-Length = 1*DIGIT
예는 다음과 같다.
Content-Length: 3495
사용자 에이전트는 메서드가 담긴 콘텐츠에 의미를 정의하고 Transfer-Encoding을 보내지 않을 때 요청에 Content-Length를 보내야 한다(SHOULD). 예를 들어 사용자 에이전트는 보통 POST 요청에서 값이 0(빈 콘텐츠를 나타냄)이더라도 Content-Length를 보낸다. 사용자 에이전트는 요청 메시지에 콘텐츠가 없고 메서드의 의미가 그런 데이터를 예상하지 않을 때는 Content-Length 헤더 필드를 보내지 않아야 한다(SHOULD NOT).
서버는 HEAD 요청(9.3.2절)에 대한 응답에 Content-Length 헤더 필드를 보낼 수 있다(MAY). 서버는 같은 요청이 GET 메서드를 썼다면 응답 콘텐츠로 보냈을 옥텟 수의 십진수와 필드 값이 같은 경우가 아니라면, 그런 응답에 Content-Length를 보내서는 안 된다(MUST NOT).
서버는 조건부 GET 요청에 대한 304 (Not Modified) 응답(15.4.5절)에 Content-Length 헤더 필드를 보낼 수 있다(MAY). 서버는 같은 요청에 대한 200 (OK) 응답의 콘텐츠로 보냈을 옥텟 수의 십진수와 필드 값이 같은 경우가 아니라면, 그런 응답에 Content-Length를 보내서는 안 된다(MUST NOT).
서버는 상태 코드가 1xx (Informational)이거나 204 (No Content)인 응답에 Content-Length 헤더 필드를 보내서는 안 된다(MUST NOT). 서버는 CONNECT 요청(9.3.6절)에 대한 2xx (Successful) 응답에 Content-Length 헤더 필드를 보내서는 안 된다(MUST NOT).
위에서 정의한 경우를 제외하고, Transfer-Encoding이 없을 때 원본 서버는 헤더 섹션 전체를 보내기 전에 콘텐츠 크기를 알고 있다면 Content-Length 헤더 필드를 보내야 한다(SHOULD). 그러면 다운스트림 수신자가 전송 진행 상황을 측정하고, 받은 메시지가 언제 완전해지는지 알고, 추가 요청에 연결을 재사용할 수도 있다.
0 이상인 모든 Content-Length 필드 값은 유효하다. 콘텐츠 길이에는 미리 정한 제한이 없으므로, 수신자는 매우 큰 십진 숫자가 올 수 있음을 반드시 예상하고, 정수 변환 시의 오버플로로 인한 파싱 오류나 정수 변환으로 인한 정밀도 손실을 막아야 한다(MUST)(17.5절).
Content-Length는 HTTP/1.1에서 메시지의 경계를 정하는 데 쓰이므로, 바로 맞닿은 연결이 HTTP/1.1을 쓰지 않더라도 그 필드 값은 다운스트림 수신자가 메시지를 파싱하는 방식에 영향을 줄 수 있다. 메시지를 다운스트림 중개자가 전달한다면, 받은 메시지 프레이밍과 맞지 않는 Content-Length 필드 값은 요청 스머글링이나 응답 분할로 인한 보안 실패를 일으킬 수 있다.
따라서 송신자는 잘못된 것으로 알려진 Content-Length 헤더 필드 값을 가진 메시지를 전달해서는 안 된다(MUST NOT).
마찬가지로 송신자는 위 ABNF에 맞지 않는 Content-Length 헤더 필드 값을 가진 메시지를 전달해서는 안 된다(MUST NOT). 단, 한 가지 예외가 있다. 같은 십진 값을 쉼표로 구분된 목록으로 반복한 Content-Length 헤더 필드 값(예: “Content-Length: 42, 42”)을 받은 수신자는, 메시지를 유효하지 않은 것으로 거부하거나 그 유효하지 않은 필드 값을 십진 값 하나로 바꿀 수 있다(MAY). 이는 업스트림 메시지 처리기가 중복을 생성했거나 결합했을 가능성이 높음을 나타내기 때문이다.
“Content-Location” 헤더 필드는 이 메시지 콘텐츠의 표현에 대응하는 특정 리소스의 식별자로 쓸 수 있는 URI를 참조한다. 다시 말해, 이 메시지가 생성된 시점에 이 URI로 GET 요청을 했다면, 200 (OK) 응답에는 이 메시지에 콘텐츠로 담긴 것과 같은 표현이 들어 있을 것이다.
Content-Location = absolute-URI / partial-URI
필드 값은 absolute-URI이거나 partial-URI다. 후자의 경우(4장) 참조된 URI는 대상 URI를 기준으로 한 상대 URI다([URI]의 5장).
Content-Location 값은 대상 URI(7.1절)를 대체하지 않는다. 이는 표현 메타데이터다. 이 필드는 [RFC2557]의 4장에서 MIME 본문 부분에 대해 정의한 같은 이름의 헤더 필드와 구문과 의미가 같다. 그러나 HTTP 메시지에 나타날 때는 HTTP 수신자에게 몇 가지 특별한 의미가 있다.
2xx (Successful) 응답 메시지에 Content-Location이 있고 그 값이 (절대 형식으로 변환한 뒤) 대상 URI와 같은 URI를 가리키면, 수신자는 그 콘텐츠를 메시지 생성 일시가 나타내는 시점의 그 리소스의 현재 표현으로 여길 수 있다(MAY). GET(9.3.1절)이나 HEAD(9.3.2절) 요청에서 이는 서버가 Content-Location을 제공하지 않았을 때의 기본 의미와 같다. PUT(9.3.4절)이나 POST(9.3.3절)처럼 상태를 바꾸는 요청에서는, 서버의 응답에 그 리소스의 새 표현이 들어 있음을 뜻하므로 동작에 대해서만 알리는 표현(예: “성공했습니다!”)과 구별된다. 이를 통해 저작 애플리케이션은 이후의 GET 요청 없이도 로컬 사본을 갱신할 수 있다.
2xx (Successful) 응답 메시지에 Content-Location이 있고 그 필드 값이 대상 URI와 다른 URI를 가리키면, 원본 서버는 그 URI가 담긴 표현에 대응하는 다른 리소스의 식별자라고 주장하는 것이다. 그런 주장은 두 식별자의 리소스 소유자가 같을 때에만 신뢰할 수 있는데, 이는 HTTP를 통해 프로그램으로 판단할 수 없다.
GET이나 HEAD 요청에 대한 응답이라면, 이는 대상 URI가 콘텐츠 협상의 대상인 리소스를 가리키며 Content-Location 필드 값이 선택된 표현에 대한 더 구체적인 식별자임을 나타낸다.201 (Created) 응답이라면, Location 필드 값과 같은 Content-Location 필드 값은 이 콘텐츠가 새로 생성된 리소스의 현재 표현임을 나타낸다.Content-Location은 이 콘텐츠가 요청한 동작의 상태를 알리는 표현이며, 같은 보고를 주어진 URI에서 (나중에 GET으로 접근할 수 있게) 얻을 수 있음을 나타낸다. 예를 들어 POST 요청으로 한 구매 거래는 200 (OK) 응답의 콘텐츠로 영수증 문서를 담을 수 있으며, Content-Location 필드 값은 나중에 같은 영수증의 사본을 가져올 수 있는 식별자를 제공한다.요청 메시지에 Content-Location을 보내는 사용자 에이전트는, 그 값이 담긴 표현의 콘텐츠를 사용자 에이전트가 (사용자 에이전트가 수정하기 전에) 원래 어디서 얻었는지를 가리킨다고 말하는 것이다. 다시 말해, 사용자 에이전트는 원래 표현의 원천으로 돌아가는 역링크를 제공하는 것이다.
요청 메시지에서 Content-Location 필드를 받은 원본 서버는 그 정보를 표현의 일부로 그대로 저장할 메타데이터가 아니라 일시적인 요청 맥락으로 반드시 다뤄야 한다(MUST). 원본 서버는 그 맥락을 요청 처리의 길잡이로 쓰거나, 원천 링크나 버전 관리 메타데이터 같은 다른 용도로 저장할 수 있다(MAY). 그러나 원본 서버는 그런 맥락 정보를 요청의 의미를 바꾸는 데 써서는 안 된다(MUST NOT).
예를 들어 클라이언트가 협상되는 리소스에 PUT 요청을 하고 원본 서버가 (리다이렉트 없이) 그 PUT을 받아들이면, 그 리소스의 새 상태는 그 PUT에서 제공한 하나의 표현과 일관될 것으로 예상된다. Content-Location을 협상된 표현 중 하나만 갱신하기 위한 일종의 역방향 콘텐츠 선택 식별자로 쓸 수는 없다. 사용자 에이전트가 후자의 의미를 원했다면 PUT을 Content-Location URI에 직접 적용했을 것이다.
역주: 8.7절의 source는 4.3.1절에서 정의한 origin(출처)과 겹치지 않도록 “원천”으로 옮겼다.
리소스 메타데이터를 전제 조건(13.1절) 안에서 조건부 요청(13장)을 만드는 데 쓸 수 있으면, 그 메타데이터를 “검증자(validator)”라고 한다. 검증자 필드는 선택된 표현(3.2절)의 현재 검증자를 전달한다.
안전한 요청에 대한 응답에서 검증자 필드는 원본 서버가 응답을 처리하는 동안 고른 선택된 표현을 설명한다. 메서드와 상태 코드의 의미에 따라, 주어진 응답의 선택된 표현이 응답 콘텐츠로 담긴 표현과 반드시 같지는 않다는 점에 유의한다.
상태를 바꾸는 요청에 대한 성공 응답에서 검증자 필드는 요청을 처리한 결과로 이전의 선택된 표현을 대체한 새 표현을 설명한다.
예를 들어 201 (Created) 응답의 ETag 필드는 새로 생성된 리소스 표현의 엔터티 태그를 전달하여, 이후 조건부 요청에서 그 엔터티 태그를 검증자로 써서 “갱신 손실(lost update)” 문제를 막을 수 있게 한다.
이 명세는 리소스 상태를 관찰하고 전제 조건을 검사하는 데 흔히 쓰이는 두 가지 형태의 메타데이터를 정의한다. 수정 일자(8.8.2절)와 불투명한 엔터티 태그(8.8.3절)다. 리소스 상태를 반영하는 추가 메타데이터는 WebDAV(Web Distributed Authoring and Versioning)[WEBDAV]처럼 이 명세의 범위를 벗어나는 여러 HTTP 확장에서 정의되었다.
검증자에는 강한 것과 약한 것 두 종류가 있다. 약한 검증자는 생성하기 쉽지만 비교에는 훨씬 덜 유용하다. 강한 검증자는 비교에 이상적이지만 효율적으로 생성하기가 매우 어려울 수 있다(가끔은 불가능하다). HTTP는 모든 형태의 리소스가 같은 강도의 검증자를 따르도록 강요하는 대신, 사용 중인 검증자의 유형을 드러내고 약한 검증자를 전제 조건으로 쓸 수 있는 경우를 제한한다.
“강한 검증자(strong validator)”는 GET에 대한 200 (OK) 응답의 콘텐츠에서 관찰할 수 있는 표현 데이터 변경이 생길 때마다 값이 바뀌는 표현 메타데이터다.
강한 검증자는 표현 메타데이터의 의미상 중요한 부분이 바뀌는 경우(예: Content-Type)처럼 표현 데이터 변경 외의 이유로 바뀔 수도 있다. 그러나 원본 서버로서는 원격 캐시와 저작 도구가 보관한 저장된 응답을 무효화해야 할 때에만 값을 바꾸는 것이 가장 좋다.
캐시 항목은 만료 시간과 상관없이 임의로 오래 남아 있을 수 있다. 따라서 캐시는 먼 과거에 얻은 검증자로 항목을 검증하려 할 수 있다. 강한 검증자는 특정 리소스와 연관된 모든 표현의 모든 버전에 걸쳐 시간이 지나도 고유하다. 그러나 서로 다른 리소스의 표현 사이에서 고유하다는 뜻은 없다(즉, 같은 강한 검증자가 동시에 여러 리소스의 표현에 쓰일 수 있으며, 그렇다고 그 표현들이 동등하다는 뜻은 아니다).
실제로 쓰이는 강한 검증자는 다양하다. 가장 좋은 것은 엄격한 개정 관리에 기반한 것으로, 표현이 바뀔 때마다 그 표현을 GET으로 접근할 수 있게 하기 전에 항상 고유한 노드 이름과 개정 식별자가 부여된다. 응답 헤더 필드를 보내기 전에 데이터를 얻을 수 있고 검증 요청을 받을 때마다 다이제스트를 다시 계산할 필요가 없다면, 표현 데이터에 적용한 충돌 저항 해시 함수도 충분하다. 그러나 리소스에 메타데이터만 다른 별개의 표현이 있다면(예: 같은 데이터 형식을 공유하는 미디어 유형들에 대한 콘텐츠 협상에서 생길 수 있음), 원본 서버는 그 표현들을 구별하기 위해 검증자에 추가 정보를 넣어야 한다.
반면 “약한 검증자(weak validator)”는 표현 데이터가 바뀔 때마다 바뀌지는 않을 수 있는 표현 메타데이터다. 이런 약함은 값을 계산하는 방식의 한계(예: 시계 해상도), 리소스의 가능한 모든 표현에 대해 고유성을 보장할 수 없음, 또는 리소스 소유자가 데이터의 고유한 시퀀스보다 스스로 정한 동등성 집합으로 표현을 묶고 싶어 함에서 비롯될 수 있다.
원본 서버는 이전 표현을 현재 표현의 대체물로 받아들일 수 없다고 여길 때마다 약한 엔터티 태그를 바꿔야 한다(SHOULD). 다시 말해, 원본 서버가 캐시가 오래된 응답을 무효화하기를 원할 때마다 약한 엔터티 태그가 바뀌어야 마땅하다.
예를 들어 동적 측정에 따라 콘텐츠가 매초 바뀌는 날씨 예보의 표현은, 캐시된 표현이 적당한 기간(서버 부하나 날씨 상태에 따라 동적으로 조정될 수 있음) 동안 유효하도록 같은 약한 검증자를 가진 (원본 서버 관점에서) 동등한 표현 집합으로 묶을 수 있다. 마찬가지로 표현의 수정 시간이 1초 해상도로만 정의된다면, 표현이 1초 안에 두 번 수정되고 그 사이에 조회될 수 있는 경우 그 수정 시간은 약한 검증자가 될 수 있다.
마찬가지로 어떤 검증자를 주어진 리소스의 둘 이상의 표현이 동시에 공유한다면, 그 표현들의 표현 데이터가 같지 않은 한 그 검증자는 약하다. 예를 들어 원본 서버가 gzip 콘텐츠 코딩을 적용한 표현과 콘텐츠 코딩을 적용하지 않은 표현에 같은 검증자를 보낸다면 그 검증자는 약하다. 그러나 같은 표현 데이터에 서로 다른 두 미디어 유형을 쓸 수 있는 경우처럼, 동시에 있는 두 표현이 표현 메타데이터만 다르다면 같은 강한 검증자를 공유할 수 있다.
강한 검증자는 캐시 검증, 부분 콘텐츠 범위, “갱신 손실” 방지를 포함한 모든 조건부 요청에 쓸 수 있다. 약한 검증자는 캐시 항목을 검증하거나 웹 순회를 최근 변경으로 제한할 때처럼, 클라이언트가 이전에 얻은 표현 데이터와 정확히 같을 것을 요구하지 않을 때에만 쓸 수 있다.
응답의 “Last-Modified” 헤더 필드는 요청 처리를 마치는 시점에 판단한, 선택된 표현이 마지막으로 수정되었다고 원본 서버가 믿는 날짜와 시간을 나타내는 타임스탬프를 제공한다.
Last-Modified = HTTP-date
사용 예는 다음과 같다.
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
원본 서버는 마지막 수정 일자를 합리적이고 일관되게 판단할 수 있는 모든 선택된 표현에 대해 Last-Modified를 보내야 한다(SHOULD). 조건부 요청과 캐시 신선도 평가([CACHING])에 이를 쓰면 불필요한 전송을 크게 줄이고 서비스 가용성과 확장성을 크게 높일 수 있기 때문이다.
표현은 보통 리소스 인터페이스 뒤에 있는 여러 부분의 합이다. 마지막 수정 시간은 보통 그 부분들 가운데 어느 것이든 바뀐 가장 최근 시간이 된다. 주어진 리소스에 대해 그 값을 어떻게 판단하는지는 이 명세의 범위를 벗어나는 구현 세부 사항이다.
원본 서버는 응답의 Date 필드 값을 생성하는 시간에 가능한 한 가깝게 표현의 Last-Modified 값을 얻어야 한다(SHOULD). 그러면 특히 응답이 생성되는 시간 근처에서 표현이 바뀌는 경우에 수신자가 표현의 수정 시간을 정확하게 판단할 수 있다.
시계(5.6.7절에 정의)가 있는 원본 서버는 서버의 메시지 생성 시간(Date, 6.6.1절)보다 늦은 Last-Modified 일시를 생성해서는 안 된다(MUST NOT). 마지막 수정 시간이, 원본 서버의 시계로 볼 때 미래의 어떤 시간으로 평가되는 구현 고유의 메타데이터에서 나온다면, 원본 서버는 그 값을 메시지 생성 일시로 반드시 바꿔야 한다(MUST). 이는 미래의 수정 일자가 캐시 검증에 나쁜 영향을 주는 것을 막는다.
시계가 없는 원본 서버는 다른 시스템(아마도 시계가 있는 시스템)이 그 리소스에 부여한 일시 값이 아니라면 응답에 Last-Modified 일시를 생성해서는 안 된다(MUST NOT).
요청에서 검증자로 쓰이는 Last-Modified 시간은, 다음 규칙으로 강하다고 추론할 수 없다면 암묵적으로 약하다.
또는
If-Modified-Since, If-Unmodified-Since, If-Range 헤더 필드에 쓰려 하고,Last-Modified 값보다 적어도 1초 뒤인 Date 값이 들어 있으며, 클라이언트가 두 값이 같은 시계로 생성되었다고 믿을 이유가 있거나 Last-Modified와 Date 값의 차이가 충분해서 시계 동기화 문제가 생길 가능성이 낮다.또는
Last-Modified 값보다 적어도 1초 뒤인 Date 값이 들어 있으며, 캐시가 두 값이 같은 시계로 생성되었다고 믿을 이유가 있거나 Last-Modified와 Date 값의 차이가 충분해서 시계 동기화 문제가 생길 가능성이 낮다.이 방법은, 원본 서버가 같은 1초 동안 서로 다른 두 응답을 보냈는데 둘 다 Last-Modified 시간이 같다면, 그 응답 중 적어도 하나는 Date 값이 Last-Modified 시간과 같다는 사실에 기반한다.
응답의 “ETag” 필드는 요청 처리를 마치는 시점에 판단한, 선택된 표현의 현재 엔터티 태그를 제공한다. 엔터티 태그는 같은 리소스의 여러 표현을 구별하기 위한 불투명한 검증자이며, 그 여러 표현이 시간에 따른 리소스 상태 변경 때문이든, 같은 시점에 여러 표현이 유효하게 되는 콘텐츠 협상 때문이든, 둘 다 때문이든 상관없다. 엔터티 태그는 불투명한 따옴표 문자열로 이루어지며, 앞에 약함 표시가 붙을 수 있다.
ETag = entity-tag
entity-tag = [ weak ] opaque-tag
weak = %s"W/"
opaque-tag = DQUOTE *etagc DQUOTE
etagc = %x21 / %x23-7E / obs-text
; VCHAR except double quotes, plus obs-text
참고: 이전에는
opaque-tag가quoted-string([RFC2616]의 3.11절)으로 정의되었으므로, 일부 수신자는 백슬래시 이스케이프를 해제할 수 있다. 따라서 서버는 엔터티 태그에 백슬래시 문자를 쓰지 않아야 마땅하다.
수정 일자를 저장하기 불편하거나, HTTP-date 값의 1초 해상도로 충분하지 않거나, 수정 일자가 일관되게 유지되지 않는 상황에서는 엔터티 태그가 수정 일자보다 검증에 더 믿을 만할 수 있다.
예:
ETag: "xyzzy"
ETag: W/"xyzzy"
ETag: ""
엔터티 태그는 약한 검증자일 수도 강한 검증자일 수도 있으며, 기본값은 강한 쪽이다. 원본 서버가 표현에 엔터티 태그를 제공하는데 그 엔터티 태그의 생성이 강한 검증자의 특성(8.8.1절)을 모두 만족하지 않으면, 원본 서버는 불투명한 값 앞에 “W/”(대소문자 구분)를 붙여 그 엔터티 태그를 약한 것으로 반드시 표시해야 한다(MUST).
송신자는 트레일러 섹션에 ETag 필드를 보낼 수 있다(MAY)(6.5절 참고). 그러나 트레일러는 무시되는 경우가 많으므로, 콘텐츠를 보내는 동안 엔터티 태그를 생성하는 경우가 아니라면 ETag를 헤더 필드로 보내는 편이 낫다.
엔터티 태그의 원리는, 리소스의 구현을 잘 알아서 그 리소스에 가장 정확하고 효율적인 검증 메커니즘을 고를 수 있는 것은 서비스 작성자뿐이며, 그런 메커니즘은 무엇이든 쉽게 비교할 수 있도록 단순한 옥텟 시퀀스로 대응시킬 수 있다는 것이다. 값이 불투명하므로 클라이언트는 각 엔터티 태그가 어떻게 만들어지는지 알 필요가 없다.
예를 들어 모든 변경에 구현 고유의 버전 관리를 적용하는 리소스는, 표현을 정확하게 구별하기 위해 내부 개정 번호를 쓸 수 있으며, 여기에 콘텐츠 협상을 위한 변형 식별자를 결합할 수도 있다. 다른 구현은 표현 콘텐츠의 충돌 저항 해시, 여러 파일 속성의 조합, 1초 미만 해상도를 가진 수정 타임스탬프를 쓸 수 있다.
원본 서버는 변경 감지를 합리적이고 일관되게 판단할 수 있는 모든 선택된 표현에 대해 ETag를 보내야 한다(SHOULD). 조건부 요청과 캐시 신선도 평가([CACHING])에 엔터티 태그를 쓰면 불필요한 전송을 크게 줄이고 서비스 가용성, 확장성, 신뢰성을 크게 높일 수 있기 때문이다.
비교 맥락이 약한 검증자 사용을 허용하는지에 따라 엔터티 태그 비교 함수는 두 가지가 있다.
opaque-tag가 문자 단위로 일치하면 동등하다.opaque-tag가 문자 단위로 일치하면, 둘 중 하나나 둘 다 “약함”으로 표시되었는지와 상관없이 동등하다.아래 예는 엔터티 태그 쌍 집합과 약한 비교 함수, 강한 비교 함수의 결과를 보여 준다.
| ETag 1 | ETag 2 | 강한 비교 | 약한 비교 |
|---|---|---|---|
W/"1" |
W/"1" |
불일치 | 일치 |
W/"1" |
W/"2" |
불일치 | 불일치 |
W/"1" |
"1" |
불일치 | 일치 |
"1" |
"1" |
일치 | 일치 |
표 3
콘텐츠 협상(12장)의 대상이고, GET 요청에 응답해 보내는 표현이 Accept-Encoding 요청 헤더 필드(12.5.3절)에 따라 달라지는 리소스를 생각해 보자.
요청:
GET /index HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip
이 경우 응답은 gzip 콘텐츠 코딩을 쓸 수도 있고 쓰지 않을 수도 있다. 쓰지 않는다면 응답은 다음과 같을 수 있다.
응답:
HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-a"
Content-Length: 70
Vary: Accept-Encoding
Content-Type: text/plain
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
gzip 콘텐츠 코딩을 쓰는 다른 표현은 다음과 같다.
응답:
HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-b"
Content-Length: 43
Vary: Accept-Encoding
Content-Type: text/plain
Content-Encoding: gzip
...binary data...
참고: 콘텐츠 코딩은 표현 데이터의 속성이므로, 캐시 갱신과 범위 요청 중에 생길 수 있는 충돌을 막기 위해, 콘텐츠 인코딩된 표현의 강한 엔터티 태그는 인코딩되지 않은 표현의 엔터티 태그와 달라야 한다. 반면 전송 코딩([HTTP/1.1]의 7장)은 메시지 전송 중에만 적용되므로 별개의 엔터티 태그를 만들지 않는다.
역주: 8장의 “ought to”(8.3.1·8.3.2·8.4.1·8.8.1·8.8.3절), “need to”(8.8.1절), “are encouraged to”(8.3절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 헤더 필드 이름인 8.3~8.8.3절 제목(
Content-Type,Content-Encoding,Content-Language,Content-Length,Content-Location,Last-Modified,ETag)과 8.3.2절 “Charset”은 원어로 두었다. 8.8.3.3절의 “» Request:”, “» Response:” 표시는 메시지 밖의 설명이므로 “요청:”, “응답:”으로 옮겼다.
요청 메서드 토큰은 요청 의미의 주된 원천이다. 요청 메서드는 클라이언트가 이 요청을 한 목적과, 클라이언트가 성공한 결과로 무엇을 기대하는지를 나타낸다.
요청 메서드의 의미는, 요청에 있는 일부 헤더 필드의 추가 의미가 메서드와 충돌하지 않는다면 그 헤더 필드의 의미로 더 구체화될 수 있다. 예를 들어 클라이언트는 조건부 요청 헤더 필드(13.1절)를 보내 요청한 동작이 대상 리소스의 현재 상태에 따라 조건부로 수행되게 할 수 있다.
HTTP는 분산 객체 시스템의 인터페이스로 쓸 수 있도록 설계되었다. 요청 메서드는 원격 메서드 호출을 식별된 객체로 보내는 것과 거의 같은 방식으로, 대상 리소스에 적용할 동작을 호출한다.
method = token
메서드 토큰은 대소문자를 구분하는 메서드 이름을 가진 객체 기반 시스템의 게이트웨이로 쓰일 수 있으므로 대소문자를 구분한다. 관례상 표준화된 메서드는 모두 대문자인 US-ASCII 문자로 정의한다.
분산 객체와 달리 HTTP의 표준화된 요청 메서드는 리소스에 따라 다르지 않다. 일관된 인터페이스가 네트워크 기반 시스템에서 더 나은 가시성과 재사용을 제공하기 때문이다[REST]. 표준화된 메서드는 일단 정의되면 어떤 리소스에 적용하든 같은 의미를 가져야 마땅하다. 다만 그 의미를 구현하거나 허용할지는 각 리소스가 스스로 정한다.
이 명세는 HTTP에서 흔히 쓰이는 여러 표준화된 메서드를 다음 표와 같이 정의한다.
| 메서드 이름 | 설명 | 절 |
|---|---|---|
GET |
대상 리소스의 현재 표현을 전송한다. | 9.3.1 |
HEAD |
GET과 같지만 응답 콘텐츠를 전송하지 않는다. |
9.3.2 |
POST |
요청 콘텐츠에 대해 리소스별 처리를 수행한다. | 9.3.3 |
PUT |
대상 리소스의 현재 표현을 모두 요청 콘텐츠로 대체한다. | 9.3.4 |
DELETE |
대상 리소스의 현재 표현을 모두 제거한다. | 9.3.5 |
CONNECT |
대상 리소스가 식별하는 서버로 터널을 맺는다. | 9.3.6 |
OPTIONS |
대상 리소스의 통신 옵션을 설명한다. | 9.3.7 |
TRACE |
대상 리소스까지의 경로를 따라 메시지 루프백 시험을 수행한다. | 9.3.8 |
표 4
모든 범용 서버는 GET과 HEAD 메서드를 반드시 지원해야 한다(MUST). 그 밖의 모든 메서드는 선택 사항이다(OPTIONAL).
대상 리소스가 허용하는 메서드 집합은 Allow 헤더 필드(10.2.1절)에 나열할 수 있다. 그러나 허용되는 메서드 집합은 동적으로 바뀔 수 있다. 인식하지 못했거나 구현하지 않은 요청 메서드를 받은 원본 서버는 501 (Not Implemented) 상태 코드로 응답해야 한다(SHOULD). 인식하고 구현했지만 대상 리소스에 허용되지 않는 요청 메서드를 받은 원본 서버는 405 (Method Not Allowed) 상태 코드로 응답해야 한다(SHOULD).
이 명세의 범위 밖에서 HTTP에 쓰도록 명세된 추가 메서드도 있다. 그런 메서드는 모두 16.1절에 설명된 대로 “HTTP 메서드 레지스트리(Hypertext Transfer Protocol (HTTP) Method Registry)”에 등록해야 마땅하다.
요청 메서드의 정의된 의미가 본질적으로 읽기 전용이라면, 즉 안전한 메서드를 대상 리소스에 적용한 결과로 클라이언트가 원본 서버의 상태 변경을 요청하지도 기대하지도 않는다면, 그 요청 메서드를 “안전하다(safe)”고 본다. 마찬가지로 안전한 메서드를 합리적으로 사용하면 원본 서버에 해를 끼치거나, 재산 손실을 일으키거나, 특별한 부담을 주지 않을 것으로 예상된다.
이 안전한 메서드 정의는 구현이 안전한 메서드를 호출하는 동안 해로울 수 있거나, 완전히 읽기 전용이 아니거나, 부작용을 일으키는 동작을 포함하는 것을 막지 않는다. 그러나 중요한 것은 클라이언트가 그 추가 동작을 요청하지 않았으며 그에 대한 책임을 물을 수 없다는 점이다. 예를 들어 대부분의 서버는 메서드와 상관없이 모든 응답을 마칠 때 요청 정보를 접근 로그 파일에 덧붙이며, 로그 저장소가 가득 차서 서버가 실패하게 되더라도 이는 안전한 것으로 본다. 마찬가지로 웹에서 광고를 선택해 시작된 안전한 요청은 흔히 광고 계정에 요금을 부과하는 부작용을 낳는다.
이 명세가 정의하는 요청 메서드 가운데 GET, HEAD, OPTIONS, TRACE 메서드는 안전한 것으로 정의된다.
안전한 메서드와 안전하지 않은 메서드를 구별하는 목적은, 자동화된 조회 프로세스(스파이더)와 캐시 성능 최적화(미리 가져오기)가 해를 끼칠 걱정 없이 동작할 수 있게 하는 것이다. 또한 사용자 에이전트가 신뢰할 수 없을 수도 있는 콘텐츠를 처리할 때 안전하지 않은 메서드의 자동 사용에 적절한 제약을 둘 수 있게 한다.
사용자 에이전트는 사용자에게 가능한 동작을 제시할 때, 안전하지 않은 동작을 요청하기 전에 사용자가 그것을 알 수 있도록 안전한 메서드와 안전하지 않은 메서드를 구별해야 한다(SHOULD).
대상 URI 안의 매개 변수가 동작을 선택하는 효과를 갖도록 리소스를 구성했다면, 그 동작이 요청 메서드의 의미와 일관되도록 보장하는 것은 리소스 소유자의 책임이다. 예를 들어 웹 기반 콘텐츠 편집 소프트웨어는 “page?do=delete”처럼 쿼리 매개 변수 안에 동작을 넣는 경우가 흔하다. 그런 리소스의 목적이 안전하지 않은 동작을 수행하는 것이라면, 리소스 소유자는 안전한 요청 메서드로 접근할 때 그 동작을 반드시 끄거나 허용하지 않아야 한다(MUST). 그렇게 하지 않으면 자동화된 프로세스가 링크 관리, 미리 가져오기, 검색 색인 구축 등을 위해 모든 URI 참조에 GET을 수행할 때 불행한 부작용이 생긴다.
어떤 요청 메서드로 같은 요청을 여러 번 보냈을 때 서버에 의도된 효과가 그 요청을 한 번 보냈을 때의 효과와 같다면, 그 요청 메서드를 “멱등(idempotent)”이라고 본다. 이 명세가 정의하는 요청 메서드 가운데 PUT, DELETE와 안전한 요청 메서드는 멱등이다.
안전함의 정의와 마찬가지로, 멱등 속성은 사용자가 요청한 것에만 적용된다. 서버는 멱등 요청마다 각 요청을 따로 로그에 기록하거나, 개정 관리 이력을 남기거나, 그 밖의 멱등이 아닌 부작용을 구현해도 된다.
멱등 메서드를 구별하는 이유는, 클라이언트가 서버의 응답을 읽기 전에 통신 장애가 생기면 그 요청을 자동으로 반복할 수 있기 때문이다. 예를 들어 클라이언트가 PUT 요청을 보냈는데 응답을 받기 전에 기반이 되는 연결이 닫히면, 클라이언트는 새 연결을 맺고 그 멱등 요청을 재시도할 수 있다. 클라이언트는 원래 요청이 성공했더라도 요청을 반복하면 의도된 효과가 같다는 것을 안다. 다만 응답은 다를 수 있다.
클라이언트는 메서드와 상관없이 요청의 의미가 실제로 멱등임을 알 수 있는 수단이나, 원래 요청이 전혀 적용되지 않았음을 감지할 수 있는 수단이 없다면, 멱등이 아닌 메서드의 요청을 자동으로 재시도하지 않아야 한다(SHOULD NOT).
예를 들어 사용자 에이전트는 그 리소스에 대해 요청이 안전하다는 것을 (설계나 설정을 통해) 알고 있다면 POST 요청을 자동으로 반복할 수 있다. 마찬가지로 버전 관리 저장소에서 동작하도록 특별히 설계된 사용자 에이전트는, 연결이 실패한 뒤 대상 리소스의 개정판을 확인하고, 일부만 적용된 변경을 되돌리거나 고친 다음, 실패한 요청을 자동으로 재시도하여 부분 실패 상황에서 복구할 수 있다.
일부 클라이언트는 더 위험한 방식을 택해 언제 자동 재시도가 가능한지 추측하려 한다. 예를 들어 클라이언트는 응답의 어떤 부분도 받기 전에 기반이 되는 전송 연결이 닫히면, 특히 유휴 상태의 지속 연결을 썼다면 POST 요청을 자동으로 재시도할 수 있다.
프록시는 멱등이 아닌 요청을 자동으로 재시도해서는 안 된다(MUST NOT). 클라이언트는 실패한 자동 재시도를 다시 자동으로 재시도하지 않아야 한다(SHOULD NOT).
캐시가 응답을 저장하고 사용하려면, 연관된 메서드가 캐싱을 명시적으로 허용하고 어떤 조건에서 응답을 이후 요청을 처리하는 데 쓸 수 있는지 자세히 밝혀야 한다. 그렇게 하지 않는 메서드 정의는 캐시할 수 없다. 추가 요구 사항은 [CACHING]을 참고한다.
이 명세는 GET, HEAD, POST에 대한 캐싱 의미를 정의하지만, 캐시 구현의 압도적 다수는 GET과 HEAD만 지원한다.
GET 메서드는 대상 리소스의 현재 선택된 표현을 전송해 달라고 요청한다. 성공 응답은 대상 URI가 식별하는 “동일성(sameness)”의 성질을 반영한다([URI]의 1.2.2절). 따라서 HTTP로 식별 가능한 정보를 가져오는 일은 보통, 200 (OK) 응답으로 그 정보를 제공할 가능성과 연관된 식별자에 GET 요청을 하는 방식으로 이루어진다.
GET은 정보 조회의 주된 메커니즘이며 거의 모든 성능 최적화의 초점이다. 중요한 리소스마다 URI를 만드는 애플리케이션은 그런 최적화의 이점을 누리면서 다른 애플리케이션이 그 리소스를 재사용할 수 있게 하여, 웹이 더 확장되도록 촉진하는 네트워크 효과를 만든다.
리소스 식별자를 원격 파일 시스템 경로 이름으로, 표현을 그런 파일 내용의 사본으로 생각하고 싶어지기 쉽다. 실제로 많은 리소스가 그렇게 구현된다(관련 보안 고려 사항은 17.3절 참고). 그러나 실제로 그런 제한은 없다.
리소스의 HTTP 인터페이스는 콘텐츠 객체의 트리, 여러 데이터베이스 레코드에 대한 프로그램적 뷰, 또는 다른 정보 시스템에 대한 게이트웨이로 구현될 가능성도 똑같이 크다. URI 대응 메커니즘이 파일 시스템에 묶여 있더라도, 원본 서버는 파일을 직접 전송하는 대신 요청을 입력으로 파일을 실행하고 그 출력을 표현으로 보내도록 설정될 수 있다. 어쨌든 각 리소스 식별자가 구현에 어떻게 대응하는지, 그리고 그 구현이 대상 리소스의 현재 표현을 어떻게 선택하고 보내는지는 원본 서버만 알면 된다.
클라이언트는 요청에 Range 헤더 필드(14.2절)를 보내, GET의 의미를 선택된 표현의 일부분만 전송하도록 요청하는 “범위 요청(range request)”으로 바꿀 수 있다.
요청 메시지 프레이밍은 사용하는 메서드와 무관하지만, GET 요청에서 받은 콘텐츠에는 일반적으로 정의된 의미가 없고, 요청의 의미나 대상을 바꿀 수 없으며, 요청 스머글링 공격([HTTP/1.1]의 11.2절)으로 쓰일 가능성 때문에 일부 구현이 요청을 거부하고 연결을 닫게 만들 수 있다. 클라이언트는 그런 요청에 목적이 있고 충분히 지원될 것임을 대역 내에서든 대역 외에서든 미리 알린 원본 서버로 직접 보내는 경우가 아니라면 GET 요청에 콘텐츠를 생성하지 않아야 한다(SHOULD NOT). HTTP 통신 참여자는 흔히 요청 사슬 위의 중개자를 알지 못하므로, 원본 서버는 콘텐츠를 받기 위해 사적 합의에 의존하지 않아야 한다(SHOULD NOT).
GET 요청에 대한 응답은 캐시 가능하다. Cache-Control 헤더 필드([CACHING]의 5.2절)가 달리 나타내지 않는 한, 캐시는 그 응답을 이후의 GET과 HEAD 요청을 처리하는 데 쓸 수 있다(MAY).
GET을 사용하는 폼의 쿼리 필드처럼 사용자가 제공한 정보로 대상 URI를 구성하는 메커니즘으로 정보를 조회할 때는, URI 안에 공개하기에 적절하지 않은, 민감할 수 있는 데이터가 제공될 수 있다(17.9절 참고). 경우에 따라 그런 정보가 드러나지 않도록 데이터를 걸러 내거나 변형할 수 있다. 그 밖의 경우, 특히 응답을 캐시해서 얻는 이점이 없다면, GET 대신 POST 메서드(9.3.3절)를 쓰면 그런 정보를 대상 URI가 아니라 요청 콘텐츠에 담아 전송할 수 있다.
HEAD 메서드는 서버가 응답에 콘텐츠를 보내서는 안 된다(MUST NOT)는 점을 제외하면 GET과 같다. HEAD는 표현 데이터를 전송하지 않고 선택된 표현에 관한 메타데이터를 얻는 데 쓰이며, 흔히 하이퍼텍스트 링크를 시험하거나 최근 수정을 찾는 데 쓰인다.
서버는 HEAD 요청에 대한 응답으로, 요청 메서드가 GET이었다면 보냈을 것과 같은 헤더 필드를 보내야 한다(SHOULD). 그러나 서버는 콘텐츠를 생성하는 동안에만 값이 정해지는 헤더 필드는 생략할 수 있다(MAY). 예를 들어 일부 서버는 작은 응답의 경계를 더 효율적으로 정하거나 콘텐츠 선택을 늦게 결정할 수 있도록, 최소한의 데이터가 생성될 때까지 GET에 대한 동적 응답을 버퍼에 담는다. 그런 GET 응답에는 예를 들어 HEAD 응답에서는 생성되지 않는 Content-Length와 Vary 필드가 들어 있을 수 있다. HEAD는 보통 효율을 위해 요청하므로, HEAD 요청을 위해 콘텐츠를 생성했다가 버리는 것보다 이런 사소한 불일치가 더 낫다고 본다.
요청 메시지 프레이밍은 사용하는 메서드와 무관하지만, HEAD 요청에서 받은 콘텐츠에는 일반적으로 정의된 의미가 없고, 요청의 의미나 대상을 바꿀 수 없으며, 요청 스머글링 공격([HTTP/1.1]의 11.2절)으로 쓰일 가능성 때문에 일부 구현이 요청을 거부하고 연결을 닫게 만들 수 있다. 클라이언트는 그런 요청에 목적이 있고 충분히 지원될 것임을 대역 내에서든 대역 외에서든 미리 알린 원본 서버로 직접 보내는 경우가 아니라면 HEAD 요청에 콘텐츠를 생성하지 않아야 한다(SHOULD NOT). HTTP 통신 참여자는 흔히 요청 사슬 위의 중개자를 알지 못하므로, 원본 서버는 콘텐츠를 받기 위해 사적 합의에 의존하지 않아야 한다(SHOULD NOT).
HEAD 요청에 대한 응답은 캐시 가능하다. Cache-Control 헤더 필드([CACHING]의 5.2절)가 달리 나타내지 않는 한, 캐시는 그 응답을 이후의 HEAD 요청을 처리하는 데 쓸 수 있다(MAY). HEAD 응답은 이전에 캐시된 GET 응답에도 영향을 줄 수 있다. [CACHING]의 4.3.5절을 참고한다.
POST 메서드는 대상 리소스가 요청에 담긴 표현을 리소스 자체의 고유한 의미에 따라 처리해 달라고 요청한다. 예를 들어 POST는 (그 밖의 용도와 함께) 다음 기능에 쓰인다.
원본 서버는 POST 요청을 처리한 결과에 따라 적절한 상태 코드를 골라 응답의 의미를 나타낸다. 이 명세가 정의하는 상태 코드는 거의 모두 POST에 대한 응답에서 받을 수 있다(예외는 206 (Partial Content), 304 (Not Modified), 416 (Range Not Satisfiable)이다).
POST 요청을 성공적으로 처리한 결과로 원본 서버에 하나 이상의 리소스가 만들어졌다면, 원본 서버는 생성된 주 리소스의 식별자를 제공하는 Location 헤더 필드(10.2.2절)와, 새 리소스(들)를 가리키면서 요청 상태를 설명하는 표현을 담은 201 (Created) 응답을 보내야 한다(SHOULD).
POST 요청에 대한 응답은 명시적인 신선도 정보([CACHING]의 4.2.1절 참고)와, POST의 대상 URI와 값이 같은 Content-Location 헤더 필드(8.7절)를 포함할 때에만 캐시 가능하다. 캐시된 POST 응답은 이후의 GET이나 HEAD 요청을 처리하는 데 재사용할 수 있다. 반면 POST는 안전하지 않을 수 있으므로 POST 요청은 캐시된 POST 응답으로 처리할 수 없다. [CACHING]의 4장을 참고한다.
POST를 처리한 결과가 기존 리소스의 표현과 동등하다면, 원본 서버는 Location 필드에 기존 리소스의 식별자를 담은 303 (See Other) 응답을 보내 사용자 에이전트를 그 리소스로 리다이렉트할 수 있다(MAY). 이렇게 하면 사용자 에이전트에 리소스 식별자를 제공하고, 공유 캐싱에 더 적합한 메서드로 표현을 전송한다는 이점이 있다. 다만 사용자 에이전트가 그 표현을 아직 캐시하지 않았다면 추가 요청 비용이 든다.
PUT 메서드는 대상 리소스의 상태를 요청 메시지 콘텐츠에 담긴 표현이 정의하는 상태로 만들거나 대체해 달라고 요청한다. 주어진 표현의 PUT이 성공했다는 것은, 이후 같은 대상 리소스에 대한 GET에 200 (OK) 응답으로 동등한 표현이 전송되리라는 것을 시사한다. 그러나 그런 상태 변경이 관찰 가능하다는 보장은 없다. 이후의 GET을 받기 전에 다른 사용자 에이전트가 대상 리소스에 동시에 작용하거나, 원본 서버가 대상 리소스를 동적으로 처리할 수 있기 때문이다. 성공 응답은 원본 서버가 처리한 시점에 사용자 에이전트의 의도가 이루어졌다는 것만 뜻한다.
대상 리소스에 현재 표현이 없고 PUT이 표현을 성공적으로 만들면, 원본 서버는 201 (Created) 응답을 보내 사용자 에이전트에 반드시 알려야 한다(MUST). 대상 리소스에 현재 표현이 있고 그 표현이 담긴 표현의 상태에 따라 성공적으로 수정되면, 원본 서버는 요청이 성공적으로 완료되었음을 나타내기 위해 200 (OK)나 204 (No Content) 응답을 반드시 보내야 한다(MUST).
원본 서버는 PUT 표현이 대상 리소스에 대해 설정된 제약과 일관되는지 검증해야 한다(SHOULD). 예를 들어 원본 서버가 URI에 따라 리소스의 표현 메타데이터를 정한다면, 원본 서버는 성공한 PUT 요청에서 받은 콘텐츠가 그 메타데이터와 일관되도록 보장해야 한다. PUT 표현이 대상 리소스와 일관되지 않으면, 원본 서버는 표현을 변형하거나 리소스 설정을 바꿔 둘을 일관되게 만들거나, 표현이 왜 적합하지 않은지 설명하는 충분한 정보를 담은 적절한 오류 메시지로 응답해야 한다(SHOULD). 409 (Conflict)나 415 (Unsupported Media Type) 상태 코드를 권하며, 후자는 Content-Type 값에 대한 제약에 특화되어 있다.
예를 들어 대상 리소스가 항상 Content-Type이 “text/html”이 되도록 설정되어 있는데 PUT하는 표현의 Content-Type이 “image/jpeg”라면, 원본 서버는 다음 중 하나를 해야 마땅하다.
a. 새 미디어 유형을 반영하도록 대상 리소스를 다시 설정한다.
b. 새 리소스 상태로 저장하기 전에 PUT 표현을 그 리소스와 일관된 형식으로 변형한다.
c. 대상 리소스가 “text/html”로 제한되어 있음을 나타내는 415 (Unsupported Media Type) 응답으로 요청을 거부한다. 이때 새 표현에 적합한 대상이 될 다른 리소스로 가는 링크를 넣을 수도 있다.
HTTP는 사용자 에이전트 요청의 의도와 원본 서버 응답의 의미로 표현할 수 있는 것 이상으로, PUT 메서드가 원본 서버의 상태에 정확히 어떤 영향을 주는지 정의하지 않는다. HTTP는 HTTP로 제공되는 인터페이스 너머에서 리소스가 어떤 의미로든 무엇일 수 있는지 정의하지 않는다. 리소스 상태가 어떻게 “저장”되는지, 리소스 상태가 바뀐 결과로 그 저장소가 어떻게 바뀔 수 있는지, 원본 서버가 리소스 상태를 어떻게 표현으로 바꾸는지도 정의하지 않는다. 일반적으로 리소스 인터페이스 뒤의 모든 구현 세부 사항은 서버가 의도적으로 감춘다.
이는 헤더 필드와 트레일러 필드를 어떻게 저장하는지에도 적용된다. Content-Type 같은 흔한 헤더 필드는 보통 저장되었다가 이후 GET 요청에서 반환되지만, 헤더 필드와 트레일러 필드의 처리는 요청을 받은 리소스에 따라 다르다. 따라서 원본 서버는 PUT 요청에서 받은 인식하지 못한 헤더 필드와 트레일러 필드를 무시해야 한다(SHOULD). 즉, 리소스 상태의 일부로 저장하지 않는다.
원본 서버는 요청의 표현 데이터가 콘텐츠에 어떤 변형도 적용하지 않고 저장되었고(즉, 리소스의 새 표현 데이터가 PUT 요청에서 받은 콘텐츠와 같고) 검증자 필드 값이 새 표현을 반영하는 경우가 아니라면, PUT에 대한 성공 응답에 ETag나 Last-Modified 필드 같은 검증자 필드(8.8절)를 보내서는 안 된다(MUST NOT). 이 요구 사항 덕분에 사용자 에이전트는 자신이 보낸(그리고 메모리에 보관하고 있는) 표현이 언제 PUT의 결과인지 알 수 있으므로, 원본 서버에서 다시 가져올 필요가 없다. 응답에서 받은 새 검증자(들)는 실수로 덮어쓰는 일을 막기 위해 이후의 조건부 요청(13.1절)에 쓸 수 있다.
POST와 PUT 메서드의 근본적인 차이는 담긴 표현에 대한 의도가 다르다는 데서 드러난다. POST 요청의 대상 리소스는 담긴 표현을 리소스 자체의 의미에 따라 처리하도록 의도되는 반면, PUT 요청에 담긴 표현은 대상 리소스의 상태를 대체하는 것으로 정의된다. 따라서 PUT의 의도는 멱등이고 중개자가 볼 수 있다. 다만 정확한 효과는 원본 서버만 안다.
PUT 요청을 올바르게 해석하는 것은 사용자 에이전트가 어떤 대상 리소스를 원하는지 알고 있다는 것을 전제로 한다. 상태를 바꾸는 요청을 받은 뒤 클라이언트를 대신해 적절한 URI를 고르는 서비스는 PUT이 아니라 POST 메서드를 사용해 구현해야 한다(SHOULD). 원본 서버가 요청받은 PUT 상태 변경을 대상 리소스에 하지 않고, 리소스가 다른 URI로 옮겨진 경우처럼 다른 리소스에 적용하고 싶다면, 원본 서버는 적절한 3xx (Redirection) 응답을 반드시 보내야 한다(MUST). 그러면 사용자 에이전트는 요청을 리다이렉트할지 스스로 결정할 수 있다(MAY).
대상 리소스에 적용한 PUT 요청은 다른 리소스에 부작용을 일으킬 수 있다. 예를 들어 글에는 각 특정 버전을 식별하는 URI들(어느 시점에 현재 버전 리소스와 같은 상태를 공유했던 서로 다른 리소스들)과 별개로 “현재 버전”(하나의 리소스)을 식별하는 URI가 있을 수 있다. 따라서 “현재 버전” URI에 대한 성공한 PUT 요청은 대상 리소스의 상태를 바꾸는 것 외에 새 버전 리소스를 만들 수 있고, 관련 리소스들 사이에 링크가 추가되게 할 수도 있다.
일부 원본 서버는 14.5절에 설명된 대로 부분 PUT을 수행하기 위해 Content-Range 헤더 필드(14.4절)를 요청 수정자로 쓰는 것을 지원한다.
PUT 메서드에 대한 응답은 캐시할 수 없다. 대상 URI에 대한 저장된 응답을 하나 이상 가진 캐시를 성공한 PUT 요청이 거쳐 가면, 그 저장된 응답들은 무효화된다([CACHING]의 4.4절 참고).
DELETE 메서드는 원본 서버가 대상 리소스와 그 현재 기능 사이의 연관을 제거해 달라고 요청한다. 사실상 이 메서드는 UNIX의 “rm” 명령과 비슷하다. 이전에 연관된 정보가 삭제되기를 기대하는 것이 아니라, 원본 서버의 URI 대응에 대한 삭제 연산을 나타낸다.
대상 리소스에 하나 이상의 현재 표현이 있다면, 그 표현들이 원본 서버에 의해 파기될 수도 있고 아닐 수도 있으며, 연관된 저장 공간이 회수될 수도 있고 아닐 수도 있다. 이는 전적으로 리소스의 성격과 원본 서버의 구현(이 명세의 범위 밖)에 달려 있다. 마찬가지로 데이터베이스나 게이트웨이 연결처럼 리소스의 다른 구현 요소도 DELETE의 결과로 비활성화되거나 보관되어야 할 수 있다. 일반적으로 원본 서버가 삭제를 수행하기 위한 정해진 메커니즘이 있는 리소스에만 DELETE를 허용한다고 가정한다.
DELETE 메서드를 허용하는 리소스는 비교적 적다. 주된 용도는 원격 저작 환경으로, 그곳에서 사용자는 그 효과에 대해 어느 정도 안내를 받는다. 예를 들어 이전에 PUT 요청으로 만들었거나, POST 요청에 대한 201 (Created) 응답 뒤 Location 헤더 필드로 식별된 리소스는, 그 동작을 되돌리기 위한 대응하는 DELETE 요청을 허용할 수 있다. 마찬가지로 원격 작업에 HTTP를 사용하는 개정 관리 클라이언트처럼 저작 기능을 구현한 맞춤형 사용자 에이전트 구현은, 서버의 URI 공간이 버전 저장소에 대응하도록 만들어졌다는 가정에 따라 DELETE를 쓸 수 있다.
DELETE 메서드가 성공적으로 적용되면, 원본 서버는 다음을 보내야 한다(SHOULD).
202 (Accepted) 상태 코드204 (No Content) 상태 코드200 (OK) 상태 코드요청 메시지 프레이밍은 사용하는 메서드와 무관하지만, DELETE 요청에서 받은 콘텐츠에는 일반적으로 정의된 의미가 없고, 요청의 의미나 대상을 바꿀 수 없으며, 요청 스머글링 공격([HTTP/1.1]의 11.2절)으로 쓰일 가능성 때문에 일부 구현이 요청을 거부하고 연결을 닫게 만들 수 있다. 클라이언트는 그런 요청에 목적이 있고 충분히 지원될 것임을 대역 내에서든 대역 외에서든 미리 알린 원본 서버로 직접 보내는 경우가 아니라면 DELETE 요청에 콘텐츠를 생성하지 않아야 한다(SHOULD NOT). HTTP 통신 참여자는 흔히 요청 사슬 위의 중개자를 알지 못하므로, 원본 서버는 콘텐츠를 받기 위해 사적 합의에 의존하지 않아야 한다(SHOULD NOT).
DELETE 메서드에 대한 응답은 캐시할 수 없다. 대상 URI에 대한 저장된 응답을 하나 이상 가진 캐시를 성공한 DELETE 요청이 거쳐 가면, 그 저장된 응답들은 무효화된다([CACHING]의 4.4절 참고).
CONNECT 메서드는 수신자가 요청 대상이 식별하는 목적지 원본 서버로 터널을 맺고, 성공하면 그 뒤로 터널이 닫힐 때까지 동작을 양방향 데이터의 맹목적 전달로 제한해 달라고 요청한다. 터널은 흔히 하나 이상의 프록시를 거치는 종단 간 가상 연결을 만드는 데 쓰이며, 그 연결은 TLS(Transport Layer Security, [TLS13])로 보안 처리할 수 있다.
CONNECT는 이 메서드에만 있는 특별한 형태의 요청 대상을 쓴다. 이 요청 대상은 터널 목적지의 호스트와 포트 번호만으로 이루어지며, 둘은 콜론으로 구분한다. 기본 포트는 없다. 클라이언트는 CONNECT 요청이 포트가 생략된 authority 구성 요소를 담은 URI 참조(4.1절)에 기반하더라도 포트 번호를 반드시 보내야 한다(MUST). 예:
CONNECT server.example.com:80 HTTP/1.1
Host: server.example.com
서버는 비어 있거나 유효하지 않은 포트 번호를 대상으로 하는 CONNECT 요청을, 보통 400 (Bad Request) 상태 코드로 응답하여 반드시 거부해야 한다(MUST).
CONNECT는 HTTP 연결의 요청/응답 성격을 바꾸므로, HTTP 버전마다 그 의미를 프로토콜의 와이어 포맷에 대응시키는 방식이 다를 수 있다.
CONNECT는 프록시로 보내는 요청에 쓰도록 의도되었다. 수신자는 요청 대상이 식별하는 서버에 직접 연결하거나, 다른 프록시를 쓰도록 설정되어 있다면 다음 인바운드 프록시로 CONNECT 요청을 전달하여 터널을 맺을 수 있다. 원본 서버는 CONNECT 요청을 받아들일 수 있지만(MAY), 대부분의 원본 서버는 CONNECT를 구현하지 않는다.
모든 2xx (Successful) 응답은 송신자(와 모든 인바운드 프록시)가 응답 헤더 섹션 직후 터널 모드로 전환한다는 것을 나타낸다. 그 헤더 섹션 뒤에 받는 데이터는 요청 대상이 식별하는 서버에서 온 것이다. 성공 응답이 아닌 모든 응답은 터널이 아직 만들어지지 않았음을 나타낸다.
터널 중개자가 어느 한쪽이 연결을 닫은 것을 감지하면 터널이 닫힌다. 중개자는 닫힌 쪽에서 온 아직 처리되지 않은 데이터를 다른 쪽으로 보내려고 반드시 시도하고, 두 연결을 모두 닫은 다음, 전달되지 않고 남은 데이터를 버려야 한다(MUST).
터널을 만들 권한을 확인하는 데 프록시 인증을 쓸 수 있다. 예:
CONNECT server.example.com:443 HTTP/1.1
Host: server.example.com:443
Proxy-Authorization: basic aGVsbG86d29ybGQ=
임의의 서버로 터널을 맺는 데는 상당한 위험이 있으며, 목적지가 웹 트래픽을 위한 것이 아닌 잘 알려진 TCP 포트나 예약된 TCP 포트라면 특히 그렇다. 예를 들어 “example.com:25”로의 CONNECT는 프록시가 SMTP 트래픽용 예약 포트로 연결하도록 요구하는 것이다. 이를 허용하면 프록시를 속여 스팸 이메일을 중계하게 할 수 있다. CONNECT를 지원하는 프록시는 그 사용을 알려진 포트의 제한된 집합이나 설정 가능한 안전한 요청 대상 목록으로 제한해야 한다(SHOULD).
서버는 CONNECT에 대한 2xx (Successful) 응답에 Transfer-Encoding이나 Content-Length 헤더 필드를 보내서는 안 된다(MUST NOT). 클라이언트는 CONNECT에 대한 성공 응답에서 받은 Content-Length나 Transfer-Encoding 헤더 필드를 반드시 무시해야 한다(MUST).
CONNECT 요청 메시지에는 콘텐츠가 없다. CONNECT 요청 메시지의 헤더 섹션 뒤에 보내는 데이터의 해석은 사용 중인 HTTP 버전에 따라 다르다.
CONNECT 메서드에 대한 응답은 캐시할 수 없다.
OPTIONS 메서드는 원본 서버나 중간에 있는 중개자에서 대상 리소스에 대해 사용할 수 있는 통신 옵션에 관한 정보를 요청한다. 이 메서드를 쓰면 클라이언트는 리소스 동작을 일으키지 않고도 리소스에 연관된 옵션이나 요구 사항, 또는 서버의 능력을 알아낼 수 있다.
요청 대상(7.1절)이 별표(““)인 OPTIONS 요청은 특정 리소스가 아니라 서버 전체에 적용된다. 서버의 통신 옵션은 보통 리소스에 따라 다르므로, “” 요청은 “ping”이나 “no-op” 유형의 메서드로만 쓸모가 있다. 클라이언트가 서버의 능력을 시험할 수 있게 하는 것 외에는 아무것도 하지 않는다. 예를 들어 이를 사용해 프록시의 HTTP/1.1 적합성(또는 그 부족)을 시험할 수 있다.
요청 대상이 별표가 아니면, OPTIONS 요청은 대상 리소스와 통신할 때 사용할 수 있는 옵션에 적용된다.
OPTIONS에 대한 성공 응답을 생성하는 서버는, 이 명세가 정의하지 않은 확장 가능성을 포함해, 서버가 구현했고 대상 리소스에 적용할 수 있는 선택적 기능을 나타낼 수 있는 헤더(예: Allow)를 모두 보내야 한다(SHOULD). 응답 콘텐츠(있다면)는 기계나 사람이 읽을 수 있는 표현으로 통신 옵션을 설명할 수도 있다. 그런 표현의 표준 형식은 이 명세가 정의하지 않지만, 앞으로의 HTTP 확장이 정의할 수 있다.
클라이언트는 요청 사슬의 특정 수신자를 대상으로 하기 위해 OPTIONS 요청에 Max-Forwards 헤더 필드를 보낼 수 있다(MAY)(7.6.2절 참고). 프록시는 Max-Forwards 필드와 함께 받은 요청이 아니라면 요청을 전달하면서 Max-Forwards 헤더 필드를 생성해서는 안 된다(MUST NOT).
콘텐츠를 담은 OPTIONS 요청을 생성하는 클라이언트는 표현의 미디어 유형을 설명하는 유효한 Content-Type 헤더 필드를 반드시 보내야 한다(MUST). 이 명세는 그런 콘텐츠의 용도를 전혀 정의하지 않는다는 점에 유의한다.
OPTIONS 메서드에 대한 응답은 캐시할 수 없다.
TRACE 메서드는 요청 메시지의 원격 애플리케이션 수준 루프백을 요청한다. 요청의 최종 수신자는 받은 메시지를, 아래에 설명하는 일부 필드를 제외하고, 200 (OK) 응답의 콘텐츠로 클라이언트에 되돌려 보내야 한다(SHOULD). “message/http” 형식([HTTP/1.1]의 10.1절)이 그렇게 하는 한 가지 방법이다. 최종 수신자는 원본 서버이거나, 요청에서 Max-Forwards 값 0을 받은 첫 서버다(7.6.2절).
클라이언트는 TRACE 요청에 응답을 통해 공개될 수 있는 민감한 데이터를 담은 필드를 생성해서는 안 된다(MUST NOT). 예를 들어 사용자 에이전트가 저장된 사용자 자격 증명(11장)이나 쿠키[COOKIE]를 TRACE 요청에 보내는 것은 어리석은 일이다. 요청의 최종 수신자는 응답 콘텐츠를 생성할 때 민감한 데이터를 담고 있을 가능성이 높은 요청 필드를 제외해야 한다(SHOULD).
TRACE를 쓰면 클라이언트는 요청 사슬의 반대쪽 끝에서 무엇을 받고 있는지 보고, 그 데이터를 시험이나 진단 정보로 쓸 수 있다. Via 헤더 필드(7.6.3절)의 값은 요청 사슬의 흔적 역할을 하므로 특히 흥미롭다. Max-Forwards 헤더 필드를 쓰면 클라이언트가 요청 사슬의 길이를 제한할 수 있는데, 이는 메시지를 무한 루프로 전달하는 프록시 사슬을 시험할 때 유용하다.
클라이언트는 TRACE 요청에 콘텐츠를 보내서는 안 된다(MUST NOT).
TRACE 메서드에 대한 응답은 캐시할 수 없다.
역주: 9장의 “ought to”(9.1·9.3.4절)와 “need to”(9.2.3·9.3.1·9.3.4절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 9.3.1~9.3.8절 제목은 메서드 이름이라 원어로 두었다. 9.3.4절의 a~c 목록은 원문의 영문자 번호를 그대로 썼다.
아래 요청 헤더 필드는 요청 뒤에 있는 사용자, 사용자 에이전트, 리소스에 관한 정보를 포함해 요청 문맥에 관한 추가 정보를 제공한다.
요청의 “Expect” 헤더 필드는 서버가 이 요청을 올바르게 처리하기 위해 지원해야 하는 특정 동작 집합, 즉 기대(expectation)를 나타낸다.
Expect = #expectation
expectation = token [ "=" ( token / quoted-string ) parameters ]
Expect 필드 값은 대소문자를 구분하지 않는다.
이 명세가 정의하는 유일한 기대는 (정의된 매개 변수가 없는) “100-continue”다.
100-continue 외의 구성원을 담은 Expect 필드 값을 받은 서버는, 예상하지 못한 기대를 충족할 수 없음을 나타내기 위해 417 (Expectation Failed) 상태 코드로 응답할 수 있다(MAY).
“100-continue” 기대는 수신자에게, 클라이언트가 이 요청에서 (아마도 큰) 콘텐츠를 곧 보내려 하며, 메서드, 대상 URI, 헤더 필드만으로 즉시 성공, 리다이렉트, 오류 응답을 일으키기에 충분하지 않다면 100 (Continue) 중간 응답을 받고 싶어 한다는 것을 알린다. 이를 통해 클라이언트는 실제로 콘텐츠를 보내기 전에 보낼 가치가 있다는 표시를 기다릴 수 있으며, 데이터가 매우 크거나 클라이언트가 오류 가능성이 높다고 예상할 때(예: 미리 검증된 인증 자격 증명 없이 상태를 바꾸는 메서드를 처음 보낼 때) 효율을 높일 수 있다.
예를 들어 다음으로 시작하는 요청은
PUT /somewhere/fun HTTP/1.1
Host: origin.example.com
Content-Type: video/h264
Content-Length: 1234567890987
Expect: 100-continue
클라이언트가 불필요한 데이터 전송으로 파이프를 채우기 시작하기 전에, 원본 서버가 401 (Unauthorized)나 405 (Method Not Allowed) 같은 오류 메시지로 즉시 응답할 수 있게 한다.
클라이언트에 대한 요구 사항:
100 (Continue) 응답을 기다릴 클라이언트는 100-continue 기대를 담은 Expect 헤더 필드를 반드시 보내야 한다(MUST).100 (Continue) 응답은 HTTP/1.0 중개자를 거쳐 보낼 수 없으므로, 그런 클라이언트는 콘텐츠를 보내기 전에 무한정 기다리지 않아야 한다(SHOULD NOT).417 (Expectation Failed) 상태 코드를 받은 클라이언트는 100-continue 기대 없이 그 요청을 반복해야 한다(SHOULD). 417 응답은 응답 사슬이 기대를 지원하지 않는다는 것(예: HTTP/1.0 서버를 거쳐 감)만 나타내기 때문이다.서버에 대한 요구 사항:
100 (Continue) 응답을 보내지 않을 수 있다(MAY).100 (Continue) 응답을 보내는 서버는, 연결이 일찍 닫히지 않는 한, 요청 콘텐츠를 받아 처리하면 결국 최종 상태 코드를 반드시 보내야 한다(MUST).메서드, 대상 URI, 그리고 100-continue 기대와 요청 콘텐츠가 뒤따른다는 표시를 담은 완전한 헤더 섹션을 가진 HTTP/1.1(이상) 요청을 받으면, 원본 서버는 반드시 다음 중 하나를 보내야 한다(MUST).
100 (Continue) 응답원본 서버는 100 (Continue) 응답을 보내기 전에 콘텐츠를 기다려서는 안 된다(MUST NOT).
메서드, 대상 URI, 그리고 100-continue 기대와 요청 콘텐츠가 뒤따른다는 표시를 담은 완전한 헤더 섹션을 가진 HTTP/1.1(이상) 요청을 받으면, 프록시는 반드시 다음 중 하나를 해야 한다(MUST).
프록시가 (설정이나 과거의 상호 작용으로부터) 다음 인바운드 서버가 HTTP/1.0만 지원한다고 믿으면, 프록시는 클라이언트가 콘텐츠를 보내기 시작하도록 유도하는 즉시 100 (Continue) 응답을 생성할 수 있다(MAY).
“From” 헤더 필드는 요청하는 사용자 에이전트를 통제하는 사람 사용자의 인터넷 이메일 주소를 담는다. 이 주소는 [RFC5322]의 3.4절에 “mailbox”로 정의된 것처럼 기계가 사용할 수 있어야 마땅하다.
From = mailbox
mailbox = <mailbox, see [RFC5322], Section 3.4>
예:
From: spider-admin@example.org
로봇이 아닌 사용자 에이전트가 From 헤더 필드를 보내는 경우는 드물다. 사용자 에이전트는 사용자가 명시적으로 설정하지 않았다면 From 헤더 필드를 보내지 않아야 한다(SHOULD NOT). 사용자의 개인 정보 보호 이익이나 사용자 쪽 사이트의 보안 정책과 충돌할 수 있기 때문이다.
로봇 사용자 에이전트는, 로봇이 과도하거나 원치 않거나 유효하지 않은 요청을 보내는 것처럼 서버에 문제가 생길 경우 로봇 운영 책임자에게 연락할 수 있도록 유효한 From 헤더 필드를 보내야 한다(SHOULD).
서버는 From 헤더 필드를 접근 제어나 인증에 쓰지 않아야 한다(SHOULD NOT). 그 값은 요청을 받거나 관찰하는 누구에게나 보일 것으로 예상되며, 개인 정보 보호에 대한 기대 없이 로그 파일과 오류 보고서에 기록되는 경우가 많기 때문이다.
“Referer”[sic] 헤더 필드는 사용자 에이전트가 대상 URI를 얻은 리소스의 URI 참조를 지정할 수 있게 한다(즉, “리퍼러(referrer)”다. 다만 필드 이름의 철자가 틀렸다). 사용자 에이전트는 Referer 필드 값을 생성할 때 URI 참조[URI]의 프래그먼트와 userinfo 구성 요소(있다면)를 포함해서는 안 된다(MUST NOT).
Referer = absolute-URI / partial-URI
필드 값은 absolute-URI이거나 partial-URI다. 후자의 경우(4장) 참조된 URI는 대상 URI를 기준으로 한 상대 URI다([URI]의 5장).
Referer 헤더 필드를 쓰면 서버는 간단한 분석, 로그 기록, 최적화된 캐싱 등을 위해 다른 리소스로 가는 역링크를 생성할 수 있다. 또한 유지 보수를 위해 폐기되었거나 잘못 입력된 링크를 찾을 수 있다. 일부 서버는 Referer 헤더 필드를 다른 사이트로부터의 링크(이른바 “딥 링킹(deep linking)”)를 거부하거나 사이트 간 요청 위조(CSRF)를 제한하는 수단으로 쓰지만, 모든 요청에 이 필드가 있는 것은 아니다.
예:
Referer: http://www.example.org/hypertext/Overview.html
대상 URI를 자체 URI가 없는 원천(예: 사용자 키보드 입력, 사용자의 북마크/즐겨찾기 항목)에서 얻었다면, 사용자 에이전트는 반드시 Referer 헤더 필드를 빼거나 “about:blank” 값으로 보내야 한다(MUST).
Referer 헤더 필드 값이 참조한 리소스의 전체 URI를 전달할 필요는 없다. 사용자 에이전트는 참조한 리소스의 출처 외의 부분을 잘라 낼 수 있다(MAY).
Referer 헤더 필드는 사용자의 요청 문맥이나 탐색 기록에 관한 정보를 드러낼 가능성이 있으며, 참조한 리소스의 식별자가 개인 정보(예: 계정 이름)나 기밀이어야 할 리소스(예: 방화벽 뒤에 있거나 보안 처리된 서비스 내부에 있는 리소스)를 드러낸다면 개인 정보 보호 문제가 된다. 대부분의 범용 사용자 에이전트는 참조한 리소스가 로컬 “file”이나 “data” URI이면 Referer 헤더 필드를 보내지 않는다. 참조한 리소스에 보안 프로토콜로 접근했고 요청 대상의 출처가 참조한 리소스의 출처와 다르다면, 참조한 리소스가 Referer를 보내도록 명시적으로 허용하지 않는 한 사용자 에이전트는 Referer 헤더 필드를 보내지 않아야 한다(SHOULD NOT). 참조한 리소스에 보안 프로토콜로 접근했다면, 사용자 에이전트는 보안 처리되지 않은 HTTP 요청에 Referer 헤더 필드를 보내서는 안 된다(MUST NOT). 추가 보안 고려 사항은 17.9절을 참고한다.
일부 중개자는 나가는 요청에서 Referer 헤더 필드를 무분별하게 제거하는 것으로 알려져 있다. 이는 CSRF 공격에 대한 보호를 방해하는 불행한 부작용을 낳으며, CSRF 공격은 그 사용자에게 훨씬 더 해로울 수 있다. Referer의 정보 공개를 제한하려는 중개자와 사용자 에이전트 확장은 내부 도메인 이름을 가명으로 바꾸거나 쿼리와 경로 구성 요소를 잘라 내는 것처럼 특정한 편집으로 변경을 제한해야 마땅하다. 중개자는 필드 값이 대상 URI와 같은 스킴과 호스트를 공유할 때 Referer 헤더 필드를 수정하거나 삭제하지 않아야 한다(SHOULD NOT).
“TE” 헤더 필드는 전송 코딩과 트레일러 섹션에 관한 클라이언트의 능력을 설명한다.
6.5절에서 설명했듯이, 요청에서 보낸 “trailers” 구성원이 있는 TE 필드는 클라이언트가 트레일러 필드를 버리지 않을 것임을 나타낸다.
TE는 HTTP/1.1에서 클라이언트가 응답에서 어떤 전송 코딩을 받아들일 수 있는지 서버에 알리는 데에도 쓰인다. 발행 시점에는 HTTP/1.1만 전송 코딩을 쓴다([HTTP/1.1]의 7장 참고).
TE 필드 값은 구성원의 목록이며, 각 구성원(“trailers” 제외)은 전송 코딩 이름 토큰과, 그 전송 코딩에 대한 클라이언트의 상대적 선호도를 나타내는 선택적 가중치(12.4.2절), 그 전송 코딩의 선택적 매개 변수로 이루어진다.
TE = #t-codings
t-codings = "trailers" / ( transfer-coding [ weight ] )
transfer-coding = token *( OWS ";" OWS transfer-parameter )
transfer-parameter = token BWS "=" BWS ( token / quoted-string )
TE를 보내는 송신자는 중개자가 이 필드를 전달하지 않도록 알리기 위해 Connection 헤더 필드(7.6.1절) 안에 “TE” 연결 옵션도 반드시 보내야 한다(MUST).
“User-Agent” 헤더 필드는 요청을 시작한 사용자 에이전트에 관한 정보를 담는다. 서버는 이 정보를 보고된 상호 운용성 문제의 범위를 파악하거나, 특정 사용자 에이전트의 한계를 피하도록 우회하거나 응답을 맞추거나, 브라우저나 운영 체제 사용에 관한 분석에 흔히 쓴다. 사용자 에이전트는 그렇게 하지 않도록 특별히 설정되지 않았다면 각 요청에 User-Agent 헤더 필드를 보내야 한다(SHOULD).
User-Agent = product *( RWS ( product / comment ) )
User-Agent 필드 값은 하나 이상의 제품 식별자로 이루어지며, 각 제품 식별자 뒤에는 0개 이상의 주석(5.6.5절)이 오고, 이들이 함께 사용자 에이전트 소프트웨어와 그 주요 하위 제품을 식별한다. 관례상 제품 식별자는 사용자 에이전트 소프트웨어를 식별하는 데 중요한 순서대로 나열한다. 각 제품 식별자는 이름과 선택적 버전으로 이루어진다.
product = token ["/" product-version]
product-version = token
송신자는 생성하는 제품 식별자를 제품을 식별하는 데 필요한 정도로 제한해야 한다(SHOULD). 송신자는 제품 식별자 안에 광고나 그 밖의 필수적이지 않은 정보를 생성해서는 안 된다(MUST NOT). 송신자는 product-version에 버전 식별자가 아닌 정보를 생성하지 않아야 한다(SHOULD NOT)(즉, 같은 제품 이름의 이후 버전은 제품 식별자의 product-version 부분만 달라야 마땅하다).
예:
User-Agent: CERN-LineMode/2.15 libwww/2.17b3
사용자 에이전트는 쓸데없이 세세한 정보를 담은 User-Agent 헤더 필드를 생성하지 않아야 하며(SHOULD NOT), 제3자가 하위 제품을 추가하는 것을 제한해야 한다(SHOULD). 지나치게 길고 자세한 User-Agent 필드 값은 요청 지연 시간을 늘리고, 사용자가 원치 않는데도 식별될 위험(“핑거프린팅(fingerprinting)”)을 높인다.
마찬가지로 구현은 다른 구현과의 호환성을 선언하기 위해 다른 구현의 제품 토큰을 쓰지 않을 것이 권장된다. 그렇게 하면 이 필드의 목적을 회피하게 되기 때문이다. 사용자 에이전트가 다른 사용자 에이전트로 위장한다면, 수신자는 실제로 쓰이는 사용자 에이전트에서는 잘 동작하지 않을 수 있더라도, 사용자가 그 식별된 사용자 에이전트에 맞춘 응답을 의도적으로 원한다고 가정할 수 있다.
아래 응답 헤더 필드는 서버, 대상 리소스, 관련 리소스에 관한 정보를 포함해, 상태 코드가 암시하는 것 이상의 응답에 관한 추가 정보를 제공한다.
“Allow” 헤더 필드는 대상 리소스가 지원한다고 알리는 메서드 집합을 나열한다. 이 필드의 목적은 오로지 수신자에게 리소스에 연관된 유효한 요청 메서드를 알리는 것이다.
Allow = #method
사용 예:
Allow: GET, HEAD, PUT
실제로 허용되는 메서드 집합은 각 요청 시점에 원본 서버가 정한다. 원본 서버는 405 (Method Not Allowed) 응답에 Allow 헤더 필드를 반드시 생성해야 하며(MUST), 그 밖의 응답에도 생성할 수 있다(MAY). 빈 Allow 필드 값은 리소스가 어떤 메서드도 허용하지 않음을 나타내며, 이는 설정에 의해 리소스가 일시적으로 비활성화된 경우의 405 응답에서 나타날 수 있다.
프록시는 Allow 헤더 필드를 수정해서는 안 된다(MUST NOT). 프록시는 일반적인 메시지 처리 규칙에 따라 메서드를 처리하기 위해 표시된 메서드를 모두 이해할 필요가 없다.
“Location” 헤더 필드는 일부 응답에서 그 응답과 관련된 특정 리소스를 가리키는 데 쓰인다. 관계의 유형은 요청 메서드와 상태 코드 의미의 조합으로 정의된다.
Location = URI-reference
필드 값은 하나의 URI-reference로 이루어진다. 상대 참조([URI]의 4.2절) 형태이면, 최종 값은 대상 URI를 기준으로 해석하여 계산한다([URI]의 5장).
201 (Created) 응답에서 Location 값은 요청으로 생성된 주 리소스를 가리킨다. 3xx (Redirection) 응답에서 Location 값은 요청을 자동으로 리다이렉트하기 위한 선호 대상 리소스를 가리킨다.
3xx (Redirection) 응답에서 제공된 Location 값에 프래그먼트 구성 요소가 없으면, 사용자 에이전트는 그 값이 대상 URI를 생성하는 데 쓴 URI 참조의 프래그먼트 구성 요소를 물려받는 것처럼 리다이렉트를 반드시 처리해야 한다(MUST)(즉, 리다이렉트는 원래 참조의 프래그먼트(있다면)를 물려받는다).
예를 들어 URI 참조 “http://www.example.org/~tim”에 대해 생성된 GET 요청은 다음 헤더 필드를 담은 303 (See Other) 응답을 받을 수 있다.
Location: /People.html#tim
이는 사용자 에이전트가 “http://www.example.org/People.html#tim”으로 리다이렉트할 것을 시사한다.
마찬가지로 URI 참조 “http://www.example.org/index.html#larry”에 대해 생성된 GET 요청은 다음 헤더 필드를 담은 301 (Moved Permanently) 응답을 받을 수 있다.
Location: http://www.example.net/index.html
이는 원래의 프래그먼트 식별자를 보존해 사용자 에이전트가 “http://www.example.net/index.html#larry”로 리다이렉트할 것을 시사한다.
Location 값의 프래그먼트 식별자가 적절하지 않은 경우도 있다. 예를 들어 201 (Created) 응답의 Location 헤더 필드는 생성된 리소스에 특정한 URI를 제공하게 되어 있다.
참고: 일부 수신자는 유효한 URI 참조가 아닌
Location헤더 필드로부터 복구하려 시도한다. 이 명세는 그런 처리를 의무화하거나 정의하지 않지만, 강건성을 위해 허용한다. 쉼표 목록 구분자가URI-reference안에서 유효한 데이터 문자이므로Location필드 값은 구성원 목록을 허용할 수 없다. 여러Location필드 줄을 담은 유효하지 않은 메시지를 보내면, 경로 위의 수신자가 그 필드 줄들을 하나의 값으로 결합할 수 있다. 그런 상황에서 유효한Location필드 값을 복구하는 것은 어렵고 구현 간에 상호 운용되지 않는다.
참고:
Content-Location헤더 필드(8.7절)는 담긴 표현에 대응하는 가장 구체적인 리소스를 가리킨다는 점에서Location과 다르다. 따라서 응답에Location과Content-Location헤더 필드가 모두 있을 수 있다.
서버는 사용자 에이전트가 후속 요청을 하기 전에 얼마나 기다려야 하는지 나타내기 위해 “Retry-After” 헤더 필드를 보낸다. 503 (Service Unavailable) 응답과 함께 보내면, Retry-After는 클라이언트가 서비스를 이용할 수 없을 것으로 예상되는 기간을 나타낸다. 3xx (Redirection) 응답과 함께 보내면, Retry-After는 사용자 에이전트가 리다이렉트된 요청을 보내기 전에 기다리도록 요청받는 최소 시간을 나타낸다.
Retry-After 필드 값은 HTTP-date이거나, 응답을 받은 뒤 지연할 초 수일 수 있다.
Retry-After = HTTP-date / delay-seconds
delay-seconds 값은 초 단위 시간을 나타내는 음이 아닌 십진 정수다.
delay-seconds = 1*DIGIT
사용 예 두 가지는 다음과 같다.
Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
Retry-After: 120
뒤의 예에서 지연은 2분이다.
“Server” 헤더 필드는 원본 서버가 요청을 처리하는 데 쓴 소프트웨어에 관한 정보를 담는다. 클라이언트는 이 정보를 보고된 상호 운용성 문제의 범위를 파악하거나, 특정 서버의 한계를 피하도록 우회하거나 요청을 맞추거나, 서버나 운영 체제 사용에 관한 분석에 흔히 쓴다. 원본 서버는 응답에 Server 헤더 필드를 생성할 수 있다(MAY).
Server = product *( RWS ( product / comment ) )
Server 헤더 필드 값은 하나 이상의 제품 식별자로 이루어지며, 각 제품 식별자 뒤에는 0개 이상의 주석(5.6.5절)이 오고, 이들이 함께 원본 서버 소프트웨어와 그 주요 하위 제품을 식별한다. 관례상 제품 식별자는 원본 서버 소프트웨어를 식별하는 데 중요한 순서대로 나열한다. 각 제품 식별자는 10.1.5절에 정의된 대로 이름과 선택적 버전으로 이루어진다.
예:
Server: CERN/3.0 libwww/2.17
원본 서버는 쓸데없이 세세한 정보를 담은 Server 헤더 필드를 생성하지 않아야 하며(SHOULD NOT), 제3자가 하위 제품을 추가하는 것을 제한해야 한다(SHOULD). 지나치게 길고 자세한 Server 필드 값은 응답 지연 시간을 늘리고, 공격자가 알려진 보안 취약점을 찾아 악용하기를 (조금) 더 쉽게 만들 수 있는 내부 구현 세부 사항을 드러낼 가능성이 있다.
역주: 10장의 “ought to”(10.1.2·10.1.3·10.1.5·10.2.3절)와 “are encouraged not to”(10.1.5절), “need to”와 “is not required to”(10.1.1절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 10.1.1~10.1.5절과 10.2.1~10.2.4절 제목은 헤더 필드 이름이라 원어로 두었다. 10.1.3절의 “[sic]”은 원문 표기를 그대로 두었다.
HTTP는 확장 가능한 챌린지-응답(challenge-response) 인증 스킴 집합을 통해 접근 제어와 인증을 위한 범용 프레임워크를 제공한다. 서버는 이 스킴으로 클라이언트 요청에 챌린지를 보내고, 클라이언트는 인증 정보를 제공할 수 있다. 인증 스킴은 대소문자를 구분하지 않는 토큰으로 식별한다.
auth-scheme = token
이 문서는 범용 프레임워크 외에는 어떤 인증 스킴도 명세하지 않는다. 새 인증 스킴과 기존 인증 스킴은 따로 명세되며, “HTTP 인증 스킴 레지스트리(Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry)”에 등록해야 마땅하다. 예를 들어 “basic”과 “digest” 인증 스킴은 각각 [RFC7617]과 [RFC7616]에 정의되어 있다.
인증 스킴 뒤에는 그 스킴으로 인증을 이루는 데 필요한 추가 정보가 온다. 이 정보는 쉼표로 구분된 매개 변수 목록이거나, base64로 인코딩된 정보를 담을 수 있는 하나의 문자 시퀀스다.
token68 = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="
token68 구문은 66개의 예약되지 않은 URI 문자([URI])와 몇 가지 다른 문자를 허용하여, 패딩이 있든 없든 공백을 제외한 base64, base64url(URL과 파일 이름에 안전한 알파벳), base32, base16(16진수) 인코딩을 담을 수 있다([RFC4648]).
인증 매개 변수는 이름/값 쌍이며, 이름 토큰은 대소문자를 구분하지 않고 비교하고, 각 매개 변수 이름은 하나의 챌린지에 반드시 한 번만 나와야 한다(MUST).
auth-param = token BWS "=" BWS ( token / quoted-string )
매개 변수 값은 “token”이나 “quoted-string”(5.6절)으로 나타낼 수 있다. 수신자가 인증 스킴과 상관없이 범용 파싱 구성 요소를 쓸 수 있도록, 인증 스킴 정의는 송신자와 수신자 모두에 대해 두 표기법을 받아들여야 한다.
하위 호환성을 위해, 인증 스킴 정의는 송신자가 쓸 형식을 둘 중 하나로 제한할 수 있다. 이는 배포된 구현이 두 형식 중 하나를 만나면 실패한다는 것이 알려져 있을 때 중요할 수 있다.
원본 서버는 사용자 에이전트의 인가를 요구하기 위해 401 (Unauthorized) 응답 메시지를 사용하며, 이 응답에는 요청한 리소스에 적용할 수 있는 챌린지를 적어도 하나 담은 WWW-Authenticate 헤더 필드가 들어 있다.
프록시는 클라이언트의 인가를 요구하기 위해 407 (Proxy Authentication Required) 응답 메시지를 사용하며, 이 응답에는 요청한 리소스에 대해 그 프록시에 적용할 수 있는 챌린지를 적어도 하나 담은 Proxy-Authenticate 헤더 필드가 들어 있다.
challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
참고: 많은 클라이언트는 알 수 없는 스킴을 담은 챌린지를 파싱하지 못한다. 이 문제의 우회책은 잘 지원되는 스킴(예: “basic”)을 먼저 나열하는 것이다.
원본 서버에 자신을 인증하려는 사용자 에이전트는, 보통 401 (Unauthorized)를 받은 뒤에(반드시 그럴 필요는 없다) 요청에 Authorization 헤더 필드를 넣어 인증할 수 있다.
프록시에 자신을 인증하려는 클라이언트는, 보통 407 (Proxy Authentication Required)를 받은 뒤에(반드시 그럴 필요는 없다) 요청에 Proxy-Authorization 헤더 필드를 넣어 인증할 수 있다.
Authorization 필드 값과 Proxy-Authorization 필드 값은 모두 응답에서 받은 (과거 어느 시점에 받았을 수도 있는) 챌린지에 기반한, 요청하는 리소스의 영역(realm)에 대한 클라이언트의 자격 증명을 담는다. 사용자 에이전트는 이 값을 만들 때, 자신이 이해하는 것 가운데 가장 안전하다고 여기는 auth-scheme을 가진 챌린지를 고르고, 필요에 따라 사용자로부터 자격 증명을 얻어 만들어야 마땅하다. 17.16.1절에서 설명하듯이, 헤더 필드 값 안에 자격 증명을 담아 전송하는 것은 기반이 되는 연결의 기밀성에 관해 중요한 보안 고려 사항을 수반한다.
credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
보호된 리소스에 대한 요청이 자격 증명을 빠뜨렸거나, 유효하지 않은 자격 증명(예: 잘못된 비밀번호)이나 부분적인 자격 증명(예: 인증 스킴이 왕복을 두 번 이상 요구할 때)을 담고 있으면, 원본 서버는 요청한 리소스에 적용할 수 있는 (새로울 수도 있는) 챌린지를 적어도 하나 담은 WWW-Authenticate 헤더 필드가 있는 401 (Unauthorized) 응답을 보내야 한다(SHOULD).
마찬가지로 프록시 자격 증명을 빠뜨렸거나 유효하지 않거나 부분적인 프록시 자격 증명을 담은 요청을 받으면, 인증을 요구하는 프록시는 그 프록시에 적용할 수 있는 (새로울 수도 있는) 챌린지를 적어도 하나 담은 Proxy-Authenticate 헤더 필드가 있는 407 (Proxy Authentication Required) 응답을 생성해야 한다(SHOULD).
유효하지만 접근 권한을 얻기에는 충분하지 않은 자격 증명을 받은 서버는 403 (Forbidden) 상태 코드(15.5.4절)로 응답해야 마땅하다.
HTTP는 애플리케이션이 접근 인증을 위해 이 단순한 챌린지-응답 프레임워크만 쓰도록 제한하지 않는다. 전송 수준의 인증이나 메시지 캡슐화를 통한 인증, 그리고 인증 정보를 지정하는 추가 헤더 필드처럼 다른 메커니즘을 쓸 수 있다. 그러나 그런 추가 메커니즘은 이 명세가 정의하지 않는다.
사용자 인증을 위한 다양한 맞춤형 메커니즘이 인증 관련 토큰을 전달하기 위해 [COOKIE]에 정의된 Set-Cookie와 Cookie 헤더 필드를 사용한다는 점에 유의한다.
“realm” 인증 매개 변수는 보호 범위를 나타내려는 인증 스킴이 쓰도록 예약되어 있다.
“보호 공간(protection space)”은 접근하는 서버의 출처(4.3.1절 참고)와, 영역 값이 있다면 그 값을 함께 써서 정의한다. 이런 영역을 쓰면 서버의 보호된 리소스를 보호 공간의 집합으로 나눌 수 있으며, 각 보호 공간은 자체 인증 스킴이나 인가 데이터베이스를 가진다. 영역 값은 보통 원본 서버가 부여하는 문자열이며, 인증 스킴에 특정한 추가 의미를 가질 수 있다. 하나의 응답에 auth-scheme은 같지만 영역이 다른 챌린지가 여러 개 있을 수 있다는 점에 유의한다.
보호 공간은 자격 증명을 자동으로 적용할 수 있는 범위를 정한다. 이전 요청이 인가되었다면, 사용자 에이전트는 인증 스킴, 매개 변수, 사용자 선호(예: 설정 가능한 비활성 타임아웃)에 따라 정해지는 기간 동안 그 보호 공간 안의 다른 모든 요청에 같은 자격 증명을 재사용할 수 있다(MAY).
보호 공간의 범위, 따라서 자격 증명이 자동으로 적용될 수 있는 요청의 범위는 추가 정보 없이는 클라이언트가 반드시 알 수 있는 것이 아니다. 인증 스킴은 보호 공간의 범위를 설명하는 매개 변수를 정의할 수 있다. 인증 스킴이 특별히 허용하지 않는 한, 하나의 보호 공간은 그 서버의 범위를 넘어설 수 없다.
역사적인 이유로, 송신자는 반드시 quoted-string 구문만 생성해야 한다(MUST). 오랫동안 두 표기법을 모두 받아들여 온 기존 클라이언트와 최대한 상호 운용하기 위해, 수신자는 token과 quoted-string 구문을 모두 지원해야 할 수도 있다.
“WWW-Authenticate” 응답 헤더 필드는 대상 리소스에 적용할 수 있는 인증 스킴(들)과 매개 변수를 나타낸다.
WWW-Authenticate = #challenge
401 (Unauthorized) 응답을 생성하는 서버는 적어도 하나의 챌린지를 담은 WWW-Authenticate 헤더 필드를 반드시 보내야 한다(MUST). 서버는 자격 증명(또는 다른 자격 증명)을 제공하면 응답이 달라질 수 있음을 나타내기 위해 그 밖의 응답 메시지에도 WWW-Authenticate 헤더 필드를 생성할 수 있다(MAY).
응답을 전달하는 프록시는 그 응답의 WWW-Authenticate 헤더 필드를 수정해서는 안 된다(MUST NOT).
필드 값에는 챌린지가 둘 이상 있을 수 있고 각 챌린지에는 쉼표로 구분된 인증 매개 변수 목록이 있을 수 있으므로, 사용자 에이전트는 필드 값을 파싱할 때 특히 주의하는 것이 좋다. 게다가 헤더 필드 자체가 여러 번 나타날 수 있다.
예:
WWW-Authenticate: Basic realm="simple", Newauth realm="apps",
type=1, title="Login to \"apps\""
이 헤더 필드에는 챌린지가 두 개 있다. 하나는 영역 값이 “simple”인 “Basic” 스킴의 챌린지이고, 다른 하나는 영역 값이 “apps”인 “Newauth” 스킴의 챌린지다. 또한 추가 매개 변수 두 개, “type”과 “title”이 들어 있다.
그러나 일부 사용자 에이전트는 이 형식을 인식하지 못한다. 따라서 같은 필드 줄에 구성원이 둘 이상인 WWW-Authenticate 필드 값을 보내면 상호 운용되지 않을 수 있다.
참고:
challenge문법 생성 규칙도 목록 구문을 사용한다. 따라서 쉼표, 공백, 쉼표가 이어진 시퀀스는 앞 챌린지에 적용되는 것으로 볼 수도 있고, 챌린지 목록의 빈 항목으로 볼 수도 있다. 실제로 이 모호함은 헤더 필드 값의 의미에 영향을 주지 않으므로 해가 없다.
“Authorization” 헤더 필드는 사용자 에이전트가, 보통 401 (Unauthorized) 응답을 받은 뒤에(반드시 그럴 필요는 없다) 원본 서버에 자신을 인증할 수 있게 한다. 그 값은 요청하는 리소스의 영역에 대한 사용자 에이전트의 인증 정보를 담은 자격 증명으로 이루어진다.
Authorization = credentials
요청이 인증되고 영역이 지정되었다면, 같은 자격 증명은 이 영역 안의 다른 모든 요청에도 유효하다고 가정한다(챌린지 값에 따라 달라지는 자격 증명이나 동기화된 시계를 쓰는 자격 증명처럼, 인증 스킴 자체가 달리 요구하지 않는다고 가정할 때).
요청을 전달하는 프록시는 그 요청의 Authorization 헤더 필드를 수정해서는 안 된다(MUST NOT). HTTP 캐시의 Authorization 헤더 필드 처리에 관한 세부 사항과 요구 사항은 [CACHING]의 3.5절을 참고한다.
HTTP 인증 스킴은 클라이언트의 인증 자격 증명이 받아들여진 뒤 정보를 전달하기 위해 “Authentication-Info” 응답 필드를 쓸 수 있다. 이 정보에는 서버의 마무리 메시지가 들어 있을 수 있다(예: 서버 인증을 담을 수 있다).
필드 값은 11.3절에 정의된 auth-param 구문을 사용하는 매개 변수(이름/값 쌍) 목록이다. 이 명세는 일반 형식만 설명하며, 개별 매개 변수는 Authentication-Info를 사용하는 인증 스킴이 정의한다. 예를 들어 “Digest” 인증 스킴은 [RFC7616]의 3.5절에서 여러 매개 변수를 정의한다.
Authentication-Info = #auth-param
Authentication-Info 필드는 요청 메서드나 상태 코드와 상관없이 어떤 HTTP 응답에서든 쓸 수 있다. 그 의미는 해당 요청의 Authorization 헤더 필드(11.6.2절)가 나타내는 인증 스킴이 정의한다.
응답을 전달하는 프록시는 필드 값을 어떤 식으로든 수정할 수 없다.
인증 스킴이 명시적으로 허용하면 Authentication-Info를 트레일러 필드(6.5절)로 보낼 수 있다.
역주: 11.6.3절 둘째 문단의 “defined in Section 11.3”은 원문 그대로 옮겼다.
auth-param구문은 실제로 11.2절에 정의되어 있다. 이 차이에 대한 정오표는 2026-10-17 기준으로 등록되어 있지 않다.
“Proxy-Authenticate” 헤더 필드는 이 요청에 대해 프록시에 적용할 수 있는 인증 스킴(들)과 매개 변수를 나타내는 챌린지를 적어도 하나 담는다. 프록시는 자신이 생성하는 각 407 (Proxy Authentication Required) 응답에 적어도 하나의 Proxy-Authenticate 헤더 필드를 반드시 보내야 한다(MUST).
Proxy-Authenticate = #challenge
WWW-Authenticate와 달리 Proxy-Authenticate 헤더 필드는 응답 사슬의 다음 아웃바운드 클라이언트에만 적용된다. 주어진 프록시를 고른 클라이언트만 인증에 필요한 자격 증명을 가지고 있을 가능성이 높기 때문이다. 그러나 대기업 네트워크 안의 사무실 캐싱 프록시와 지역 캐싱 프록시처럼 같은 관리 도메인 안에서 여러 프록시를 쓸 때는, 사용자 에이전트가 자격 증명을 생성해 소비될 때까지 계층을 따라 전달하는 경우가 흔하다. 따라서 그런 구성에서는 각 프록시가 같은 챌린지 집합을 보내므로 Proxy-Authenticate가 전달되는 것처럼 보인다.
WWW-Authenticate에 대한 파싱 고려 사항은 이 헤더 필드에도 적용된다는 점에 유의한다. 자세한 내용은 11.6.1절을 참고한다.
“Proxy-Authorization” 헤더 필드는 클라이언트가 인증을 요구하는 프록시에 자신(또는 그 사용자)을 식별할 수 있게 한다. 그 값은 프록시나 요청하는 리소스의 영역에 대한 클라이언트의 인증 정보를 담은 자격 증명으로 이루어진다.
Proxy-Authorization = credentials
Authorization과 달리 Proxy-Authorization 헤더 필드는 Proxy-Authenticate 헤더 필드로 인증을 요구한 다음 인바운드 프록시에만 적용된다. 사슬에서 여러 프록시를 쓸 때, Proxy-Authorization 헤더 필드는 자격 증명을 받을 것으로 예상하던 첫 인바운드 프록시가 소비한다. 프록시들이 협력해서 주어진 요청을 인증하는 메커니즘이라면, 프록시는 클라이언트 요청의 자격 증명을 다음 프록시로 중계할 수 있다(MAY).
“Proxy-Authentication-Info” 응답 헤더 필드는 Authentication-Info와 같지만, 프록시 인증(11.3절)에 적용되며 그 의미는 해당 요청의 Proxy-Authorization 헤더 필드(11.7.2절)가 나타내는 인증 스킴이 정의한다는 점이 다르다.
Proxy-Authentication-Info = #auth-param
그러나 Authentication-Info와 달리 Proxy-Authentication-Info 헤더 필드는 응답 사슬의 다음 아웃바운드 클라이언트에만 적용된다. 주어진 프록시를 고른 클라이언트만 인증에 필요한 자격 증명을 가지고 있을 가능성이 높기 때문이다. 그러나 대기업 네트워크 안의 사무실 캐싱 프록시와 지역 캐싱 프록시처럼 같은 관리 도메인 안에서 여러 프록시를 쓸 때는, 사용자 에이전트가 자격 증명을 생성해 소비될 때까지 계층을 따라 전달하는 경우가 흔하다. 따라서 그런 구성에서는 각 프록시가 같은 필드 값을 보내므로 Proxy-Authentication-Info가 전달되는 것처럼 보인다.
인증 스킴이 명시적으로 허용하면 Proxy-Authentication-Info를 트레일러 필드(6.5절)로 보낼 수 있다.
역주: 11장의 “ought to”(11.1·11.4절), “need to”(11.2절), “are advised to”(11.6.1절), “might have to”(11.5절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 11.6.1~11.6.3절과 11.7.1~11.7.3절 제목은 헤더 필드 이름이라 원어로 두었다. 11.6.3절의 “is not allowed to”(프록시의 수정 금지)도 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
realm매개 변수 이름과 “basic”, “digest” 같은 스킴 이름은 원어로 두었다.
응답이 콘텐츠를 전달할 때, 그것이 성공을 나타내든 오류를 나타내든, 원본 서버는 그 정보를 표현하는 여러 방법을 가지고 있는 경우가 많다. 예를 들어 형식, 언어, 인코딩이 다를 수 있다. 마찬가지로 사용자나 사용자 에이전트마다 능력, 특성, 선호가 달라서, 사용할 수 있는 표현 가운데 어느 것을 전달하는 것이 가장 좋은지에 영향을 줄 수 있다. 이런 이유로 HTTP는 콘텐츠 협상 메커니즘을 제공한다.
이 명세는 프로토콜 안에서 드러날 수 있는 콘텐츠 협상의 세 가지 패턴을 정의한다. 사용자 에이전트가 밝힌 선호에 따라 서버가 표현을 선택하는 “사전(proactive)” 협상, 사용자 에이전트가 고를 수 있도록 서버가 표현 목록을 제공하는 “사후(reactive)” 협상, 그리고 과거 응답에서 서버가 밝힌 선호에 따라 사용자 에이전트가 이후 요청의 표현을 선택하는 “요청 콘텐츠(request content)” 협상이다.
콘텐츠 협상의 다른 패턴으로는 표현이 여러 부분으로 이루어져 사용자 에이전트 매개 변수에 따라 선택적으로 렌더링되는 “조건부 콘텐츠(conditional content)”, 표현에 사용자 에이전트 특성에 따라 추가적인(더 구체적인) 요청을 하는 스크립트가 들어 있는 “능동 콘텐츠(active content)”, 그리고 중개자가 콘텐츠 선택을 수행하는 “투명한 콘텐츠 협상(Transparent Content Negotiation)”([RFC2295])이 있다. 이 패턴들은 서로 배타적이지 않으며, 각각 적용 가능성과 실용성 면에서 장단점이 있다.
모든 경우에 HTTP는 리소스의 의미를 알지 못한다는 점에 유의한다. 원본 서버가 시간에 따라, 그리고 콘텐츠 협상의 여러 차원에 걸쳐 요청에 얼마나 일관되게 응답하는지, 따라서 시간이 지나도 관찰되는 리소스 표현들의 “동일성”은 전적으로 그 응답을 선택하거나 생성하는 주체나 알고리즘이 결정한다.
서버에 있는 알고리즘이 선호되는 표현을 고르도록 유도하기 위해 사용자 에이전트가 요청에 콘텐츠 협상 선호를 담아 보내는 것을 “사전 협상(proactive negotiation)”(일명 “서버 주도 협상(server-driven negotiation)”)이라고 한다. 선택은 응답에 사용할 수 있는 표현들(언어, 콘텐츠 코딩 등 응답이 달라질 수 있는 차원)을, 아래의 명시적인 협상 헤더 필드와 클라이언트의 네트워크 주소나 User-Agent 필드의 일부 같은 암묵적인 특성을 포함한 요청의 다양한 정보와 비교하여 이루어진다.
사전 협상은 사용할 수 있는 표현 가운데 하나를 고르는 알고리즘을 사용자 에이전트에 설명하기 어려울 때, 또는 서버가 첫 응답과 함께 “최선의 추측”을 사용자 에이전트에 보내고 싶을 때 유리하다(그 “최선의 추측”이 사용자에게 충분히 좋다면 이후 요청의 왕복 지연을 피할 수 있다). 서버의 추측을 개선하기 위해, 사용자 에이전트는 자신의 선호를 설명하는 요청 헤더 필드를 보낼 수 있다(MAY).
사전 협상에는 심각한 단점이 있다.
원본 서버가 요청한 리소스에 사전 협상을 구현하지 않았을 수도 있고, 사용자 에이전트의 선호에 맞지 않는 응답을 보내는 것이 406 (Not Acceptable) 응답을 보내는 것보다 낫다고 판단할 수도 있으므로, 사용자 에이전트는 사전 협상 선호가 일관되게 존중되리라고 기대할 수 없다.
사전 협상의 대상인 응답에는 요청 정보의 어느 부분이 선택 알고리즘에 쓰였는지 나타내기 위해 Vary 헤더 필드(12.5.5절)를 보내는 경우가 많다.
사용자 에이전트가 응답 콘텐츠의 사전 협상에 참여할 수 있도록 요청 헤더 필드 Accept, Accept-Charset, Accept-Encoding, Accept-Language를 아래에서 정의한다. 이 필드들로 보낸 선호는 대상 리소스의 표현, 오류나 처리 상태의 표현, 그리고 프로토콜 안에 나타날 수 있는 갖가지 텍스트 문자열까지 포함해 응답의 모든 콘텐츠에 적용된다.
“사후 협상(reactive negotiation)”(일명 “에이전트 주도 협상(agent-driven negotiation)”)에서는 사용자 에이전트가 첫 응답을 받은 뒤 (상태 코드와 상관없이) 콘텐츠를 선택한다. 사후 협상 메커니즘은 대체 표현에 대한 참조 목록처럼 단순할 수 있다.
사용자 에이전트는 첫 응답 콘텐츠가 만족스럽지 않으면, 다른 표현을 얻기 위해 대체 리소스 중 하나 이상에 GET 요청을 할 수 있다. 그런 대체 리소스의 선택은 (사용자 에이전트가) 자동으로 하거나, (예: 사용자가 하이퍼텍스트 메뉴에서 골라) 수동으로 할 수 있다.
서버는 대체 목록 외에는 첫 표현을 보내지 않음으로써, 사용자 에이전트의 사후 협상을 선호한다는 것을 나타낼 수도 있다. 예를 들어 300 (Multiple Choices)와 406 (Not Acceptable) 상태 코드를 가진 응답에 나열된 대체 목록에는, 사용자나 사용자 에이전트가 선택으로 반응할 수 있도록 사용할 수 있는 표현에 관한 정보가 들어 있다.
사후 협상은 응답이 흔히 쓰이는 차원(유형, 언어, 인코딩 등)에 따라 달라질 때, 원본 서버가 요청을 살펴서는 사용자 에이전트의 능력을 판단할 수 없을 때, 그리고 일반적으로 서버 부하를 분산하고 네트워크 사용량을 줄이기 위해 공용 캐시를 쓸 때 유리하다.
사후 협상의 단점은 대체 목록을 사용자 에이전트에 전송해야 한다는 것(헤더 섹션으로 전송하면 사용자가 느끼는 지연 시간이 나빠진다)과, 대체 표현을 얻기 위해 두 번째 요청이 필요하다는 것이다. 게다가 이 명세는 자동 선택을 지원하는 메커니즘을 정의하지 않는다. 다만 그런 메커니즘이 개발되는 것을 막지는 않는다.
서버의 응답에 콘텐츠 협상 선호를 담아 보낼 때, 나열된 선호는 그 리소스에 대한 이후 요청에서 적절한 콘텐츠를 선택하는 데 영향을 주려는 것이므로 “요청 콘텐츠 협상(request content negotiation)”이라고 한다. 예를 들어 Accept(12.5.1절)와 Accept-Encoding(12.5.3절) 헤더 필드를 응답에 보내, 그 리소스에 대한 이후 요청에서 선호하는 미디어 유형과 콘텐츠 코딩을 나타낼 수 있다.
마찬가지로 [RFC5789]의 3.1절은 PATCH 요청에서 어떤 콘텐츠 유형을 받아들이는지 알아낼 수 있게 하는 “Accept-Patch” 응답 헤더 필드를 정의한다.
각 콘텐츠 협상 필드에 대해, 그 필드가 없는 요청은 송신자가 그 협상 차원에 대해 선호가 없다는 뜻이다.
요청에 콘텐츠 협상 헤더 필드가 있는데 응답에 사용할 수 있는 표현 가운데 그 필드에 따라 받아들일 수 있다고 볼 만한 것이 없으면, 원본 서버는 406 (Not Acceptable) 응답을 보내 그 헤더 필드를 존중하거나, 그 요청 헤더 필드에 대해서는 응답이 콘텐츠 협상의 대상이 아닌 것처럼 다뤄 헤더 필드를 무시할 수 있다. 그러나 이것이 클라이언트가 그 표현을 쓸 수 있다는 뜻은 아니다.
참고: 이런 헤더 필드를 보내는 사용자 에이전트는, 서버가 사용자 에이전트의 요청 특성으로 개인을 식별하기 더 쉽게 만든다(17.13절).
이 명세가 정의하는 콘텐츠 협상 필드는 연관된 종류의 콘텐츠에 대한 선호에 상대적인 “가중치(weight)”를 부여하기 위해 “q”(대소문자 구분 없음)라는 공통 매개 변수를 사용한다. 같은 매개 변수 이름이 서버 설정에서 리소스에 대해 선택할 수 있는 여러 표현의 상대적 품질에 가중치를 부여하는 데 흔히 쓰이기 때문에, 이 가중치를 “품질 값(quality value)”(또는 “qvalue”)이라고 한다.
가중치는 0부터 1까지의 실수로 정규화되며, 0.001이 가장 덜 선호되고 1이 가장 선호된다. 값 0은 “받아들일 수 없음”을 뜻한다. “q” 매개 변수가 없으면 기본 가중치는 1이다.
weight = OWS ";" OWS "q=" qvalue
qvalue = ( "0" [ "." 0*3DIGIT ] )
/ ( "1" [ "." 0*3("0") ] )
qvalue를 보내는 송신자는 소수점 뒤에 세 자리를 넘는 숫자를 생성해서는 안 된다(MUST NOT). 이 값의 사용자 설정도 같은 방식으로 제한해야 마땅하다.
이 헤더 필드 대부분은, 표시된 경우, 지정되지 않은 값을 선택하기 위한 와일드카드 값(“*“)을 정의한다. 와일드카드가 없으면 필드에 명시적으로 언급되지 않은 값은 받아들일 수 없는 것으로 본다. Vary 안에서 와일드카드 값은 달라짐에 제한이 없다는 뜻이다.
참고: 실제로 콘텐츠 협상에서 와일드카드를 쓰는 것은 실용적 가치가 제한적이다. 예를 들어 “image/*를 (다른 특정 값)보다 더 또는 덜 선호한다”고 말하는 것이 쓸모 있는 경우는 드물기 때문이다. 클라이언트는
Accept: */*;q=0을 보내 더 선호하는 형식을 사용할 수 없으면406 (Not Acceptable)응답을 명시적으로 요청할 수 있다. 그러나 서버는 그 선호를 무시해도 되므로 클라이언트는 여전히 다른 응답을 처리할 수 있어야 한다.
“Accept” 헤더 필드는 사용자 에이전트가 응답 미디어 유형에 관한 선호를 지정하는 데 쓸 수 있다. 예를 들어 Accept 헤더 필드는 인라인 이미지를 요청하는 경우처럼, 요청이 특별히 원하는 소수의 유형으로 제한되어 있음을 나타내는 데 쓸 수 있다.
서버가 응답에 보내면, Accept는 같은 리소스에 대한 이후 요청의 콘텐츠에서 어떤 콘텐츠 유형을 선호하는지에 관한 정보를 제공한다.
Accept = #( media-range [ weight ] )
media-range = ( "*/*"
/ ( type "/" "*" )
/ ( type "/" subtype )
) parameters
별표 “” 문자는 미디어 유형을 범위로 묶는 데 쓰이며, “/“는 모든 미디어 유형을, “type/“는 그 유형의 모든 하위 유형을 나타낸다. media-range에는 그 범위에 적용할 수 있는 미디어 유형 매개 변수를 넣을 수 있다.
각 media-range 뒤에는 적용할 수 있는 선택적 미디어 유형 매개 변수(예: charset)가 오고, 그 뒤에 상대적 가중치를 나타내는 선택적 “q” 매개 변수(12.4.2절)가 올 수 있다.
이전 명세들은 가중치 매개 변수 뒤에 추가 확장 매개 변수가 오는 것을 허용했다. accept 확장 문법(accept-params, accept-ext)은 정의가 복잡했고, 실제로 쓰이지 않았으며, 새 헤더 필드로 더 쉽게 배포할 수 있어서 삭제되었다. 가중치를 쓰는 송신자는 “q”를 마지막에(모든 media-range 매개 변수 뒤에) 보내야 한다(SHOULD). 수신자는 매개 변수 순서와 상관없이 “q”라는 이름의 매개 변수를 가중치로 처리해야 한다(SHOULD).
참고: 콘텐츠 협상을 제어하기 위해 “q” 매개 변수 이름을 쓰면 같은 이름을 가진 미디어 유형 매개 변수와 충돌한다. 따라서 미디어 유형 레지스트리는 “q”라는 이름의 매개 변수를 허용하지 않는다.
다음 예는
Accept: audio/*; q=0.2, audio/basic
“audio/basic을 선호하지만, 품질을 80% 낮춘 뒤에도 그것이 가장 좋은 것이라면 어떤 audio 유형이든 보내 달라”로 해석된다.
더 정교한 예는 다음과 같다.
Accept: text/plain; q=0.5, text/html,
text/x-dvi; q=0.8, text/x-c
말로 풀면 “text/html과 text/x-c를 똑같이 선호하는 미디어 유형이지만, 그것들이 없으면 text/x-dvi 표현을 보내고, 그것도 없으면 text/plain 표현을 보내 달라”로 해석된다.
더 구체적인 미디어 범위나 구체적인 미디어 유형이 미디어 범위보다 우선할 수 있다. 둘 이상의 미디어 범위가 주어진 유형에 적용되면, 가장 구체적인 참조가 우선한다. 예를 들어
Accept: text/*, text/plain, text/plain;format=flowed, */*
는 다음 우선순위를 갖는다.
주어진 유형에 연관된 미디어 유형 품질 계수는 그 유형과 일치하는 미디어 범위 가운데 우선순위가 가장 높은 것을 찾아 정한다. 예를 들어
Accept: text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed,
text/plain;format=fixed;q=0.4, */*;q=0.5
는 다음 값을 연관시킨다.
| 미디어 유형 | 품질 값 |
|---|---|
| text/plain;format=flowed | 1 |
| text/plain | 0.7 |
| text/html | 0.3 |
| image/jpeg | 0.5 |
| text/plain;format=fixed | 0.4 |
| text/html;level=3 |
표 5
역주: 정오표 7138(Verified)에 따라 마지막 행의 0.7을 고친 값이다.
참고: 사용자 에이전트에 특정 미디어 범위에 대한 기본 품질 값 집합이 주어질 수 있다. 그러나 사용자 에이전트가 다른 렌더링 에이전트와 상호 작용할 수 없는 폐쇄 시스템이 아니라면, 이 기본 집합은 사용자가 설정할 수 있어야 마땅하다.
“Accept-Charset” 헤더 필드는 사용자 에이전트가 텍스트 응답 콘텐츠의 charset에 대한 선호를 나타내기 위해 보낼 수 있다. 예를 들어 이 필드를 쓰면 더 포괄적이거나 특수 목적의 charset을 이해할 수 있는 사용자 에이전트가, 그 charset으로 정보를 표현할 수 있는 원본 서버에 그 능력을 알릴 수 있다.
Accept-Charset = #( ( token / "*" ) [ weight ] )
charset 이름은 8.3.2절에 정의되어 있다. 사용자 에이전트는 12.4.2절에 정의된 대로, 그 charset에 대한 사용자의 상대적 선호를 나타내기 위해 각 charset에 품질 값을 연관시킬 수 있다(MAY). 예는 다음과 같다.
Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
특수 값 “*“가 Accept-Charset 헤더 필드에 있으면, 필드의 다른 곳에서 언급되지 않은 모든 charset과 일치한다.
참고: UTF-8이 거의 어디서나 쓰이게 되었고, 사용자가 선호하는 charset의 자세한 목록을 보내면 대역폭을 낭비하고 지연 시간을 늘리며 수동적 핑거프린팅을 너무 쉽게 만들기 때문에(17.13절),
Accept-Charset은 사용이 중단되었다. 대부분의 범용 사용자 에이전트는 특별히 설정되지 않는 한Accept-Charset을 보내지 않는다.
“Accept-Encoding” 헤더 필드는 콘텐츠 코딩(8.4.1절)의 사용에 관한 선호를 나타내는 데 쓸 수 있다.
사용자 에이전트가 요청에 보내면, Accept-Encoding은 응답에서 받아들일 수 있는 콘텐츠 코딩을 나타낸다.
서버가 응답에 보내면, Accept-Encoding은 같은 리소스에 대한 이후 요청의 콘텐츠에서 어떤 콘텐츠 코딩을 선호하는지에 관한 정보를 제공한다.
인코딩을 선호하지 않음을 전달하기 위해 “identity” 토큰을 “인코딩 없음”의 동의어로 쓴다.
Accept-Encoding = #( codings [ weight ] )
codings = content-coding / "identity" / "*"
각 codings 값에는 12.4.2절에 정의된 대로 그 인코딩에 대한 선호를 나타내는 품질 값(가중치)을 연관시킬 수 있다(MAY). Accept-Encoding 필드의 별표 “*” 기호는 필드에 명시적으로 나열되지 않은, 사용할 수 있는 모든 콘텐츠 코딩과 일치한다.
예:
Accept-Encoding: compress, gzip
Accept-Encoding:
Accept-Encoding: *
Accept-Encoding: compress;q=0.5, gzip;q=1.0
Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
서버는 다음 규칙으로 주어진 표현의 콘텐츠 코딩을 받아들일 수 있는지 검사한다.
Accept-Encoding 헤더 필드가 없으면, 사용자 에이전트가 어떤 콘텐츠 코딩이든 받아들일 수 있는 것으로 본다.Accept-Encoding 헤더 필드가 “identity”에 대한 더 구체적인 항목 없이 “identity;q=0”이나 “*;q=0”을 명시해 특별히 제외하지 않는 한 기본적으로 받아들일 수 있다.Accept-Encoding 필드 값에 나열된 콘텐츠 코딩 중 하나이면, qvalue가 0이 아닌 한 받아들일 수 있다. (12.4.2절에 정의된 대로 qvalue 0은 “받아들일 수 없음”을 뜻한다.)표현은 여러 콘텐츠 코딩으로 인코딩될 수 있다. 그러나 대부분의 콘텐츠 코딩은 같은 목적(예: 데이터 압축)을 이루는 대체 방법이다. 같은 목적을 가진 여러 콘텐츠 코딩 가운데 고를 때는, 받아들일 수 있는 콘텐츠 코딩 중 0이 아닌 qvalue가 가장 높은 것을 선호한다.
필드 값이 빈 Accept-Encoding 헤더 필드는 사용자 에이전트가 응답에 어떤 콘텐츠 코딩도 원하지 않는다는 뜻이다. 요청에 비어 있지 않은 Accept-Encoding 헤더 필드가 있는데 응답에 사용할 수 있는 표현 가운데 받아들일 수 있다고 나열된 콘텐츠 코딩을 가진 것이 없으면, 원본 서버는 identity 코딩이 받아들일 수 없다고 표시되지 않은 한 콘텐츠 코딩이 없는 응답을 보내야 한다(SHOULD).
응답에 Accept-Encoding 헤더 필드가 있으면, 그 필드는 리소스가 연관된 요청에서 어떤 콘텐츠 코딩을 받아들일 의향이 있었는지 나타낸다. 필드 값은 요청에서와 같은 방식으로 평가한다.
이 정보는 연관된 요청에 특정한 것이라는 점에 유의한다. 지원하는 인코딩 집합은 같은 서버의 다른 리소스에서는 다를 수 있고, 시간이 지나면서 바뀌거나 요청의 다른 측면(예: 요청 메서드)에 따라 달라질 수 있다.
지원하지 않는 콘텐츠 코딩 때문에 요청을 실패시키는 서버는, 클라이언트가 콘텐츠 코딩 관련 문제와 미디어 유형 관련 문제를 구별할 수 있도록 415 (Unsupported Media Type) 상태로 응답하고 그 응답에 Accept-Encoding 헤더 필드를 넣어야 마땅하다. 미디어 유형 관련 문제와 혼동되지 않도록, 콘텐츠 코딩과 무관한 이유로 415 상태로 요청을 실패시키는 서버는 Accept-Encoding 헤더 필드를 넣어서는 안 된다(MUST NOT).
Accept-Encoding의 가장 흔한 용도는 클라이언트가 낙관적으로 콘텐츠 코딩을 쓴 것에 대한 응답인 415 (Unsupported Media Type) 상태 코드의 응답이다. 그러나 이 헤더 필드는 이후 상호 작용을 최적화하기 위해 콘텐츠 코딩을 지원한다는 것을 클라이언트에 알리는 데도 쓸 수 있다. 예를 들어 리소스는 요청 콘텐츠가 압축 코딩을 쓸 만큼 컸는데도 클라이언트가 그렇게 하지 않았다면 2xx (Successful) 응답에 이 필드를 넣을 수 있다.
“Accept-Language” 헤더 필드는 사용자 에이전트가 응답에서 선호하는 자연어 집합을 나타내는 데 쓸 수 있다. 언어 태그는 8.5.1절에 정의되어 있다.
Accept-Language = #( language-range [ weight ] )
language-range =
<language-range, see [RFC4647], Section 2.1>
각 language-range에는 12.4.2절에 정의된 대로, 그 범위가 지정하는 언어에 대한 사용자 선호의 추정치를 나타내는 품질 값을 연관시킬 수 있다. 예를 들어
Accept-Language: da, en-gb;q=0.8, en;q=0.7
는 “덴마크어를 선호하지만, 영국 영어와 그 밖의 영어도 받아들인다”는 뜻이다.
일부 수신자는 언어 태그가 나열된 순서를, 특히 같은 품질 값이 부여된 태그(값이 없으면 q=1과 같다)에 대해, 우선순위가 내려가는 순서를 나타내는 것으로 다룬다는 점에 유의한다. 그러나 이 동작에 의존할 수는 없다. 일관성을 위해, 그리고 상호 운용성을 최대한 높이기 위해, 많은 사용자 에이전트는 각 언어 태그에 고유한 품질 값을 부여하면서 품질이 내려가는 순서로 나열한다. 언어 우선순위 목록에 관한 추가 논의는 [RFC4647]의 2.3절에서 볼 수 있다.
일치 판정을 위해 [RFC4647]의 3장은 여러 일치 스킴을 정의한다. 구현은 자신의 요구 사항에 가장 적합한 일치 스킴을 제공할 수 있다. “Basic Filtering” 스킴([RFC4647]의 3.3.1절)은 이전에 [RFC2616]의 14.4절에서 HTTP에 대해 정의된 일치 스킴과 같다.
모든 요청에 사용자의 완전한 언어 선호를 담은 Accept-Language 헤더 필드를 보내는 것은 사용자의 개인 정보 보호 기대에 어긋날 수 있다(17.13절).
이해도는 개별 사용자에 크게 좌우되므로, 사용자 에이전트는 (사용자 에이전트 자체의 설정이나, 사용자가 제어할 수 있는 시스템 설정을 기본값으로 쓰는 방식으로) 사용자가 언어 선호를 제어할 수 있게 해야 한다. 사용자에게 그런 제어를 제공하지 않는 사용자 에이전트는 Accept-Language 헤더 필드를 보내서는 안 된다(MUST NOT).
참고: 사용자는 위에서 설명한 언어 일치의 세부 사항을 잘 모르는 경우가 많으므로, 사용자 에이전트는 사용자가 선호를 설정할 때 안내를 제공해야 마땅하다. 예를 들어 사용자는 “en-gb”를 고르면 영국 영어를 사용할 수 없을 때 어떤 종류의 영어 문서든 받을 것이라고 생각할 수 있다. 이런 경우 사용자 에이전트는 더 나은 일치 동작을 위해 목록에 “en”을 추가하라고 제안할 수 있다.
응답의 “Vary” 헤더 필드는 메서드와 대상 URI 외에 요청 메시지의 어떤 부분이 이 응답의 콘텐츠를 선택하는 원본 서버의 처리에 영향을 주었을 수 있는지 설명한다.
Vary = #( "*" / field-name )
Vary 필드 값은 와일드카드 구성원 “*“이거나, 이 응답의 표현을 선택하는 데 역할을 했을 수 있는, 선택 헤더 필드(selecting header field)라고 부르는 요청 필드 이름의 목록이다. 선택 헤더 필드가 될 수 있는 필드는 이 명세가 정의한 필드에 한정되지 않는다.
구성원 ““를 담은 목록은 요청의 다른 측면, 아마도 메시지 구문 밖의 측면(예: 클라이언트의 네트워크 주소)까지 응답 표현을 선택하는 데 역할을 했을 수 있음을 알린다. 수신자는 요청을 원본 서버로 전달하지 않고는 이 응답이 이후 요청에 적절한지 판단할 수 없다. 프록시는 Vary 필드 값에 ““를 생성해서는 안 된다(MUST NOT).
예를 들어 다음을 담은 응답은
Vary: accept-encoding, accept-language
원본 서버가 이 응답의 콘텐츠를 고르면서 요청의 Accept-Encoding과 Accept-Language 헤더 필드(또는 그 부재)를 결정 요인으로 썼을 수 있음을 나타낸다.
필드 이름 목록을 담은 Vary 필드에는 두 가지 목적이 있다.
Vary는 새 요청을 저장된 캐시 항목과 일치시키는 데 필요한 캐시 키를 확장한다.원본 서버는 캐시 가능한 응답이 이후 요청에 선택적으로 재사용되기를 원할 때, 그 응답에 Vary 헤더 필드를 생성해야 한다(SHOULD). 일반적으로 원본 서버가 요청의 Accept-Language 헤더 필드에 따라 응답의 언어를 고른 경우처럼, 응답 콘텐츠가 그 선택 헤더 필드가 나타낸 선호에 더 잘 맞도록 맞춰진 경우가 그렇다.
원본 서버가 콘텐츠 선택의 달라짐을 Vary가 캐싱 성능에 미치는 영향보다 덜 중요하다고 여기면, 특히 재사용이 이미 캐시 응답 지시자([CACHING]의 5.2절)로 제한되어 있을 때는 Vary를 생략할 수 있다.
다른 사용자를 위해 그 응답을 재사용하는 것은 필드 정의(11.6.2절)로 금지되어 있으므로, Vary에 Authorization 필드 이름을 보낼 필요는 없다. 마찬가지로 응답 콘텐츠가 네트워크 지역에 따라 선택되었거나 영향을 받았더라도, 원본 서버가 수신자가 한 지역에서 다른 지역으로 이동해도 캐시된 응답이 재사용되기를 원한다면, 원본 서버가 그 달라짐을 Vary에 나타낼 필요는 없다.
역주: 12장의 “ought to”(12.4.2·12.5.1·12.5.3·12.5.4절)와 “need to”(12.4.3·12.5.4절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 12.5.1~12.5.5절 제목은 헤더 필드 이름이라 원어로 두었다. 미디어 유형·미디어 범위 문자열(text/plain 등)과 “Basic Filtering” 스킴 이름은 원어로 두었다. 12.5.1절 우선순위 목록의
*/*는 Markdown 강조로 해석되지 않도록 별표 앞에 백슬래시를 넣었다.
조건부 요청은 대상 리소스에 요청 메서드를 적용하기 전에 검사할 전제 조건을 나타내는 요청 헤더 필드를 하나 이상 가진 HTTP 요청이다. 13.2절은 전제 조건을 언제 평가하는지, 그리고 전제 조건이 둘 이상 있을 때의 우선순위를 정의한다.
조건부 GET 요청은 HTTP 캐시 갱신을 위한 가장 효율적인 메커니즘이다([CACHING]). 조건은 PUT과 DELETE 같은 상태를 바꾸는 메서드에도 적용하여, 한 클라이언트가 병렬로 작업해 온 다른 클라이언트의 작업을 실수로 덮어쓰는 “갱신 손실(lost update)” 문제를 막을 수 있다.
전제 조건은 보통 대상 리소스 전체의 상태(현재 값 집합)나, 이전에 얻은 표현에서 관찰된 상태(그 집합의 한 값)에 대해 정의된다. 리소스가 각각 관찰 가능한 상태를 가진 여러 현재 표현을 가지고 있다면, 전제 조건은 각 요청이 선택된 표현(3.2절)에 대응되는 방식이 시간이 지나도 일관된다고 가정한다. 어쨌든 그 대응이 일관되지 않거나 서버가 적절한 표현을 선택할 수 없더라도, 전제 조건이 거짓으로 평가되면 해가 생기지 않는다.
아래에 정의된 각 전제 조건은 대상 리소스의 이전 표현들에서 얻은 검증자 집합과, 선택된 표현에 대한 검증자의 현재 상태(8.8절)를 비교하는 것으로 이루어진다. 따라서 이 전제 조건들은 대상 리소스의 상태가 클라이언트가 알고 있는 주어진 상태 이후로 바뀌었는지를 평가한다. 그런 평가의 효과는 13.2절에 정의된 대로, 메서드 의미와 조건 선택에 따라 달라진다.
다른 명세가 확장 필드로 정의하는 다른 전제 조건은 모든 수신자, 대상 리소스의 상태 전반, 또는 리소스 그룹에 조건을 걸 수 있다. 예를 들어 WebDAV의 “If” 헤더 필드는, 수신자가 그 필드를 이해하고 구현한다면, 잠금 같은 여러 리소스의 다양한 측면에 요청을 조건부로 만들 수 있다([WEBDAV]의 10.4절).
전제 조건의 확장은 (If-Modified-Since처럼) 알지 못하는 전제 조건을 안전하게 무시할 수 있을 때, 주어진 사용 사례에서 배포를 가정할 수 있을 때, 또는 대상 리소스의 다른 속성으로 구현 여부가 알려질 때에만 가능하다. 이는 서로 합의된 공통 표준의 배포에 집중하도록 장려한다.
“If-Match” 헤더 필드는 필드 값이 “*“이면 수신한 원본 서버가 대상 리소스의 현재 표현을 적어도 하나 가지고 있는지에, 그렇지 않으면 필드 값에 주어진 엔터티 태그 목록의 구성원과 일치하는 엔터티 태그를 가진 대상 리소스의 현재 표현을 가지고 있는지에 요청 메서드를 조건부로 만든다.
원본 서버는 If-Match의 엔터티 태그를 비교할 때 강한 비교 함수(8.8.3.2절)를 반드시 사용해야 한다(MUST). 클라이언트는 표현 데이터에 어떤 변경이라도 있었다면 메서드가 적용되지 않도록 하려고 이 전제 조건을 쓰기 때문이다.
If-Match = "*" / #entity-tag
예:
If-Match: "xyzzy"
If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-Match: *
If-Match는 여러 사용자 에이전트가 같은 리소스에 병렬로 작업할 수 있을 때 실수로 덮어쓰는 것을 막기 위해(즉, “갱신 손실” 문제를 막기 위해) 상태를 바꾸는 메서드(예: POST, PUT, DELETE)와 함께 가장 자주 쓰인다. 일반적으로는 표현의 선택이나 수정이 따르는 어떤 메서드에도 쓸 수 있으며, 선택된 표현의 현재 엔터티 태그가 If-Match 필드 값의 구성원이 아니면 요청을 중단하게 한다.
원본 서버가 표현을 선택하는 요청을 받았는데 그 요청에 If-Match 헤더 필드가 있으면, 원본 서버는 메서드를 수행하기 전에 13.2절에 따라 If-Match 조건을 반드시 평가해야 한다(MUST).
받은 If-Match 헤더 필드는 다음과 같이 평가한다.
If-Match 조건을 평가하는 원본 서버는 조건이 거짓으로 평가되면 요청된 메서드를 수행해서는 안 된다(MUST NOT). 대신 원본 서버는 412 (Precondition Failed) 상태 코드로 응답하여 조건부 요청이 실패했음을 나타낼 수 있다(MAY). 또는 요청이 이미 선택된 표현에 적용된 것으로 보이는 상태 변경 작업이라면, 원본 서버는 2xx (Successful) 상태 코드로 응답할 수 있다(MAY). (즉, 사용자 에이전트가 요청한 변경은 이미 성공했지만, 아마 이전 응답이 유실되었거나 다른 사용자 에이전트가 같은 변경을 했기 때문에, 사용자 에이전트가 그 사실을 모를 수 있다.)
변경 요청이 이미 적용된 것으로 보일 때 원본 서버가 성공 응답을 보내도록 허용하면 여러 저작 사용 사례에서 더 효율적이지만, 여러 사용자 에이전트가 매우 비슷하지만 서로 협력하지 않는 변경 요청을 보내고 있다면 어느 정도 위험이 따른다. 예를 들어 여러 사용자 에이전트가 공통 리소스를 세마포어로 삼아 쓰기를 하면(예: 원자적이지 않은 증가) 충돌할 가능성이 크고, 그러면 중요한 상태 전이를 잃을 수도 있다. 그런 종류의 리소스에서는 원본 서버가 안전하지 않은 메서드의 전제 조건이 실패할 때마다 엄격하게 412를 보내는 편이 낫다. 다른 경우에는 성공 응답에서 ETag 필드를 빼면, 리소스의 현재 상태에 대한 혼란을 없애기 위해 사용자 에이전트가 다음 요청으로 GET을 수행하도록 유도할 수 있다.
클라이언트는 선택된 표현이 일치하지 않으면 412 (Precondition Failed) 응답을 선호한다는 것을 나타내기 위해 GET 요청에 If-Match 헤더 필드를 보낼 수 있다(MAY). 그러나 이것은 새 표현을 원하지 않을 때 이전에 받은 부분 표현을 완성하기 위한 범위 요청(14장)에서만 쓸모가 있다. 클라이언트가 새 표현을 받기를 선호할 때는 If-Range(13.1.5절)가 범위 요청에 더 적합하다.
캐시나 중개자는 If-Match를 무시할 수 있다(MAY). 그 상호 운용 기능은 원본 서버에만 필요하기 때문이다.
““와 다른 값(““의 다른 인스턴스 포함)을 함께 담은 목록 값의 If-Match 헤더 필드는 구문상 유효하지 않으며(따라서 생성할 수 없으며) 상호 운용될 가능성도 낮다는 점에 유의한다.
“If-None-Match” 헤더 필드는 필드 값이 “*“이면 수신한 캐시나 원본 서버가 대상 리소스의 현재 표현을 하나도 가지고 있지 않은지에, 그렇지 않으면 필드 값에 나열된 어떤 엔터티 태그와도 일치하지 않는 엔터티 태그를 가진 선택된 표현을 가지고 있는지에 요청 메서드를 조건부로 만든다.
수신자는 If-None-Match의 엔터티 태그를 비교할 때 약한 비교 함수(8.8.3.2절)를 반드시 사용해야 한다(MUST). 약한 엔터티 태그는 표현 데이터에 변경이 있었더라도 캐시 검증에 쓸 수 있기 때문이다.
If-None-Match = "*" / #entity-tag
예:
If-None-Match: "xyzzy"
If-None-Match: W/"xyzzy"
If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
If-None-Match: *
If-None-Match는 주로 조건부 GET 요청에서 최소한의 트랜잭션 오버헤드로 캐시된 정보를 효율적으로 갱신하기 위해 쓰인다. 클라이언트가 엔터티 태그를 가진 저장된 응답 하나 이상을 갱신하고 싶을 때, 클라이언트는 GET 요청을 하면서 그 엔터티 태그들의 목록을 담은 If-None-Match 헤더 필드를 생성해야 한다(SHOULD). 그러면 수신 서버는 저장된 응답 중 하나가 선택된 표현과 일치할 때 그것을 나타내는 304 (Not Modified) 응답을 보낼 수 있다.
If-None-Match는 값 “*“와 함께 써서, 클라이언트가 리소스에 현재 표현이 없다고 믿을 때 안전하지 않은 요청 메서드(예: PUT)가 대상 리소스의 기존 표현을 뜻하지 않게 수정하는 것을 막을 수도 있다(9.2.1절). 이것은 둘 이상의 클라이언트가 대상 리소스의 초기 표현을 만들려고 할 때 생길 수 있는 “갱신 손실” 문제의 변형이다.
원본 서버가 표현을 선택하는 요청을 받았는데 그 요청에 If-None-Match 헤더 필드가 있으면, 원본 서버는 메서드를 수행하기 전에 13.2절에 따라 If-None-Match 조건을 반드시 평가해야 한다(MUST).
받은 If-None-Match 헤더 필드는 다음과 같이 평가한다.
If-None-Match 조건을 평가하는 원본 서버는 조건이 거짓으로 평가되면 요청된 메서드를 수행해서는 안 된다(MUST NOT). 대신 원본 서버는 a) 요청 메서드가 GET이나 HEAD이면 304 (Not Modified) 상태 코드로, b) 그 밖의 모든 요청 메서드에는 412 (Precondition Failed) 상태 코드로 반드시 응답해야 한다(MUST).
받은 If-None-Match 헤더 필드의 캐시 처리 요구 사항은 [CACHING]의 4.3.2절에 정의되어 있다.
““와 다른 값(““의 다른 인스턴스 포함)을 함께 담은 목록 값의 If-None-Match 헤더 필드는 구문상 유효하지 않으며(따라서 생성할 수 없으며) 상호 운용될 가능성도 낮다는 점에 유의한다.
“If-Modified-Since” 헤더 필드는 GET이나 HEAD 요청 메서드를, 선택된 표현의 수정 일자가 필드 값에 주어진 일자보다 최근인지에 조건부로 만든다. 선택된 표현의 데이터가 바뀌지 않았다면 그 데이터의 전송을 피한다.
If-Modified-Since = HTTP-date
필드의 예는 다음과 같다.
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
요청에 If-None-Match 헤더 필드가 있으면 수신자는 If-Modified-Since를 반드시 무시해야 한다(MUST). If-None-Match의 조건은 If-Modified-Since의 조건을 더 정확하게 대체하는 것으로 여겨지며, 둘을 함께 쓰는 것은 If-None-Match를 구현하지 않았을 수 있는 오래된 중개자와 상호 운용하기 위해서일 뿐이다.
받은 필드 값이 유효한 HTTP-date가 아니거나, 필드 값의 구성원이 둘 이상이거나, 요청 메서드가 GET도 HEAD도 아니면, 수신자는 If-Modified-Since 헤더 필드를 반드시 무시해야 한다(MUST).
리소스에 사용할 수 있는 수정 일자가 없으면 수신자는 If-Modified-Since 헤더 필드를 반드시 무시해야 한다(MUST).
수신자는 If-Modified-Since 필드 값의 타임스탬프를 원본 서버의 시계 기준으로 반드시 해석해야 한다(MUST).
If-Modified-Since는 보통 두 가지 서로 다른 목적으로 쓰인다. 1) 엔터티 태그가 없는 캐시된 표현을 효율적으로 갱신하기 위해, 2) 웹 순회의 범위를 최근에 바뀐 리소스로 제한하기 위해서다.
캐시 갱신에 쓸 때, 캐시는 보통 캐시된 메시지의 Last-Modified 헤더 필드 값을 써서 If-Modified-Since의 필드 값을 생성한다. 이 동작은 시계가 제대로 동기화되지 않았거나, 서버가 정확히 일치하는 타임스탬프만 존중하기로 한 경우(원본 서버의 시계가 바로잡히거나 표현이 보관된 백업에서 복원될 때 Last-Modified 일자가 “과거로 돌아가는” 것처럼 보이는 문제 때문에)에 가장 잘 상호 운용된다. 그러나 캐시는 특히 캐시된 메시지에 Last-Modified 헤더 필드가 없을 때, 캐시된 메시지의 Date 헤더 필드나 메시지를 받은 시각 같은 다른 데이터를 바탕으로 필드 값을 생성하기도 한다.
검색 범위를 최근 시간 구간으로 제한하는 데 쓸 때, 사용자 에이전트는 자신의 시계나 이전 응답에서 서버로부터 받은 Date 헤더 필드를 바탕으로 If-Modified-Since 필드 값을 생성한다. 선택된 표현의 Last-Modified 헤더 필드를 바탕으로 정확한 타임스탬프 일치를 택하는 원본 서버는, 사용자 에이전트가 데이터 전송을 지정된 구간 동안 바뀐 것으로만 제한하도록 도울 수 없다.
원본 서버가 표현을 선택하는 요청을 받았는데 그 요청에 If-None-Match 헤더 필드 없이 If-Modified-Since 헤더 필드가 있으면, 원본 서버는 메서드를 수행하기 전에 13.2절에 따라 If-Modified-Since 조건을 평가해야 한다(SHOULD).
받은 If-Modified-Since 헤더 필드는 다음과 같이 평가한다.
If-Modified-Since 조건을 평가하는 원본 서버는 조건이 거짓으로 평가되면 요청된 메서드를 수행하지 않아야 한다(SHOULD NOT). 대신 원본 서버는 이전에 캐시된 응답을 식별하거나 갱신하는 데 쓸모 있는 메타데이터만 담은 304 (Not Modified) 응답을 생성해야 한다(SHOULD).
받은 If-Modified-Since 헤더 필드의 캐시 처리 요구 사항은 [CACHING]의 4.3.2절에 정의되어 있다.
“If-Unmodified-Since” 헤더 필드는 요청 메서드를, 선택된 표현의 마지막 수정 일자가 필드 값에 주어진 일자보다 이르거나 같은지에 조건부로 만든다. 이 필드는 사용자 에이전트가 표현의 엔터티 태그를 가지고 있지 않은 경우에 If-Match와 같은 목적을 이룬다.
If-Unmodified-Since = HTTP-date
필드의 예는 다음과 같다.
If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
요청에 If-Match 헤더 필드가 있으면 수신자는 If-Unmodified-Since를 반드시 무시해야 한다(MUST). If-Match의 조건은 If-Unmodified-Since의 조건을 더 정확하게 대체하는 것으로 여겨지며, 둘을 함께 쓰는 것은 If-Match를 구현하지 않았을 수 있는 오래된 중개자와 상호 운용하기 위해서일 뿐이다.
받은 필드 값이 유효한 HTTP-date가 아니면(필드 값이 일자 목록으로 보이는 경우 포함) 수신자는 If-Unmodified-Since 헤더 필드를 반드시 무시해야 한다(MUST).
리소스에 사용할 수 있는 수정 일자가 없으면 수신자는 If-Unmodified-Since 헤더 필드를 반드시 무시해야 한다(MUST).
수신자는 If-Unmodified-Since 필드 값의 타임스탬프를 원본 서버의 시계 기준으로 반드시 해석해야 한다(MUST).
If-Unmodified-Since는 여러 사용자 에이전트가 표현과 함께 엔터티 태그를 제공하지 않는 리소스에 병렬로 작업할 수 있을 때 실수로 덮어쓰는 것을 막기 위해(즉, “갱신 손실” 문제를 막기 위해) 상태를 바꾸는 메서드(예: POST, PUT, DELETE)와 함께 가장 자주 쓰인다. 일반적으로는 표현의 선택이나 수정이 따르는 어떤 메서드에도 쓸 수 있으며, 선택된 표현의 마지막 수정 일자가 If-Unmodified-Since 필드 값에 주어진 일자 이후로 바뀌었으면 요청을 중단하게 한다.
원본 서버가 표현을 선택하는 요청을 받았는데 그 요청에 If-Match 헤더 필드 없이 If-Unmodified-Since 헤더 필드가 있으면, 원본 서버는 메서드를 수행하기 전에 13.2절에 따라 If-Unmodified-Since 조건을 반드시 평가해야 한다(MUST).
받은 If-Unmodified-Since 헤더 필드는 다음과 같이 평가한다.
If-Unmodified-Since 조건을 평가하는 원본 서버는 조건이 거짓으로 평가되면 요청된 메서드를 수행해서는 안 된다(MUST NOT). 대신 원본 서버는 412 (Precondition Failed) 상태 코드로 응답하여 조건부 요청이 실패했음을 나타낼 수 있다(MAY). 또는 요청이 이미 선택된 표현에 적용된 것으로 보이는 상태 변경 작업이라면, 원본 서버는 2xx (Successful) 상태 코드로 응답할 수 있다(MAY). (즉, 사용자 에이전트가 요청한 변경은 이미 성공했지만, 아마 이전 응답이 유실되었거나 다른 사용자 에이전트가 같은 변경을 했기 때문에, 사용자 에이전트가 그 사실을 모를 수 있다.)
변경 요청이 이미 적용된 것으로 보일 때 원본 서버가 성공 응답을 보내도록 허용하면 여러 저작 사용 사례에서 더 효율적이지만, 여러 사용자 에이전트가 매우 비슷하지만 서로 협력하지 않는 변경 요청을 보내고 있다면 어느 정도 위험이 따른다. 그런 경우에는 원본 서버가 안전하지 않은 메서드의 전제 조건이 실패할 때마다 엄격하게 412를 보내는 편이 낫다.
클라이언트는 선택된 표현이 수정되었으면 412 (Precondition Failed) 응답을 선호한다는 것을 나타내기 위해 GET 요청에 If-Unmodified-Since 헤더 필드를 보낼 수 있다(MAY). 그러나 이것은 새 표현을 원하지 않을 때 이전에 받은 부분 표현을 완성하기 위한 범위 요청(14장)에서만 쓸모가 있다. 클라이언트가 새 표현을 받기를 선호할 때는 If-Range(13.1.5절)가 범위 요청에 더 적합하다.
캐시나 중개자는 If-Unmodified-Since를 무시할 수 있다(MAY). 그 상호 운용 기능은 원본 서버에만 필요하기 때문이다.
“If-Range” 헤더 필드는 If-Match, If-Unmodified-Since 헤더 필드와 비슷한 특별한 조건부 요청 메커니즘을 제공하지만, 검증자가 일치하지 않으면 수신자에게 Range 헤더 필드를 무시하라고 지시하여, 412 (Precondition Failed) 응답 대신 새로 선택된 표현을 전송하게 한다.
클라이언트가 표현의 부분 사본을 가지고 있고 전체 표현의 최신 사본을 원한다면, 조건부 GET(If-Unmodified-Since와 If-Match 중 하나 또는 둘 다 사용)과 함께 Range 헤더 필드를 쓸 수도 있다. 그러나 표현이 수정되어 전제 조건이 실패하면, 클라이언트는 현재 전체 표현을 얻기 위해 두 번째 요청을 해야 한다.
“If-Range” 헤더 필드를 쓰면 클라이언트는 두 번째 요청을 “건너뛸(short-circuit)” 수 있다. 비공식적으로 그 의미는 다음과 같다. 표현이 바뀌지 않았다면 내가 Range로 요청하는 부분(들)을 보내고, 그렇지 않으면 전체 표현을 보내 달라.
If-Range = entity-tag / HTTP-date
유효한 entity-tag는 처음 세 문자에 DQUOTE가 있는지 살펴 유효한 HTTP-date와 구별할 수 있다.
클라이언트는 Range 헤더 필드가 없는 요청에 If-Range 헤더 필드를 생성해서는 안 된다(MUST NOT). 서버는 Range 헤더 필드가 없는 요청에서 받은 If-Range 헤더 필드를 반드시 무시해야 한다(MUST). 원본 서버는 Range 요청을 지원하지 않는 대상 리소스에 대한 요청에서 받은 If-Range 헤더 필드를 반드시 무시해야 한다(MUST).
클라이언트는 약하다고 표시된 엔터티 태그를 담은 If-Range 헤더 필드를 생성해서는 안 된다(MUST NOT). 클라이언트는 해당 표현의 엔터티 태그가 없고 그 일자가 8.8.2.2절에서 정의한 의미의 강한 검증자인 경우가 아니라면, HTTP-date를 담은 If-Range 헤더 필드를 생성해서는 안 된다(MUST NOT).
Range 요청에서 If-Range 헤더 필드를 받은 서버는 메서드를 수행하기 전에 13.2절에 따라 조건을 반드시 평가해야 한다(MUST).
HTTP-date를 담은 If-Range 헤더 필드는 다음과 같이 평가한다.
HTTP-date 검증자가 8.8.2.2절에서 정의한 의미의 강한 검증자가 아니면 조건은 거짓이다.HTTP-date 검증자가 선택된 표현의 Last-Modified 필드 값과 정확히 일치하면 조건은 참이다.entity-tag를 담은 If-Range 헤더 필드는 다음과 같이 평가한다.
entity-tag 검증자가 강한 비교 함수(8.8.3.2절)로 선택된 표현의 ETag 필드 값과 정확히 일치하면 조건은 참이다.If-Range 헤더 필드의 수신자는 If-Range 조건이 거짓으로 평가되면 Range 헤더 필드를 반드시 무시해야 한다(MUST). 그렇지 않으면 수신자는 요청대로 Range 헤더 필드를 처리해야 한다(SHOULD).
If-Range 비교는 검증자가 HTTP-date인 경우를 포함해 정확한 일치로 이루어지므로, If-Unmodified-Since 조건을 평가할 때 쓰는 “이르거나 같음” 비교와 다르다는 점에 유의한다.
아래에서 제외하는 경우를 빼면, 수신한 캐시나 원본 서버는 정상적인 요청 검사를 성공적으로 수행한 뒤, 그리고 요청 콘텐츠(있다면)를 처리하거나 요청 메서드에 연관된 동작을 수행하기 직전에 받은 요청 전제 조건을 반드시 평가해야 한다(MUST). 서버는 그 조건들이 없는 같은 요청에 대한 응답이, 요청 콘텐츠를 처리하기 전에, 2xx (Successful)나 412 (Precondition Failed)가 아닌 상태 코드였을 것이라면 받은 전제 조건을 모두 반드시 무시해야 한다(MUST). 다시 말해, 상당한 처리가 일어나기 전에 감지할 수 있는 리다이렉트와 실패는 전제 조건 평가보다 우선한다.
대상 리소스의 원본 서버가 아니면서 대상 리소스에 대한 요청의 캐시로도 동작할 수 없는 서버는 이 명세가 정의한 조건부 요청 헤더 필드를 평가해서는 안 되며(MUST NOT), 요청을 전달한다면 그 필드들도 반드시 전달해야 한다(MUST). 그 필드를 생성한 클라이언트는 현재 표현을 제공할 수 있는 서버가 그것을 평가하기를 의도하기 때문이다. 마찬가지로 서버는 CONNECT, OPTIONS, TRACE처럼 선택된 표현의 선택이나 수정이 따르지 않는 요청 메서드와 함께 받은, 이 명세가 정의한 조건부 요청 헤더 필드를 반드시 무시해야 한다(MUST).
프로토콜 확장은 전제 조건이 평가되는 조건이나 평가의 결과를 바꿀 수 있다는 점에 유의한다. 예를 들어 ([RFC8246]에 정의된) immutable 캐시 지시자는 캐시가 신선한 응답을 가지고 있을 때 조건부 요청 전달을 하지 말라고 지시한다.
조건부 요청 헤더 필드는 (HEAD의 의미를 GET과 일관되게 유지하기 위해) HEAD 메서드와 함께 쓸 수 있도록 정의되어 있지만, 조건부 HEAD를 보내는 것은 의미가 없다. 성공 응답이 304 (Not Modified) 응답과 크기가 비슷하고 412 (Precondition Failed) 응답보다 쓸모 있기 때문이다.
요청에 조건부 요청 헤더 필드가 둘 이상 있으면, 필드를 평가하는 순서가 중요해진다. 실제로 이 문서가 정의한 필드들은 하나의 논리적 순서로 일관되게 구현된다. “갱신 손실” 전제 조건은 캐시 검증보다 요구 사항이 더 엄격하고, 검증된 캐시는 부분 응답보다 효율적이며, 엔터티 태그는 일자 검증자보다 정확하다고 여겨지기 때문이다.
수신한 캐시나 원본 서버는 이 명세가 정의한 요청 전제 조건을 다음 순서로 반드시 평가해야 한다(MUST).
If-Match가 있으면 If-Match 전제 조건을 평가한다.
412 (Precondition Failed)로 응답한다.If-Match가 없으며, If-Unmodified-Since가 있으면 If-Unmodified-Since 전제 조건을 평가한다.
412 (Precondition Failed)로 응답한다.If-None-Match가 있으면 If-None-Match 전제 조건을 평가한다.
GET/HEAD에서 거짓이면 304 (Not Modified)로 응답한다.412 (Precondition Failed)로 응답한다.GET이나 HEAD이고, If-None-Match가 없으며, If-Modified-Since가 있으면 If-Modified-Since 전제 조건을 평가한다.
304 (Not Modified)로 응답한다.GET이고 Range와 If-Range가 모두 있으면 If-Range 전제 조건을 평가한다.
Range가 선택된 표현에 적용 가능하면 206 (Partial Content)로 응답한다.Range 헤더 필드를 무시하고 200 (OK)로 응답한다.추가 조건부 요청 헤더 필드를 정의하는 HTTP 확장은 그 필드들을 이 문서가 정의한 필드 및 실제로 쓰일 수 있는 다른 조건들과의 관계에서 어떤 순서로 평가하는지 정의해야 마땅하다.
역주: 13장의 “ought to”(13.2.2절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 13.1.1~13.1.5절 제목은 헤더 필드 이름이라 원어로 두었다. 13.1.5절의 “short-circuit”은 두 번째 요청을 생략한다는 뜻이라 “건너뛰다”로 옮기고 원어를 병기했다.
클라이언트는 요청이 취소되거나 연결이 끊겨 데이터 전송이 중단되는 일을 자주 겪는다. 클라이언트가 부분 표현을 저장해 두었다면, 이후 요청에서 전체 표현을 전송받기보다 그 표현의 나머지를 요청하는 것이 바람직하다. 마찬가지로 로컬 저장 공간이 제한된 장치는 매우 큰 문서의 한 페이지나 삽입된 이미지의 크기 정보처럼, 더 큰 표현의 일부만 요청할 수 있으면 도움이 된다.
범위 요청은 HTTP의 선택 사항(OPTIONAL)인 기능으로, 이 기능을 구현하지 않은(또는 대상 리소스에 대해 지원하지 않는) 수신자가 상호 운용성에 영향을 주지 않고 일반 GET 요청인 것처럼 응답할 수 있도록 설계되었다. 부분 응답은 이 기능을 구현하지 않았을 수 있는 캐시가 전체 응답으로 오인하지 않도록 별도의 상태 코드로 나타낸다.
표현 데이터의 콘텐츠 코딩이나 미디어 유형에 고유한, 주소 지정이 가능한 구조 단위가 있으면 표현 데이터를 하위 범위로 나눌 수 있다. 예를 들어 옥텟(일명 바이트) 경계는 모든 표현 데이터에 공통인 구조 단위로, 데이터의 일부를 그 데이터의 시작이나 끝에서 어떤 오프셋만큼 떨어진 바이트 범위로 식별할 수 있게 한다.
이 일반적인 “범위 단위(range unit)” 개념은 범위 요청 지원을 알리는 Accept-Ranges(14.3절) 응답 헤더 필드, 표현에서 요청하는 부분을 표시하는 Range(14.2절) 요청 헤더 필드, 그리고 표현의 어느 부분이 전송되는지 설명하는 Content-Range(14.4절) 헤더 필드에서 쓰인다.
range-unit = token
모든 범위 단위 이름은 대소문자를 구분하지 않으며, 16.5.1절에 정의된 “HTTP 범위 단위 레지스트리(HTTP Range Unit Registry)”에 등록되어야 마땅하다.
범위 단위는 16.5절에서 설명하듯 확장할 수 있도록 의도되었다.
범위는 범위 단위와 범위 지정자(range specifier) 집합의 쌍으로 표현한다. 범위 단위 이름은 그 단위의 지정자에 어떤 종류의 range-spec을 적용할 수 있는지 결정한다. 따라서 다음 문법은 일반적인 것이며, 각 범위 단위는 int-range, suffix-range, other-range를 언제 허용하는지에 관한 요구 사항을 명시할 것으로 기대된다.
범위 요청은 하나의 표현 안에서 단일 범위나 범위 집합을 지정할 수 있다.
; 원문: ranges-specifier = range-unit "=" range-set
ranges-specifier = range-unit "=" OWS range-set
range-set = 1#range-spec
range-spec = int-range
/ suffix-range
/ other-range
역주: 정오표 7306(Verified)에 따라 “=” 뒤에
OWS를 넣은 규칙이다(원문 규칙은 주석으로 남김).
int-range는 음이 아닌 정수 두 개로, 또는 음이 아닌 정수 하나부터 표현 데이터의 끝까지로 표현한 범위다. 정수가 무엇을 뜻하는지는 범위 단위가 정한다(예: 시작부터의 단위 오프셋, 양 끝을 포함하는 부분 번호 등).
int-range = first-pos "-" [ last-pos ]
first-pos = 1*DIGIT
last-pos = 1*DIGIT
last-pos 값이 있고 그 값이 first-pos보다 작으면 int-range는 유효하지 않다.
suffix-range는 주어진 음이 아닌 정수를 (범위 단위로 센) 최대 길이로 하는, 표현 데이터의 접미사로 표현한 범위다. 다시 말해, 표현 데이터의 마지막 N 단위다.
suffix-range = "-" suffix-length
suffix-length = 1*DIGIT
확장성을 위해 other-range 규칙은 애플리케이션별 범위 단위나 앞으로 정의될 범위 단위가 추가 범위 지정자를 정의할 수 있도록 거의 제약이 없는 문법이다.
other-range = 1*( %x21-2B / %x2D-7E )
; 1*(VCHAR excluding comma)
ranges-specifier가 표시된 range-unit에 대해 유효하지 않거나 정의되지 않은 range-spec을 하나라도 담고 있으면 그 ranges-specifier는 유효하지 않다.
유효한 ranges-specifier는 표시된 range-unit이 정의한 대로 충족 가능한 range-spec을 적어도 하나 담고 있으면 “충족 가능(satisfiable)”하다. 그렇지 않으면 그 ranges-specifier는 “충족 불가능(unsatisfiable)”하다.
“bytes” 범위 단위는 표현 데이터의 옥텟 시퀀스의 하위 범위를 표현하는 데 쓰인다. 각 바이트 범위는 표현 데이터의 시작(int-range)이나 끝(suffix-range)에 상대적인 어떤 오프셋에서의 정수 범위로 표현한다. 바이트 범위는 other-range 지정자를 쓰지 않는다.
바이트 int-range의 first-pos 값은 범위의 첫 바이트의 오프셋을 준다. last-pos 값은 범위의 마지막 바이트의 오프셋을 준다. 즉, 지정된 바이트 위치는 양 끝을 포함한다. 바이트 오프셋은 0부터 시작한다.
표현 데이터에 콘텐츠 코딩이 적용되어 있으면, 각 바이트 범위는 디코딩한 뒤에 얻을 기반 바이트 시퀀스가 아니라 인코딩된 바이트 시퀀스를 기준으로 계산한다.
바이트 범위 지정자의 예:
처음 500바이트(바이트 오프셋 0-499, 양 끝 포함):
bytes=0-499
두 번째 500바이트(바이트 오프셋 500-999, 양 끝 포함):
bytes=500-999
클라이언트는 선택된 표현의 크기를 몰라도 요청하는 바이트 수를 제한할 수 있다. last-pos 값이 없거나 그 값이 표현 데이터의 현재 길이보다 크거나 같으면, 바이트 범위는 표현의 나머지로 해석된다(즉, 서버는 last-pos 값을 선택된 표현의 현재 길이보다 1 작은 값으로 바꾼다).
클라이언트는 suffix-range를 써서 선택된 표현의 마지막 N바이트(N > 0)를 가리킬 수 있다. 선택된 표현이 지정된 suffix-length보다 짧으면 표현 전체를 쓴다.
길이가 10000인 표현을 가정한 추가 예:
마지막 500바이트(바이트 오프셋 9500-9999, 양 끝 포함):
bytes=-500
또는:
bytes=9500-
첫 바이트와 마지막 바이트만(바이트 0과 9999):
bytes=0-0,-1
처음, 중간, 마지막 1000바이트:
bytes= 0-999, 4500-5499, -1000
두 번째 500바이트(바이트 오프셋 500-999, 양 끝 포함)를 지정하는, 유효하지만 정규형이 아닌 다른 표기:
bytes=500-600,601-999
bytes=500-700,601-999
GET 요청에서 유효한 바이트 range-spec은 다음 중 하나이면 충족 가능하다.
first-pos가 선택된 표현의 현재 길이보다 작은 int-rangesuffix-length가 0이 아닌 suffix-range선택된 표현의 길이가 0이면, GET 요청에서 충족 가능한 range-spec 형식은 suffix-length가 0이 아닌 suffix-range뿐이다.
바이트 범위 구문에서 first-pos, last-pos, suffix-length는 옥텟 수를 십진수로 표현한다. 콘텐츠 길이에는 미리 정해진 한계가 없으므로, 수신자는 큰 십진수가 올 수 있음을 예상하여 정수 변환 오버플로로 인한 파싱 오류를 반드시 막아야 한다(MUST).
GET 요청의 “Range” 헤더 필드는 메서드 의미를 바꾸어, 선택된 표현 전체가 아니라 선택된 표현 데이터(8.1절)의 하위 범위 하나 이상만 전송하도록 요청한다.
Range = ranges-specifier
서버는 Range 헤더 필드를 무시할 수 있다(MAY). 그러나 원본 서버와 중간 캐시는 가능하면 바이트 범위를 지원해야 마땅하다. 바이트 범위는 부분적으로 실패한 전송에서 효율적으로 복구하고 큰 표현의 일부를 가져오는 것을 지원하기 때문이다.
서버는 알지 못하거나 범위 처리가 정의되지 않은 요청 메서드와 함께 받은 Range 헤더 필드를 반드시 무시해야 한다(MUST). 이 명세에서 범위 처리가 정의된 메서드는 GET뿐이다.
원본 서버는 자신이 이해하지 못하는 범위 단위를 담은 Range 헤더 필드를 반드시 무시해야 한다(MUST). 프록시는 자신이 이해하지 못하는 범위 단위를 담은 Range 헤더 필드를 버릴 수 있다(MAY).
범위 요청을 지원하는 서버는 유효하지 않은 ranges-specifier(14.1.1절), 겹치는 범위가 둘을 넘는 ranges-specifier, 또는 오름차순으로 나열되지 않은 작은 범위 여러 개의 집합을 담은 Range 헤더 필드를 무시하거나 거부할 수 있다(MAY). 이런 것은 고장 난 클라이언트나 의도적인 서비스 거부 공격(17.15절)의 징후이기 때문이다. 클라이언트는 같은 데이터를 포괄하는 단일 범위보다 처리하고 전송하기에 본질적으로 덜 효율적인 여러 범위를 요청하지 않아야 한다(SHOULD NOT).
범위 요청을 지원하는 서버는 선택된 표현에 콘텐츠가 없으면(즉, 선택된 표현의 데이터 길이가 0이면) Range 헤더 필드를 무시할 수 있다(MAY).
여러 범위를 요청하는 클라이언트는 뒷부분을 먼저 요청해야 할 특별한 필요가 없는 한, 그 범위들을 오름차순(완전한 표현에서 보통 받게 될 순서)으로 나열해야 한다(SHOULD). 예를 들어 부분들의 내부 목록을 가진 큰 표현을 처리하는 사용자 에이전트는, 특히 표현이 역순으로 저장된 페이지로 이루어져 있고 사용자 에이전트가 한 번에 한 페이지씩 전송하려는 경우, 뒷부분을 먼저 요청해야 할 수 있다.
Range 헤더 필드는 13.1절에 정의된 전제 조건 헤더 필드를 평가한 뒤에, 그리고 Range 헤더 필드가 없을 때의 결과가 200 (OK) 응답일 때에만 평가한다. 다시 말해, 조건부 GET의 결과가 304 (Not Modified) 응답이면 Range는 무시된다.
If-Range 헤더 필드(13.1.5절)는 Range 헤더 필드를 적용하기 위한 전제 조건으로 쓸 수 있다.
모든 전제 조건이 참이고, 서버가 대상 리소스에 대해 Range 헤더 필드를 지원하며, 받은 Range 필드 값이 그 대상 리소스에 지원되는 range-unit을 가진 유효한 ranges-specifier를 담고 있고, 그 ranges-specifier가 선택된 표현에 대해 충족 가능하면, 서버는 요청된 충족 가능한 range-spec에 해당하는 부분 표현 하나 이상을 담은 콘텐츠와 함께 206 (Partial Content) 응답을 보내야 한다(SHOULD).
위 내용이 서버가 요청된 범위를 모두 보낸다는 뜻은 아니다. 어떤 경우에는 요청된 범위의 일부를 먼저 보내고, 나머지 부분은 클라이언트가 여전히 원한다면 나중에 다시 요청하기를 기대하는 것만 가능하거나 효율적일 수 있다(15.3.7절 참고).
모든 전제 조건이 참이고, 서버가 대상 리소스에 대해 Range 헤더 필드를 지원하며, 받은 Range 필드 값이 유효한 ranges-specifier를 담고 있지만, range-unit이 그 대상 리소스에 지원되지 않거나 ranges-specifier가 선택된 표현에 대해 충족 불가능하면, 서버는 416 (Range Not Satisfiable) 응답을 보내야 한다(SHOULD).
응답의 “Accept-Ranges” 필드는 업스트림 서버가 대상 리소스에 대한 범위 요청을 지원하는지 나타낸다.
Accept-Ranges = acceptable-ranges
acceptable-ranges = 1#range-unit
예를 들어 바이트 범위 요청(14.1.2절)을 지원하는 서버는 다음 필드를 보내
Accept-Ranges: bytes
그 대상 리소스에 대해 바이트 범위 요청을 지원한다는 것을 나타내고, 그럼으로써 같은 요청 경로에 대한 이후의 부분 요청에서 클라이언트가 그것을 쓰도록 장려할 수 있다. 범위 단위는 14.1절에 정의되어 있다.
클라이언트는 Accept-Ranges 필드를 받았는지와 상관없이 범위 요청을 생성할 수 있다(MAY). 이 정보는 성능을 높이고 불필요한 네트워크 전송을 줄이기 위한 조언을 제공할 뿐이다.
반대로, 클라이언트는 Accept-Ranges 필드를 받았다고 해서 이후의 범위 요청이 부분 응답을 돌려줄 것이라고 가정해서는 안 된다(MUST NOT). 콘텐츠가 바뀔 수도 있고, 서버가 특정 시간이나 특정 조건에서만 범위 요청을 지원할 수도 있으며, 다음 요청을 다른 중개자가 처리할 수도 있다.
대상 리소스에 대해 어떤 종류의 범위 요청도 지원하지 않는 서버는 다음을 보내
Accept-Ranges: none
클라이언트에게 같은 요청 경로에 범위 요청을 시도하지 말라고 조언할 수 있다(MAY). 범위 단위 “none”은 이 목적으로 예약되어 있다.
Accept-Ranges 필드는 트레일러 섹션으로 보낼 수 있지만(MAY), 헤더 필드로 보내는 것이 선호된다. 이 정보는 콘텐츠 중간에서(트레일러 섹션을 받기 전에) 실패한 큰 정보 전송을 다시 시작할 때 특히 쓸모 있기 때문이다.
“Content-Range” 헤더 필드는 단일 파트 206 (Partial Content) 응답에서 메시지 콘텐츠로 동봉된 선택된 표현의 부분 범위를 나타내기 위해, 멀티파트 206 응답의 각 파트에서 각 본문 부분에 동봉된 범위를 나타내기 위해(14.6절), 그리고 416 (Range Not Satisfiable) 응답에서 선택된 표현에 관한 정보를 제공하기 위해 보낸다.
Content-Range = range-unit SP
( range-resp / unsatisfied-range )
range-resp = incl-range "/" ( complete-length / "*" )
incl-range = first-pos "-" last-pos
unsatisfied-range = "*/" complete-length
complete-length = 1*DIGIT
206 (Partial Content) 응답이 수신자가 이해하지 못하는 범위 단위(14.1절)를 가진 Content-Range 헤더 필드를 담고 있으면, 수신자는 그것을 저장된 표현과 다시 결합하려고 시도해서는 안 된다(MUST NOT). 그런 메시지를 받은 프록시는 그것을 다운스트림으로 전달해야 한다(SHOULD).
Content-Range는 클라이언트와 원본 서버 사이의 사적 합의에 따라, 14.5절에서 설명하는 부분 PUT을 요청하기 위한 요청 수정자로 보낼 수도 있다. 서버는 Content-Range 지원이 정의되지 않은 메서드의 요청에서 받은 Content-Range 헤더 필드를 반드시 무시해야 한다(MUST).
바이트 범위에 대해 송신자는, 완전한 길이를 모르거나 알아내기 어려운 경우가 아니라면, 범위를 추출한 표현의 완전한 길이를 나타내야 한다(SHOULD). complete-length 자리의 별표 문자(“*“)는 헤더 필드를 생성할 때 표현 길이를 몰랐다는 것을 나타낸다.
다음 예는 송신자가 선택된 표현의 완전한 길이를 1234바이트로 알고 있는 경우를 보여 준다.
Content-Range: bytes 42-1233/1234
다음 두 번째 예는 완전한 길이를 모르는 경우를 보여 준다.
Content-Range: bytes 42-1233/*
Content-Range 필드 값이 담은 range-resp에서 last-pos 값이 first-pos 값보다 작거나 complete-length 값이 last-pos 값보다 작거나 같으면, 그 필드 값은 유효하지 않다. 유효하지 않은 Content-Range의 수신자는 받은 콘텐츠를 저장된 표현과 다시 결합하려고 시도해서는 안 된다(MUST NOT).
바이트 범위 요청에 대해 416 (Range Not Satisfiable) 응답을 생성하는 서버는 다음 예처럼 unsatisfied-range 값을 가진 Content-Range 헤더 필드를 보내야 한다(SHOULD).
Content-Range: bytes */1234
416 응답의 complete-length는 선택된 표현의 현재 길이를 나타낸다.
Content-Range 헤더 필드는 그 의미를 명시적으로 설명하지 않는 상태 코드에서는 아무 의미도 없다. 이 명세에서는 206 (Partial Content)와 416 (Range Not Satisfiable) 상태 코드만 Content-Range의 의미를 설명한다.
다음은 선택된 표현이 모두 1234바이트일 때의 Content-Range 값의 예다.
처음 500바이트:
Content-Range: bytes 0-499/1234
두 번째 500바이트:
Content-Range: bytes 500-999/1234
처음 500바이트를 뺀 나머지 전부:
Content-Range: bytes 500-1233/1234
마지막 500바이트:
Content-Range: bytes 734-1233/1234
일부 원본 서버는 사용자 에이전트가 요청에 Content-Range 헤더 필드(14.4절)를 보낼 때 부분 표현의 PUT을 지원하지만, 그런 지원은 일관되지 않으며 사용자 에이전트와의 사적 합의에 달려 있다. 일반적으로 이는 대상 리소스 상태의 일부를, Content-Range 값이 나타내는 오프셋과 길이에서 동봉된 콘텐츠로 교체하도록 요청하는 것이며, 이때 오프셋은 현재 선택된 표현에 상대적이다.
원본 서버는 부분 PUT 요청을 지원하지 않는 대상 리소스에 대한 PUT에서 Content-Range를 받으면 400 (Bad Request) 상태 코드로 응답해야 한다(SHOULD).
부분 PUT은 PUT의 원래 정의와 하위 호환되지 않는다. 콘텐츠가 현재 표현을 완전히 대체하는 것으로 기록될 수 있다.
부분적인 리소스 갱신은 더 큰 리소스의 일부와 겹치거나 그 일부를 확장하는 상태를 가진, 별도로 식별되는 리소스를 대상으로 하거나, 부분 갱신을 위해 특별히 정의된 다른 메서드(예: [RFC5789]에 정의된 PATCH 메서드)를 써서 할 수도 있다.
206 (Partial Content) 응답 메시지가 여러 범위의 콘텐츠를 담을 때, 그 범위들은 “multipart/byteranges” 미디어 유형을 가진 멀티파트 메시지 본문([RFC2046]의 5.1절)의 본문 부분으로 전송된다.
“multipart/byteranges” 미디어 유형은 각각 자신의 Content-Type과 Content-Range 필드를 가진 본문 부분을 하나 이상 담는다. 필수 매개 변수인 boundary는 각 본문 부분을 구분하는 데 쓰는 경계 문자열을 지정한다.
구현 참고 사항:
이름과 달리 “multipart/byteranges” 미디어 유형은 바이트 범위로 제한되지 않는다. 다음 예는 “exampleunit” 범위 단위를 쓴다.
HTTP/1.1 206 Partial Content
Date: Tue, 14 Nov 1995 06:25:24 GMT
Last-Modified: Tue, 14 July 04:58:08 GMT
Content-Length: 2331785
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: exampleunit 1.2-4.3/25
...the first range...
--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: exampleunit 11.2-14.3/25
...the second range
--THIS_STRING_SEPARATES--
다음 정보는 “multipart/byteranges” 미디어 유형의 등록 양식이다.
역주: 14장의 “ought to”(14.1·14.2절)와 “need to”(14.2절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 14.2~14.4절 제목은 헤더 필드 이름이라 원어로 두었다. 등록 양식의 항목 이름은 IANA 양식 이름이라 원어를 병기했고, 값 가운데 N/A, COMMON, IESG 같은 양식 값은 원어로 두었다. 14.6절 예의
Last-Modified: Tue, 14 July 04:58:08 GMT는 연도가 빠져 유효한HTTP-date가 아니지만 원문 그대로 두었다.
응답의 상태 코드는 요청의 결과와 응답의 의미를 설명하는 세 자리 정수 코드로, 요청이 성공했는지와 어떤 콘텐츠가 동봉되었는지(있다면)를 포함한다. 유효한 상태 코드는 모두 100부터 599까지(양 끝 포함)의 범위 안에 있다.
상태 코드의 첫 자리는 응답의 클래스를 정의한다. 마지막 두 자리는 분류 역할을 하지 않는다. 첫 자리에는 다섯 가지 값이 있다.
1xx (Informational): 요청을 받았으며 처리를 계속하는 중2xx (Successful): 요청을 성공적으로 받고, 이해하고, 수락함3xx (Redirection): 요청을 완료하려면 추가 조치가 필요함4xx (Client Error): 요청에 잘못된 구문이 있거나 요청을 이행할 수 없음5xx (Server Error): 서버가 겉보기에 유효한 요청을 이행하지 못함HTTP 상태 코드는 확장할 수 있다. 클라이언트가 등록된 모든 상태 코드의 의미를 이해하는 것이 분명 바람직하지만, 꼭 이해해야 하는 것은 아니다. 그러나 클라이언트는 첫 자리로 나타나는 모든 상태 코드의 클래스를 반드시 이해해야 하고, 알지 못하는 상태 코드를 그 클래스의 x00 상태 코드와 동등하게 다뤄야 한다(MUST).
예를 들어 클라이언트가 알지 못하는 상태 코드 471을 받으면, 첫 자리를 보고 자신의 요청에 뭔가 잘못이 있었음을 알 수 있으므로 400 (Bad Request) 상태 코드를 받은 것처럼 응답을 다룰 수 있다. 응답 메시지에는 보통 그 상태를 설명하는 표현이 담겨 있다.
100..599 범위 밖의 값은 유효하지 않다. 구현은 HTTP가 아닌 상태(예: 라이브러리 오류)를 내부에서 전달하기 위해 그 범위 밖의 세 자리 정수 값(즉, 600..999)을 쓰는 경우가 많다. 유효하지 않은 상태 코드를 가진 응답을 받은 클라이언트는 그 응답이 5xx (Server Error) 상태 코드를 가진 것처럼 처리해야 한다(SHOULD).
하나의 요청에는 여러 응답이 연관될 수 있다. “정보(informational)”(1xx) 범위의 상태 코드를 가진 0개 이상의 “중간(interim)”(최종이 아닌) 응답과, 그 뒤에 오는 다른 범위 중 하나의 상태 코드를 가진 정확히 하나의 “최종(final)” 응답이다.
아래에 나열한 상태 코드는 이 명세에서 정의한다. 여기에 나열한 이유 구문은 권장일 뿐이며, 프로토콜에 영향을 주지 않고 지역에 맞는 동등한 표현으로 바꾸거나 아예 생략할 수 있다.
휴리스틱 캐시 가능(heuristically cacheable)으로 정의된 상태 코드(예: 이 명세에서는 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501)를 가진 응답은, 메서드 정의나 명시적 캐시 제어([CACHING])가 달리 나타내지 않는 한 캐시가 휴리스틱 만료(heuristic expiration)를 적용해 재사용할 수 있다. 그 밖의 모든 상태 코드는 휴리스틱 캐싱이 불가능하다.
이 명세의 범위 밖에서도 HTTP에서 쓰도록 명시된 추가 상태 코드들이 있다. 그런 상태 코드는 모두 16.2절에서 설명하듯 “HTTP 상태 코드 레지스트리(Hypertext Transfer Protocol (HTTP) Status Code Registry)”에 등록되어야 마땅하다.
1xx (Informational) 클래스의 상태 코드는 요청된 동작을 완료하고 최종 응답을 보내기 전에 연결 상태나 요청 진행 상황을 전달하기 위한 중간 응답을 나타낸다. HTTP/1.0은 1xx 상태 코드를 정의하지 않았으므로, 서버는 HTTP/1.0 클라이언트에 1xx 응답을 보내서는 안 된다(MUST NOT).
1xx 응답은 헤더 섹션의 끝에서 종료된다. 콘텐츠나 트레일러를 담을 수 없다.
클라이언트는 최종 응답 전에 받은 하나 이상의 1xx 응답을, 기대하지 않았더라도, 반드시 파싱할 수 있어야 한다(MUST). 사용자 에이전트는 기대하지 않은 1xx 응답을 무시할 수 있다(MAY).
프록시는 자신이 1xx 응답 생성을 요청한 경우가 아니라면 1xx 응답을 반드시 전달해야 한다(MUST). 예를 들어 프록시가 요청을 전달하면서 “Expect: 100-continue” 헤더 필드를 추가했다면, 그에 해당하는 100 (Continue) 응답은 전달할 필요가 없다.
100 (Continue) 상태 코드는 요청의 처음 부분을 받았고 서버가 아직 거부하지 않았음을 나타낸다. 서버는 요청을 모두 받고 처리한 뒤에 최종 응답을 보낼 생각이다.
요청에 100-continue 기대를 담은 Expect 헤더 필드가 있으면, 100 응답은 10.1.1절에서 설명하듯 서버가 요청 콘텐츠를 받기를 원한다는 것을 나타낸다. 클라이언트는 요청을 계속 보내고 100 응답은 버려야 마땅하다.
요청에 100-continue 기대를 담은 Expect 헤더 필드가 없었다면, 클라이언트는 이 중간 응답을 그냥 버릴 수 있다.
101 (Switching Protocols) 상태 코드는 서버가 이 연결에서 쓰는 애플리케이션 프로토콜을 바꾸자는, Upgrade 헤더 필드(7.8절)를 통한 클라이언트의 요청을 이해했고 이에 따를 의향이 있음을 나타낸다. 서버는 이 응답 뒤에 어떤 프로토콜(들)이 적용될지 나타내는 Upgrade 헤더 필드를 응답에 반드시 생성해야 한다(MUST).
서버는 프로토콜을 바꾸는 것이 유리할 때에만 바꾸는 데 동의한다고 가정한다. 예를 들어 HTTP의 새 버전으로 바꾸는 것이 이전 버전보다 유리할 수 있고, 실시간 동기식 프로토콜로 바꾸는 것은 그런 기능을 쓰는 리소스를 전달할 때 유리할 수 있다.
2xx (Successful) 클래스의 상태 코드는 클라이언트의 요청을 성공적으로 받고, 이해하고, 수락했음을 나타낸다.
200 (OK) 상태 코드는 요청이 성공했음을 나타낸다. 200 응답으로 보내는 콘텐츠는 요청 메서드에 따라 다르다. 이 명세가 정의한 메서드에 대해 콘텐츠의 의도된 의미는 다음과 같이 요약할 수 있다.
| 요청 메서드 | 응답 콘텐츠가 표현하는 것 |
|---|---|
| GET | 대상 리소스 |
| HEAD | GET처럼 대상 리소스이지만, 표현 데이터는 전송하지 않음 |
| POST | 동작의 상태, 또는 동작으로 얻은 결과 |
| PUT, DELETE | 동작의 상태 |
| OPTIONS | 대상 리소스의 통신 옵션 |
| TRACE | 추적을 돌려주는 서버가 받은 그대로의 요청 메시지 |
표 6
CONNECT에 대한 응답이 아니라면, 200 응답은 메시지 프레이밍이 콘텐츠 길이가 0이라고 명시적으로 나타내지 않는 한 메시지 콘텐츠를 담을 것으로 기대된다. 요청의 어떤 측면이 성공 시 콘텐츠가 없기를 선호한다고 나타낸다면, 원본 서버는 대신 204 (No Content) 응답을 보내야 마땅하다. CONNECT의 경우 성공 결과가 200 응답 헤더 섹션 바로 뒤에서 시작되는 터널이므로 콘텐츠가 없다.
200 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
GET이나 HEAD에 대한 200 응답에서 원본 서버는 선택된 표현에 대해 사용할 수 있는 검증자 필드(8.8절)를 보내야 하며(SHOULD), 강한 엔터티 태그와 Last-Modified 일자를 모두 보내는 것이 선호된다.
상태를 바꾸는 메서드에 대한 200 응답에서, 응답에 보낸 검증자 필드(8.8절)는 요청 의미를 성공적으로 적용한 결과로 만들어진 새 표현의 현재 검증자를 전달한다. PUT 메서드(9.3.4절)에는 그런 검증자를 보내지 못하게 할 수 있는 추가 요구 사항이 있다는 점에 유의한다.
201 (Created) 상태 코드는 요청이 이행되었고 그 결과 하나 이상의 새 리소스가 생성되었음을 나타낸다. 요청으로 생성된 주 리소스는 응답의 Location 헤더 필드로, 또는 Location 헤더 필드를 받지 못했다면 대상 URI로 식별된다.
201 응답 콘텐츠는 보통 생성된 리소스(들)를 설명하고 그것에 링크한다. 응답에 보낸 검증자 필드(8.8절)는 요청으로 생성된 새 표현의 현재 검증자를 전달한다. PUT 메서드(9.3.4절)에는 그런 검증자를 보내지 못하게 할 수 있는 추가 요구 사항이 있다는 점에 유의한다.
202 (Accepted) 상태 코드는 요청이 처리를 위해 수락되었지만 처리가 완료되지 않았음을 나타낸다. 실제로 처리가 이루어질 때 허용되지 않을 수도 있으므로, 요청은 결국 처리될 수도 있고 처리되지 않을 수도 있다. HTTP에는 비동기 작업에서 상태 코드를 다시 보내는 기능이 없다.
202 응답은 의도적으로 확답을 주지 않는다. 그 목적은 사용자 에이전트와 서버의 연결이 처리가 끝날 때까지 유지될 필요 없이, 서버가 다른 처리(아마도 하루에 한 번만 실행되는 일괄 처리)를 위한 요청을 수락할 수 있게 하는 것이다. 이 응답과 함께 보내는 표현은 요청의 현재 상태를 설명하고, 요청이 언제 이행될지 추정치를 사용자에게 제공할 수 있는 상태 모니터를 가리키거나 포함해야 마땅하다.
203 (Non-Authoritative Information) 상태 코드는 요청이 성공했지만, 동봉된 콘텐츠가 변형 프록시(7.7절)에 의해 원본 서버의 200 (OK) 응답과 달라졌음을 나타낸다. 이 상태 코드로 프록시는 변형이 적용되었음을 수신자에게 알릴 수 있다. 그 사실이 콘텐츠에 관한 이후 결정에 영향을 줄 수 있기 때문이다. 예를 들어 이후 그 콘텐츠에 대한 캐시 검증 요청은 같은 요청 경로를 따라서만(같은 프록시를 거쳐서만) 적용될 수 있다.
203 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
204 (No Content) 상태 코드는 서버가 요청을 성공적으로 이행했으며 응답 콘텐츠로 보낼 추가 콘텐츠가 없음을 나타낸다. 응답 헤더 필드의 메타데이터는 요청된 동작이 적용된 뒤의 대상 리소스와 그 선택된 표현을 가리킨다.
예를 들어 PUT 요청에 대한 응답으로 204 상태 코드를 받았고 응답에 ETag 필드가 있다면, PUT은 성공했으며 ETag 필드 값은 그 대상 리소스의 새 표현에 대한 엔터티 태그를 담고 있다.
204 응답으로 서버는 대상 리소스에 동작이 성공적으로 적용되었음을 나타내면서, 사용자 에이전트가 현재의 “문서 보기(document view)”(있다면)에서 벗어날 필요가 없음을 암시할 수 있다. 서버는 사용자 에이전트가 자신의 인터페이스에 맞게 사용자에게 성공을 어떤 식으로든 표시하고, 응답의 새 메타데이터나 갱신된 메타데이터를 활성 표현에 적용할 것이라고 가정한다.
예를 들어 204 상태 코드는 “저장” 동작에 해당하는 문서 편집 인터페이스에서 흔히 쓰여, 저장되는 문서를 사용자가 계속 편집할 수 있게 한다. 분산 버전 관리 시스템처럼 자동 데이터 전송이 주를 이룰 것으로 예상되는 인터페이스에서도 자주 쓰인다.
204 응답은 헤더 섹션의 끝에서 종료된다. 콘텐츠나 트레일러를 담을 수 없다.
204 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
205 (Reset Content) 상태 코드는 서버가 요청을 이행했으며, 사용자 에이전트가 요청을 보내게 한 “문서 보기”를 원본 서버에서 받은 원래 상태로 재설정하기를 바란다는 것을 나타낸다.
이 응답은 흔한 데이터 입력 사용 사례를 지원하기 위한 것이다. 사용자는 데이터 입력을 지원하는 콘텐츠(양식, 메모장, 캔버스 등)를 받아 그 공간에 데이터를 입력하거나 조작하고, 입력한 데이터를 요청으로 제출하게 하면, 사용자가 다른 입력 동작을 쉽게 시작할 수 있도록 데이터 입력 메커니즘이 다음 입력을 위해 재설정된다.
205 상태 코드는 추가 콘텐츠가 제공되지 않음을 암시하므로, 서버는 205 응답에 콘텐츠를 생성해서는 안 된다(MUST NOT).
206 (Partial Content) 상태 코드는 서버가 선택된 표현의 하나 이상의 부분을 전송하여 대상 리소스에 대한 범위 요청을 성공적으로 이행하고 있음을 나타낸다.
범위 요청(14장)을 지원하는 서버는 보통 요청된 범위를 모두 충족시키려고 한다. 더 적은 데이터를 보내면 클라이언트가 나머지를 위해 다시 요청할 가능성이 크기 때문이다. 그러나 서버는 일시적인 사용 불가, 캐시 효율, 부하 분산 등 자신의 이유로 요청된 데이터의 일부만 보내고 싶을 수 있다. 206 응답은 자기 서술적이므로, 클라이언트는 범위 요청을 부분적으로만 충족하는 응답도 이해할 수 있다.
클라이언트는 어떤 부분이 동봉되었고 추가 요청이 필요한지 판단하기 위해 206 응답의 Content-Type과 Content-Range 필드(들)를 반드시 살펴야 한다(MUST).
206 응답을 생성하는 서버는 아래 하위 절에서 요구하는 필드에 더해, 같은 요청에 대한 200 (OK) 응답에서 보냈을 필드라면 다음 헤더 필드를 반드시 생성해야 한다(MUST). Date, Cache-Control, ETag, Expires, Content-Location, Vary.
206 응답에 있는 Content-Length 헤더 필드는 이 메시지 콘텐츠의 옥텟 수를 나타내며, 이는 보통 선택된 표현의 완전한 길이가 아니다. 각 Content-Range 헤더 필드에는 선택된 표현의 완전한 길이에 관한 정보가 들어 있다.
If-Range 헤더 필드가 있는 요청에 206 응답을 생성하는 송신자는 필요한 것 외의 다른 표현 헤더 필드를 생성하지 않아야 한다(SHOULD NOT). 클라이언트는 이미 그 헤더 필드들을 담은 이전 응답을 가지고 있기 때문이다. 그렇지 않은 경우 송신자는 같은 요청에 대한 200 (OK) 응답에서 보냈을 표현 헤더 필드를 모두 반드시 생성해야 한다(MUST).
206 응답은 휴리스틱 캐시 가능하다. 즉, 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
단일 파트를 전송하는 경우, 206 응답을 생성하는 서버는 선택된 표현의 어떤 범위가 동봉되었는지 설명하는 Content-Range 헤더 필드와, 그 범위로 이루어진 콘텐츠를 반드시 생성해야 한다(MUST). 예:
HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 1995 06:25:24 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Content-Range: bytes 21010-47021/47022
Content-Length: 26012
Content-Type: image/gif
... 26012 bytes of partial image data ...
여러 파트를 전송하는 경우, 206 응답을 생성하는 서버는 14.6절에 정의된 “multipart/byteranges” 콘텐츠와, “multipart/byteranges” 미디어 유형 및 그 필수 매개 변수 boundary를 담은 Content-Type 헤더 필드를 반드시 생성해야 한다(MUST). 단일 파트 응답과 혼동되지 않도록, 서버는 다중 파트 응답의 HTTP 헤더 섹션에 Content-Range 헤더 필드를 생성해서는 안 된다(MUST NOT)(이 필드는 대신 각 파트에 보낸다).
멀티파트 콘텐츠의 각 본문 부분의 헤더 영역 안에, 서버는 그 본문 부분에 동봉된 범위에 해당하는 Content-Range 헤더 필드를 반드시 생성해야 한다(MUST). 선택된 표현이 200 (OK) 응답에서 Content-Type 헤더 필드를 가졌을 것이라면, 서버는 각 본문 부분의 헤더 영역에 같은 Content-Type 헤더 필드를 생성해야 한다(SHOULD). 예:
HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 1995 06:25:24 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Content-Length: 1741
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 500-999/8000
...the first range...
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 7000-7999/8000
...the second range
--THIS_STRING_SEPARATES--
여러 범위가 요청되었을 때, 서버는 받은 Range 헤더 필드에서 해당 range-spec이 나타난 순서와 상관없이, 겹치는 범위나 여러 파트를 보내는 오버헤드보다 작은 간격으로 떨어진 범위를 병합할 수 있다(MAY). “multipart/byteranges”의 각 파트 사이의 오버헤드는 선택된 표현의 미디어 유형과 고른 boundary 매개 변수의 길이에 따라 보통 80바이트 정도이므로, 서로 떨어진 작은 파트를 많이 전송하는 것은 선택된 표현 전체를 전송하는 것보다 덜 효율적일 수 있다.
단일 범위 요청에 대해 서버는 멀티파트 응답을 생성해서는 안 된다(MUST NOT). 여러 파트를 요청하지 않는 클라이언트는 멀티파트 응답을 지원하지 않을 수 있기 때문이다. 그러나 여러 범위가 요청되었는데 충족 가능한 범위가 하나뿐이거나 병합한 뒤에 범위가 하나만 남았다면, 서버는 본문 부분이 하나뿐인 “multipart/byteranges” 응답을 생성할 수 있다(MAY). “multipart/byteranges” 응답을 처리할 수 없는 클라이언트는 여러 범위를 요구하는 요청을 생성해서는 안 된다(MUST NOT).
멀티파트 응답을 생성하는 서버는, 충족 불가능하다고 판단했거나 다른 범위로 병합한 범위를 빼고, 받은 Range 헤더 필드에서 해당 range-spec이 나타난 순서와 같은 순서로 파트를 보내야 한다(SHOULD). 멀티파트 응답을 받은 클라이언트는 각 본문 부분에 어떤 범위가 들어 있는지 판단하기 위해 각 본문 부분에 있는 Content-Range 헤더 필드를 반드시 살펴야 한다(MUST). 클라이언트는 자신이 요청한 것과 같은 범위를, 또는 요청한 것과 같은 순서로 받으리라고 기대할 수 없다.
연결이 일찍 닫혔거나 요청이 하나 이상의 Range 지정을 사용했다면, 응답은 표현의 하위 범위만 전송할 수 있다. 그런 전송을 여러 번 하고 나면, 클라이언트는 같은 표현의 범위 여러 개를 받았을 수 있다. 이 범위들은 모두 같은 강한 검증자(8.8.1절)를 공통으로 가질 때에만 안전하게 결합할 수 있다.
대상 리소스에 대한 GET 요청에 여러 부분 응답을 받은 클라이언트는, 그 응답들이 같은 강한 검증자를 공유한다면 그것들을 더 큰 연속 범위로 결합할 수 있다(MAY).
가장 최근 응답이 불완전한 200 (OK) 응답이면, 그 응답의 헤더 필드를 결합된 응답에 쓰고 일치하는 저장된 응답들의 헤더 필드를 대체한다.
가장 최근 응답이 206 (Partial Content) 응답이고 일치하는 저장된 응답 중 적어도 하나가 200 (OK)이면, 결합된 응답의 헤더 필드는 가장 최근 200 응답의 헤더 필드로 이루어진다. 일치하는 저장된 응답이 모두 206 응답이면, 가장 최근 헤더 필드를 가진 저장된 응답을 결합된 응답의 헤더 필드 원천으로 쓴다. 다만 클라이언트는 Content-Range를 제외하고 새 응답에서 제공된 다른 헤더 필드를 써서, 저장된 응답의 해당 헤더 필드 인스턴스를 모두 반드시 대체해야 한다(MUST).
결합된 응답 콘텐츠는 새 응답과, 일치하는 모든 저장된 응답에 들어 있는 부분 콘텐츠 범위의 합집합으로 이루어진다. 합집합이 표현의 전체 범위로 이루어지면, 클라이언트는 결합된 응답을 완전한 길이를 반영한 Content-Length 헤더 필드를 포함하는 완전한 200 (OK) 응답처럼 반드시 처리해야 한다(MUST). 그렇지 않으면 클라이언트는 연속 범위들의 집합을 다음 중 하나로 반드시 처리해야 한다(MUST). 결합된 응답이 표현의 앞부분이면 불완전한 200 (OK) 응답으로, “multipart/byteranges” 콘텐츠를 담은 단일 206 (Partial Content) 응답으로, 또는 각각 Content-Range 헤더 필드로 표시된 연속 범위 하나를 가진 여러 206 (Partial Content) 응답으로 처리한다.
3xx (Redirection) 클래스의 상태 코드는 요청을 이행하려면 사용자 에이전트가 추가 조치를 취해야 함을 나타낸다. 리다이렉트에는 몇 가지 유형이 있다.
301 (Moved Permanently), 302 (Found), 307 (Temporary Redirect), 308 (Permanent Redirect) 상태 코드처럼, Location 헤더 필드가 제공하는 다른 URI에서 이 리소스를 사용할 수 있을지도 모른다고 나타내는 리다이렉트.300 (Multiple Choices) 상태 코드처럼, 이 리소스를 표현할 수 있는 일치하는 리소스들 가운데 선택지를 제공하는 리다이렉션.303 (See Other) 상태 코드처럼, 요청에 대한 간접 응답을 표현할 수 있는, Location 헤더 필드로 식별되는 다른 리소스로의 리다이렉션.304 (Not Modified) 상태 코드처럼, 이전에 저장된 결과로의 리다이렉션.참고: HTTP/1.0에서 상태 코드
301 (Moved Permanently)와302 (Found)는 원래 CERN의 구현에 맞춰 메서드를 유지하는 것으로 정의되었고([HTTP/1.0]의 9.3절),303 (See Other)는 메서드를GET으로 바꾸는 리다이렉션으로 정의되었다. 그러나 초기 사용자 에이전트들은POST요청을 (당시 명세에 따라)POST로 리다이렉트할지, (다른 사이트로 리다이렉트될 때 더 안전한 대안인)GET으로 리다이렉트할지를 두고 갈렸다. 결국 널리 쓰이는 관행은 메서드를GET으로 바꾸는 쪽으로 모였다. 이후 메서드를 유지하는 리다이렉트를 모호하지 않게 나타내기 위해307 (Temporary Redirect)와308 (Permanent Redirect)([RFC7538])가 추가되었고, 상태 코드301과302는POST요청을GET으로 리다이렉트하는 것을 허용하도록 조정되었다.
Location 헤더 필드(10.2.2절)가 제공되면, 사용자 에이전트는 특정 상태 코드를 이해하지 못하더라도 Location 필드 값이 참조하는 URI로 요청을 자동으로 리다이렉트할 수 있다(MAY). 사용자가 안전하지 않은 요청을 리다이렉트하기를 원하지 않을 수 있으므로, 9.2.1절에 정의된 대로 안전하다고 알려지지 않은 메서드에 대한 자동 리다이렉션은 신중하게 해야 한다.
리다이렉트된 요청을 자동으로 따라갈 때, 사용자 에이전트는 다음과 같이 수정하여 원래 요청 메시지를 다시 보내야 한다(SHOULD).
Location 헤더 필드 값이 참조하는 URI를 원래 요청의 대상 URI에 상대적으로 해석한 URI로 바꾼다.Proxy-Authorization을 포함하되 그에 한정되지 않는, 클라이언트의 프록시 설정에 특정한 헤더 필드Host를 포함하되 그에 한정되지 않는, 출처별 헤더 필드(있다면)If-None-Match, If-Modified-Since)Referer, Origin, Authorization, Cookie를 포함하되 그에 한정되지 않는, 리소스별 헤더 필드Authorization과 Cookie가 포함되며 그에 한정되지 않는다.GET이나 HEAD로 바뀌었다면, Content-Encoding, Content-Language, Content-Location, Content-Type, Content-Length, Digest, Last-Modified를 포함하되 그에 한정되지 않는 콘텐츠별 헤더 필드를 제거한다.클라이언트는 순환 리다이렉션(즉, “무한” 리다이렉션 루프)을 감지하고 개입해야 한다(SHOULD).
참고: 이 명세의 이전 버전은 리다이렉션을 최대 다섯 번까지로 권고했다([RFC2068]의 10.3절). 콘텐츠 개발자는 일부 클라이언트가 그런 고정된 제한을 구현했을 수 있다는 점을 알아 두어야 한다.
역주: 2.3항의 “Origin-specific header fields”는 4장의 결정대로 origin을 “출처”로 옮겨 “출처별 헤더 필드”로 했다. 2026-10-17 기준 15.4절에 대한 정오표 8138은 기각(Rejected)되어 반영하지 않았다.
300 (Multiple Choices) 상태 코드는 대상 리소스에 각각 더 구체적인 식별자를 가진 표현이 둘 이상 있으며, 사용자(또는 사용자 에이전트)가 그 식별자 중 하나 이상으로 요청을 리다이렉트하여 선호하는 표현을 선택할 수 있도록 대안에 관한 정보를 제공하고 있음을 나타낸다. 다시 말해, 서버는 사용자 에이전트가 자신의 필요에 가장 적합한 표현(들)을 선택하기 위해 사후 협상(12장)에 참여하기를 원한다.
서버에 선호하는 선택지가 있다면, 서버는 선호하는 선택지의 URI 참조를 담은 Location 헤더 필드를 생성해야 한다(SHOULD). 사용자 에이전트는 자동 리다이렉션에 Location 필드 값을 쓸 수 있다(MAY).
HEAD가 아닌 요청 메서드에 대해, 서버는 사용자나 사용자 에이전트가 가장 선호하는 것을 고를 수 있는 표현 메타데이터와 URI 참조(들)의 목록을 담은 콘텐츠를 300 응답에 생성해야 한다(SHOULD). 사용자 에이전트는 제공된 미디어 유형을 이해한다면 그 목록에서 자동으로 선택할 수 있다(MAY). HTTP는 자신의 콘텐츠 정의와 직교하도록 유지하려 하므로, 이 명세는 자동 선택을 위한 특정 형식을 정의하지 않는다. 실제로 표현은 공유된 설계나 콘텐츠 협상으로 정해진, 사용자 에이전트가 받아들일 수 있다고 여겨지는 쉽게 파싱되는 형식이나, 흔히 받아들여지는 하이퍼텍스트 형식으로 제공된다.
300 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
참고:
300상태 코드의 원래 제안은 대안 표현의 목록을 제공하는URI헤더 필드를 정의하여,200,300,406응답에서 쓸 수 있고HEAD메서드에 대한 응답으로도 전송될 수 있게 했다. 그러나 배포가 부족하고 구문에 대한 의견이 갈려,URI와 (그 뒤의 제안인)Alternates는 모두 이 명세에서 빠졌다. 구성원이 “alternate” 관계를 가진Link헤더 필드 값([RFC8288])으로 목록을 전달할 수는 있지만, 배포는 닭이 먼저냐 달걀이 먼저냐의 문제다.
301 (Moved Permanently) 상태 코드는 대상 리소스에 새 영구 URI가 할당되었으며 이 리소스에 대한 이후의 참조는 동봉된 URI 중 하나를 써야 마땅하다는 것을 나타낸다. 서버는 링크 편집 기능을 가진 사용자 에이전트가 대상 URI에 대한 참조를 서버가 보낸 새 참조 중 하나로 영구히 바꿀 수 있다고 제안하는 것이다. 그러나 이 제안은 사용자 에이전트가 참조를 적극적으로 편집하고 있고(예: 콘텐츠 저작 중), 연결이 보안 처리되어 있으며, 원본 서버가 편집 중인 콘텐츠에 대해 신뢰할 수 있는 권한 기관인 경우가 아니면 보통 무시된다.
서버는 새 영구 URI에 대해 선호하는 URI 참조를 담은 Location 헤더 필드를 응답에 생성해야 한다(SHOULD). 사용자 에이전트는 자동 리다이렉션에 Location 필드 값을 쓸 수 있다(MAY). 서버의 응답 콘텐츠에는 보통 새 URI(들)로의 하이퍼링크가 있는 짧은 하이퍼텍스트 안내가 들어 있다.
참고: 역사적인 이유로, 사용자 에이전트는 이후 요청에서 요청 메서드를
POST에서GET으로 바꿀 수 있다(MAY). 이 동작을 원하지 않으면 대신308 (Permanent Redirect)상태 코드를 쓸 수 있다.
301 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
302 (Found) 상태 코드는 대상 리소스가 일시적으로 다른 URI 아래에 있음을 나타낸다. 리다이렉션은 때때로 바뀔 수 있으므로, 클라이언트는 이후 요청에 대상 URI를 계속 써야 마땅하다.
서버는 다른 URI에 대한 URI 참조를 담은 Location 헤더 필드를 응답에 생성해야 한다(SHOULD). 사용자 에이전트는 자동 리다이렉션에 Location 필드 값을 쓸 수 있다(MAY). 서버의 응답 콘텐츠에는 보통 다른 URI(들)로의 하이퍼링크가 있는 짧은 하이퍼텍스트 안내가 들어 있다.
참고: 역사적인 이유로, 사용자 에이전트는 이후 요청에서 요청 메서드를
POST에서GET으로 바꿀 수 있다(MAY). 이 동작을 원하지 않으면 대신307 (Temporary Redirect)상태 코드를 쓸 수 있다.
303 (See Other) 상태 코드는 서버가 원래 요청에 대한 간접 응답을 제공하기 위해, Location 헤더 필드의 URI가 나타내는 다른 리소스로 사용자 에이전트를 리다이렉트하고 있음을 나타낸다. 사용자 에이전트는 그 URI를 대상으로 하는 검색 요청(HTTP를 쓴다면 GET이나 HEAD 요청)을 수행할 수 있고, 그 요청도 리다이렉트될 수 있으며, 최종 결과를 원래 요청에 대한 답으로 제시할 수 있다. Location 헤더 필드의 새 URI는 대상 URI와 동등한 것으로 여겨지지 않는다는 점에 유의한다.
이 상태 코드는 어떤 HTTP 메서드에도 적용할 수 있다. 주로 POST 동작의 출력이 사용자 에이전트를 다른 리소스로 리다이렉트하도록 하는 데 쓰인다. 그렇게 하면 POST 응답에 해당하는 정보를 별도로 식별하고, 북마크하고, 캐시할 수 있는 리소스로 제공하기 때문이다.
GET 요청에 대한 303 응답은 원본 서버에 HTTP로 전송할 수 있는 대상 리소스의 표현이 없음을 나타낸다. 그러나 Location 필드 값은 대상 리소스를 설명하는 리소스를 가리키므로, 그 다른 리소스에 검색 요청을 하면 원래 대상 리소스를 표현한다는 뜻 없이 수신자에게 쓸모 있는 표현을 얻을 수 있다. 무엇을 표현할 수 있는지, 어떤 표현이 적절한지, 무엇이 쓸모 있는 설명일 수 있는지에 대한 답은 HTTP의 범위 밖이라는 점에 유의한다.
HEAD 요청에 대한 응답을 제외하면, 303 응답의 표현에는 Location 헤더 필드에 제공된 것과 같은 URI 참조로의 하이퍼링크가 있는 짧은 하이퍼텍스트 안내가 들어 있어야 마땅하다.
304 (Not Modified) 상태 코드는 조건부 GET이나 HEAD 요청을 받았으며, 조건이 거짓으로 평가되지 않았다면 200 (OK) 응답이 되었을 것임을 나타낸다. 다시 말해, 요청을 조건부로 만든 클라이언트가 이미 유효한 표현을 가지고 있다고 요청이 나타내므로 서버가 대상 리소스의 표현을 전송할 필요가 없다. 따라서 서버는 클라이언트가 그 저장된 표현을 200 (OK) 응답의 콘텐츠인 것처럼 쓰도록 리다이렉트하고 있는 것이다.
304 응답을 생성하는 서버는 같은 요청에 대한 200 (OK) 응답에서 보냈을 다음 헤더 필드를 반드시 생성해야 한다(MUST).
Content-Location, Date, ETag, VaryCache-Control, Expires([CACHING] 참고)304 응답의 목표는 수신자가 이미 캐시된 표현을 하나 이상 가지고 있을 때 정보 전송을 최소화하는 것이므로, 송신자는 위에 나열한 필드 외의 표현 메타데이터는, 그 메타데이터가 캐시 갱신을 안내할 목적으로 존재하는 경우(예: 응답에 ETag 필드가 없으면 Last-Modified가 쓸모 있을 수 있다)가 아니면 생성하지 않아야 한다(SHOULD NOT).
304 응답을 받는 캐시에 대한 요구 사항은 [CACHING]의 4.3.4절에 정의되어 있다. 조건부 요청이 자신의 캐시를 가진 사용자 에이전트가 공유 프록시에 조건부 GET을 보내는 경우처럼 아웃바운드 클라이언트에서 시작되었다면, 프록시는 304 응답을 그 클라이언트로 전달해야 한다(SHOULD).
304 응답은 헤더 섹션의 끝에서 종료된다. 콘텐츠나 트레일러를 담을 수 없다.
305 (Use Proxy) 상태 코드는 이 명세의 이전 버전에서 정의되었으며 지금은 사용이 중단되었다([RFC7231]의 부록 B).
306 상태 코드는 이 명세의 이전 버전에서 정의되었고, 더 이상 쓰이지 않으며, 코드는 예약되어 있다.
307 (Temporary Redirect) 상태 코드는 대상 리소스가 일시적으로 다른 URI 아래에 있으며, 사용자 에이전트가 그 URI로 자동 리다이렉션을 수행한다면 요청 메서드를 바꿔서는 안 된다(MUST NOT)는 것을 나타낸다. 리다이렉션은 시간에 따라 바뀔 수 있으므로, 클라이언트는 이후 요청에 원래 대상 URI를 계속 써야 마땅하다.
서버는 다른 URI에 대한 URI 참조를 담은 Location 헤더 필드를 응답에 생성해야 한다(SHOULD). 사용자 에이전트는 자동 리다이렉션에 Location 필드 값을 쓸 수 있다(MAY). 서버의 응답 콘텐츠에는 보통 다른 URI(들)로의 하이퍼링크가 있는 짧은 하이퍼텍스트 안내가 들어 있다.
308 (Permanent Redirect) 상태 코드는 대상 리소스에 새 영구 URI가 할당되었으며 이 리소스에 대한 이후의 참조는 동봉된 URI 중 하나를 써야 마땅하다는 것을 나타낸다. 사용자 에이전트가 그 URI로 자동 리다이렉션을 수행한다면 요청 메서드를 바꿔서는 안 된다(MUST NOT). 서버는 링크 편집 기능을 가진 사용자 에이전트가 대상 URI에 대한 참조를 서버가 보낸 새 참조 중 하나로 영구히 바꿀 수 있다고 제안하는 것이다. 그러나 이 제안은 사용자 에이전트가 참조를 적극적으로 편집하고 있고(예: 콘텐츠 저작 중), 연결이 보안 처리되어 있으며, 원본 서버가 편집 중인 콘텐츠에 대해 신뢰할 수 있는 권한 기관인 경우가 아니면 보통 무시된다.
역주: 첫 문단 둘째 문장은 정오표 7109(Verified)에 따라 추가한 내용이다.
서버는 새 영구 URI에 대해 선호하는 URI 참조를 담은 Location 헤더 필드를 응답에 생성해야 한다(SHOULD). 사용자 에이전트는 자동 리다이렉션에 Location 필드 값을 쓸 수 있다(MAY). 서버의 응답 콘텐츠에는 보통 새 URI(들)로의 하이퍼링크가 있는 짧은 하이퍼텍스트 안내가 들어 있다.
308 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
참고: 이 상태 코드는 형제 코드들보다 훨씬 나중(2014년 6월)에 생겼으므로 모든 곳에서 인식되지는 않을 수 있다. 배포 고려 사항은 [RFC7538]의 4장을 참고한다.
4xx (Client Error) 클래스의 상태 코드는 클라이언트가 잘못한 것으로 보임을 나타낸다. HEAD 요청에 응답하는 경우를 제외하면, 서버는 오류 상황에 대한 설명과 그것이 일시적인 상태인지 영구적인 상태인지를 담은 표현을 보내야 한다(SHOULD). 이 상태 코드들은 어떤 요청 메서드에도 적용할 수 있다. 사용자 에이전트는 포함된 표현을 사용자에게 표시해야 한다(SHOULD).
400 (Bad Request) 상태 코드는 클라이언트 오류로 인식되는 무언가(예: 형식이 잘못된 요청 구문, 유효하지 않은 요청 메시지 프레이밍, 기만적인 요청 라우팅) 때문에 서버가 요청을 처리할 수 없거나 처리하지 않겠다는 것을 나타낸다.
401 (Unauthorized) 상태 코드는 대상 리소스에 대한 유효한 인증 자격 증명이 없어 요청이 적용되지 않았음을 나타낸다. 401 응답을 생성하는 서버는 대상 리소스에 적용할 수 있는 챌린지를 적어도 하나 담은 WWW-Authenticate 헤더 필드(11.6.1절)를 반드시 보내야 한다(MUST).
요청에 인증 자격 증명이 들어 있었다면, 401 응답은 그 자격 증명에 대한 인가가 거부되었음을 나타낸다. 사용자 에이전트는 새로운 또는 교체된 Authorization 헤더 필드(11.6.2절)로 요청을 반복할 수 있다(MAY). 401 응답이 이전 응답과 같은 챌린지를 담고 있고 사용자 에이전트가 이미 적어도 한 번 인증을 시도했다면, 동봉된 표현에는 보통 관련 진단 정보가 들어 있으므로 사용자 에이전트는 그것을 사용자에게 제시해야 한다(SHOULD).
역주: 15.5.2절의 “has not been applied”를 “processed”로 고치자는 정오표 7530은 기각(Rejected)되어 반영하지 않았다.
402 (Payment Required) 상태 코드는 앞으로의 사용을 위해 예약되어 있다.
403 (Forbidden) 상태 코드는 서버가 요청을 이해했지만 이행을 거부함을 나타낸다. 요청이 금지된 이유를 공개하고 싶은 서버는 그 이유를 응답 콘텐츠(있다면)에 설명할 수 있다.
요청에 인증 자격 증명이 제공되었다면, 서버는 그것이 접근을 허락하기에 충분하지 않다고 판단한 것이다. 클라이언트는 같은 자격 증명으로 요청을 자동으로 반복하지 않아야 한다(SHOULD NOT). 클라이언트는 새로운 또는 다른 자격 증명으로 요청을 반복할 수 있다(MAY). 그러나 요청은 자격 증명과 무관한 이유로 금지될 수도 있다.
금지된 대상 리소스가 현재 존재한다는 사실을 “숨기고” 싶은 원본 서버는 대신 404 (Not Found) 상태 코드로 응답할 수 있다(MAY).
404 (Not Found) 상태 코드는 원본 서버가 대상 리소스의 현재 표현을 찾지 못했거나, 그런 표현이 존재한다는 사실을 공개할 의향이 없음을 나타낸다. 404 상태 코드는 이 표현 부재가 일시적인지 영구적인지 나타내지 않는다. 원본 서버가 (아마도 설정 가능한 어떤 수단을 통해) 그 상태가 영구적일 가능성이 크다는 것을 안다면 404보다 410 (Gone) 상태 코드가 선호된다.
404 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
405 (Method Not Allowed) 상태 코드는 요청 줄에서 받은 메서드를 원본 서버는 알지만 대상 리소스가 지원하지 않음을 나타낸다. 원본 서버는 대상 리소스가 현재 지원하는 메서드 목록을 담은 Allow 헤더 필드를 405 응답에 반드시 생성해야 한다(MUST).
405 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
406 (Not Acceptable) 상태 코드는 요청에서 받은 사전 협상 헤더 필드(12.1절)에 따르면 대상 리소스에 사용자 에이전트가 받아들일 수 있는 현재 표현이 없고, 서버가 기본 표현을 제공할 의향도 없음을 나타낸다.
서버는 사용자나 사용자 에이전트가 가장 적절한 것을 고를 수 있도록, 사용할 수 있는 표현 특성과 그에 해당하는 리소스 식별자의 목록을 담은 콘텐츠를 생성해야 한다(SHOULD). 사용자 에이전트는 그 목록에서 가장 적절한 선택지를 자동으로 고를 수 있다(MAY). 그러나 15.4.1절에서 설명하듯, 이 명세는 그런 자동 선택을 위한 표준을 정의하지 않는다.
407 (Proxy Authentication Required) 상태 코드는 401 (Unauthorized)와 비슷하지만, 이 요청에 프록시를 쓰려면 클라이언트가 자신을 인증해야 함을 나타낸다. 프록시는 그 요청에 대해 그 프록시에 적용할 수 있는 챌린지를 담은 Proxy-Authenticate 헤더 필드(11.7.1절)를 반드시 보내야 한다(MUST). 클라이언트는 새로운 또는 교체된 Proxy-Authorization 헤더 필드(11.7.2절)로 요청을 반복할 수 있다(MAY).
408 (Request Timeout) 상태 코드는 서버가 기다릴 준비가 된 시간 안에 완전한 요청 메시지를 받지 못했음을 나타낸다.
클라이언트에 전송 중인 미처리 요청이 있다면 그 요청을 반복할 수 있다(MAY). 현재 연결을 쓸 수 없다면(예: HTTP/1.1에서 요청 구분이 사라진 경우처럼), 새 연결을 쓴다.
409 (Conflict) 상태 코드는 대상 리소스의 현재 상태와 충돌하여 요청을 완료할 수 없었음을 나타낸다. 이 코드는 사용자가 충돌을 해결하고 요청을 다시 제출할 수 있을 만한 상황에서 쓰인다. 서버는 사용자가 충돌의 원인을 알아볼 수 있을 만큼의 정보를 담은 콘텐츠를 생성해야 한다(SHOULD).
충돌은 PUT 요청에 대한 응답에서 가장 일어나기 쉽다. 예를 들어 버전 관리를 쓰고 있는데 PUT하는 표현에 이전(제3자) 요청이 한 변경과 충돌하는 리소스 변경이 들어 있다면, 원본 서버는 요청을 완료할 수 없음을 나타내기 위해 409 응답을 쓸 수 있다. 이 경우 응답 표현에는 수정 이력을 바탕으로 차이를 병합하는 데 쓸모 있는 정보가 들어 있을 가능성이 크다.
410 (Gone) 상태 코드는 원본 서버에서 대상 리소스에 더 이상 접근할 수 없으며 이 상태가 영구적일 가능성이 크다는 것을 나타낸다. 원본 서버가 그 상태가 영구적인지 모르거나 판단할 수단이 없다면, 대신 404 (Not Found) 상태 코드를 써야 마땅하다.
410 응답은 주로, 리소스를 의도적으로 사용할 수 없게 했으며 서버 소유자가 그 리소스에 대한 원격 링크가 제거되기를 바란다는 것을 수신자에게 알려 웹 유지 관리 작업을 돕기 위한 것이다. 이런 일은 기간 한정 판촉 서비스나, 더 이상 원본 서버의 사이트와 관련이 없는 개인에게 속한 리소스에서 흔하다. 영구적으로 사용할 수 없는 리소스를 모두 “사라짐(gone)”으로 표시하거나 그 표시를 일정 기간 유지할 필요는 없다. 그것은 서버 소유자의 재량에 맡긴다.
410 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
411 (Length Required) 상태 코드는 서버가 정의된 Content-Length(8.6절) 없이는 요청을 받아들이기를 거부함을 나타낸다. 클라이언트는 요청 콘텐츠의 길이를 담은 유효한 Content-Length 헤더 필드를 추가하면 요청을 반복할 수 있다(MAY).
412 (Precondition Failed) 상태 코드는 요청 헤더 필드에 주어진 하나 이상의 조건이 서버에서 검사했을 때 거짓으로 평가되었음을 나타낸다(13장). 이 응답 상태 코드로 클라이언트는 현재 리소스 상태(현재 표현과 메타데이터)에 전제 조건을 걸어, 대상 리소스가 예상하지 못한 상태일 때 요청 메서드가 적용되지 않게 할 수 있다.
413 (Content Too Large) 상태 코드는 요청 콘텐츠가 서버가 처리할 의향이 있거나 처리할 수 있는 것보다 커서 서버가 요청 처리를 거부하고 있음을 나타낸다. 서버는 사용 중인 프로토콜 버전이 허용한다면 요청을 종료할 수 있다(MAY). 그렇지 않으면 서버는 연결을 닫을 수 있다(MAY).
그 상태가 일시적이라면, 서버는 그것이 일시적이며 클라이언트가 얼마나 지난 뒤에 다시 시도할 수 있는지(MAY) 나타내는 Retry-After 헤더 필드를 생성해야 한다(SHOULD).
414 (URI Too Long) 상태 코드는 대상 URI가 서버가 해석할 의향이 있는 것보다 길어서 서버가 요청 처리를 거부하고 있음을 나타낸다. 이 드문 상태는 클라이언트가 POST 요청을 긴 쿼리 정보를 가진 GET 요청으로 잘못 변환했을 때, 클라이언트가 리다이렉션의 무한 루프에 빠졌을 때(예: 리다이렉트된 URI 접두사가 자기 자신의 접미사를 가리키는 경우), 또는 잠재적인 보안 허점을 악용하려는 클라이언트가 서버를 공격하고 있을 때에만 일어날 가능성이 있다.
414 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
415 (Unsupported Media Type) 상태 코드는 콘텐츠가 대상 리소스에서 이 메서드가 지원하지 않는 형식이어서 원본 서버가 요청 처리를 거부하고 있음을 나타낸다.
형식 문제는 요청이 나타낸 Content-Type이나 Content-Encoding 때문일 수도 있고, 데이터를 직접 살펴본 결과일 수도 있다.
문제가 지원하지 않는 콘텐츠 코딩 때문이었다면, 요청에서 어떤 콘텐츠 코딩(있다면)이 받아들여졌을지 나타내기 위해 Accept-Encoding 응답 헤더 필드(12.5.3절)를 써야 마땅하다.
반면 원인이 지원하지 않는 미디어 유형이었다면, 요청에서 어떤 미디어 유형이 받아들여졌을지 나타내기 위해 Accept 응답 헤더 필드(12.5.1절)를 쓸 수 있다.
416 (Range Not Satisfiable) 상태 코드는 요청된 범위 중 충족 가능한 것이 하나도 없거나, 클라이언트가 작거나 겹치는 범위를 지나치게 많이 요청했기 때문에(잠재적인 서비스 거부 공격) 요청의 Range 헤더 필드(14.2절)에 있는 범위 집합이 거부되었음을 나타낸다.
각 범위 단위는 자신의 범위 집합이 충족 가능하려면 무엇이 필요한지 정의한다. 예를 들어 14.1.2절은 바이트 범위 집합이 충족 가능해지는 조건을 정의한다.
바이트 범위 요청에 416 응답을 생성하는 서버는 선택된 표현의 현재 길이를 지정하는 Content-Range 헤더 필드(14.4절)를 생성해야 한다(SHOULD).
예:
HTTP/1.1 416 Range Not Satisfiable
Date: Fri, 20 Jan 2012 15:41:54 GMT
Content-Range: bytes */47022
참고: 서버는
Range를 자유롭게 무시할 수 있으므로, 많은 구현은 선택된 표현 전체를200 (OK)응답으로 보낸다. 이는 대부분의 클라이언트가 (덜 효율적이긴 하지만) 작업을 마치기 위해200 (OK)를 받을 준비가 되어 있기 때문이기도 하고, 클라이언트가 완전한 표현을 받을 때까지 유효하지 않은 범위 요청을 멈추지 않을 수 있기 때문이기도 하다. 따라서 클라이언트는416 (Range Not Satisfiable)응답이 가장 적절한 상황에서도 그것을 받으리라고 기대할 수 없다.
417 (Expectation Failed) 상태 코드는 요청의 Expect 헤더 필드(10.1.1절)에 주어진 기대를 인바운드 서버 중 적어도 하나가 충족할 수 없었음을 나타낸다.
[RFC2324]는 HTTP가 남용되는 갖가지 방식을 풍자한 만우절 RFC였다. 그런 남용 중 하나가 애플리케이션별 418 상태 코드의 정의였는데, 이 코드는 농담으로 충분히 자주 배포되어 앞으로 어떤 용도로도 쓸 수 없게 되었다.
따라서 418 상태 코드는 IANA HTTP 상태 코드 레지스트리에 예약되어 있다. 이는 현재 그 상태 코드를 다른 애플리케이션에 할당할 수 없음을 나타낸다. 앞으로 사정상 그 사용이 필요해지면(예: 4NN 상태 코드가 고갈되는 경우), 다른 용도로 다시 할당할 수 있다.
421 (Misdirected Request) 상태 코드는 대상 URI에 대해 권한 있는 응답을 생성할 수 없거나 생성할 의향이 없는 서버로 요청이 향했음을 나타낸다. 원본 서버(또는 원본 서버를 대신해 동작하는 게이트웨이)는 서버가 설정된 출처와 일치하지 않거나(4.3.1절) 요청을 받은 연결 문맥과 일치하지 않는(7.4절) 대상 URI를 거부하기 위해 421을 보낸다.
421 (Misdirected Request) 응답을 받은 클라이언트는 요청 메서드가 멱등인지와 상관없이, 대상 리소스의 출처에 특정한 새 연결 같은 다른 연결로, 또는 대체 서비스([ALTSVC])를 통해 요청을 재시도할 수 있다(MAY).
프록시는 421 응답을 생성해서는 안 된다(MUST NOT).
422 (Unprocessable Content) 상태 코드는 서버가 요청 콘텐츠의 콘텐츠 유형을 이해하고(따라서 415 (Unsupported Media Type) 상태 코드는 적절하지 않다) 요청 콘텐츠의 구문도 올바르지만, 담긴 지시를 처리할 수 없었음을 나타낸다. 예를 들어 XML 요청 콘텐츠가 형식은 올바르지만(즉, 구문상 올바르지만) 의미상 잘못된 XML 지시를 담고 있으면 이 상태 코드를 보낼 수 있다.
426 (Upgrade Required) 상태 코드는 서버가 현재 프로토콜로는 요청 수행을 거부하지만, 클라이언트가 다른 프로토콜로 업그레이드한 뒤에는 수행할 의향이 있을 수 있음을 나타낸다. 서버는 필요한 프로토콜(들)을 나타내기 위해 426 응답에 Upgrade 헤더 필드를 반드시 보내야 한다(MUST)(7.8절).
예:
HTTP/1.1 426 Upgrade Required
Upgrade: HTTP/3.0
Connection: Upgrade
Content-Length: 53
Content-Type: text/plain
This service requires use of the HTTP/3.0 protocol.
5xx (Server Error) 클래스의 상태 코드는 서버가 자신이 잘못했거나 요청된 메서드를 수행할 능력이 없다는 것을 알고 있음을 나타낸다. HEAD 요청에 응답하는 경우를 제외하면, 서버는 오류 상황에 대한 설명과 그것이 일시적인 상태인지 영구적인 상태인지를 담은 표현을 보내야 한다(SHOULD). 사용자 에이전트는 포함된 표현을 사용자에게 표시해야 한다(SHOULD). 이 상태 코드들은 어떤 요청 메서드에도 적용할 수 있다.
500 (Internal Server Error) 상태 코드는 서버가 요청을 이행하지 못하게 하는 예상하지 못한 상태를 만났음을 나타낸다.
501 (Not Implemented) 상태 코드는 서버가 요청을 이행하는 데 필요한 기능을 지원하지 않음을 나타낸다. 서버가 요청 메서드를 알지 못하고 어떤 리소스에 대해서도 그것을 지원할 수 없을 때 적절한 응답이다.
501 응답은 휴리스틱 캐시 가능하다. 즉, 메서드 정의나 명시적 캐시 제어가 달리 나타내지 않는 한 그렇다([CACHING]의 4.2.2절 참고).
502 (Bad Gateway) 상태 코드는 서버가 게이트웨이나 프록시로 동작하면서 요청을 이행하려고 접근한 인바운드 서버로부터 유효하지 않은 응답을 받았음을 나타낸다.
503 (Service Unavailable) 상태 코드는 일시적인 과부하나 예정된 유지 보수 때문에 서버가 현재 요청을 처리할 수 없으며, 이는 얼마간의 지연 뒤에 나아질 가능성이 크다는 것을 나타낸다. 서버는 클라이언트가 요청을 재시도하기 전에 기다릴 적절한 시간을 제안하기 위해 Retry-After 헤더 필드(10.2.3절)를 보낼 수 있다(MAY).
참고:
503상태 코드가 있다고 해서 서버가 과부하 상태가 될 때 그것을 꼭 써야 한다는 뜻은 아니다. 어떤 서버는 그냥 연결을 거부할 수도 있다.
504 (Gateway Timeout) 상태 코드는 서버가 게이트웨이나 프록시로 동작하면서, 요청을 완료하기 위해 접근해야 했던 업스트림 서버로부터 제때 응답을 받지 못했음을 나타낸다.
505 (HTTP Version Not Supported) 상태 코드는 서버가 요청 메시지에 쓰인 HTTP의 주 버전을 지원하지 않거나 지원하기를 거부함을 나타낸다. 서버는 2.5절에서 설명하듯, 이 오류 메시지 외에는 클라이언트와 같은 주 버전으로 요청을 완료할 수 없거나 완료할 의향이 없음을 나타내고 있다. 서버는 505 응답에 그 버전이 왜 지원되지 않는지와 그 서버가 어떤 다른 프로토콜을 지원하는지 설명하는 표현을 생성해야 한다(SHOULD).
역주: 15장의 “ought to”(15.1·15.2.1·15.3.1·15.3.3·15.4.2·15.4.3·15.4.4·15.4.8·15.4.9·15.5.11·15.5.16절)와 “need to”(15·15.3.5·15.4·15.5.8절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 15.2.1~15.6.6절의 상태 코드 절 제목은 상태 코드와 이유 구문이라 원어로 두었고, 15.2~15.6절 제목의 클래스 이름은 옮기되 상태 코드는 본문에서
2xx (Successful)처럼 원어로 두었다. 15장 서두에 대한 정오표 8459(상태 코드 범위를 100~999로 넓히자는 제안)는 기각(Rejected)되어 반영하지 않았다.
HTTP는 새 버전을 도입하지 않고도 프로토콜에 기능을 들여올 수 있는 여러 범용 확장 지점(extension point)을 정의한다. 메서드, 상태 코드, 필드 이름, 그리고 인증 스킴과 캐시 지시자 같은 정의된 필드 안의 추가 확장 지점이 여기에 포함된다([CACHING]의 5.2.3절에 있는 Cache-Control 확장 참고). HTTP 의미론은 버전이 매겨지지 않으므로 이 확장 지점들은 지속적이다. 사용 중인 프로토콜 버전은 그 의미에 영향을 주지 않는다.
버전에 독립적인 확장은 사용 중인 특정 프로토콜 버전에 의존하거나 그것과 상호 작용하지 않는 것이 좋다. 그것을 피할 수 없을 때는 확장이 여러 버전에 걸쳐 어떻게 상호 운용될 수 있는지 신중하게 고려해야 한다.
또한 HTTP의 특정 버전에는 HTTP/1.1의 전송 코딩([HTTP/1.1]의 6.1절)이나 HTTP/2의 SETTINGS 또는 프레임 유형([HTTP/2])처럼 그 버전만의 확장 지점이 있을 수 있다. 이 확장 지점들은 그것이 나타나는 프로토콜 버전에 특정하다.
버전에 특정한 확장은, 그 프로토콜 요소가 명시적으로 허용하지 않는 한, 버전에 독립적인 메커니즘이나 확장 지점(메서드나 헤더 필드 같은 것)의 의미를 덮어쓰거나 수정할 수 없다. 예를 들어 CONNECT 메서드(9.3.6절)는 이를 허용한다.
이 지침들은 경로의 일부가 서로 다른 HTTP 버전을 구현하더라도 프로토콜이 올바르고 예측 가능하게 동작하도록 보장한다.
IANA가 https://www.iana.org/assignments/http-methods에서 관리하는 “HTTP 메서드 레지스트리”는 메서드 이름을 등록한다.
HTTP 메서드 등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
이 네임스페이스에 값을 추가하려면 IETF Review가 필요하다([RFC8126]의 4.8절 참고).
표준화된 메서드는 범용이다. 즉, 특정한 미디어 유형, 리소스 종류, 애플리케이션 하나에만이 아니라 잠재적으로 어떤 리소스에도 적용할 수 있다. 그러므로 직교하는 기술은 직교하는 명세를 가져야 마땅하다는 점에서, 새 메서드는 단일 애플리케이션이나 데이터 형식에 특정하지 않은 문서에 등록하는 것이 선호된다.
메시지 파싱(6장)은 (HEAD에 대한 응답을 제외하고) 메서드 의미와 독립적이어야 하므로, 새 메서드의 정의는 파싱 알고리즘을 바꾸거나 요청 메시지나 응답 메시지에 콘텐츠가 있는 것을 금지할 수 없다. 새 메서드의 정의는 값이 “0”인 Content-Length 헤더 필드를 요구하여 길이가 0인 콘텐츠만 허용된다고 명시할 수는 있다.
마찬가지로 새 메서드는 CONNECT와 OPTIONS에 각각 허용되는 특별한 host:port 형식과 별표 형식의 요청 대상을 쓸 수 없다(7.1절). 대상 URI에는 절대 형식의 완전한 URI가 필요하다. 즉, 요청 대상을 절대 형식으로 보내거나, 다른 메서드와 같은 방식으로 요청 문맥에서 대상 URI를 재구성해야 한다.
새 메서드 정의는 그 메서드가 안전한지(9.2.1절), 멱등인지(9.2.2절), 캐시 가능한지(9.2.3절), 요청 콘텐츠(있다면)에 어떤 의미가 연관되는지, 그리고 메서드가 헤더 필드나 상태 코드 의미를 어떻게 세분하는지 나타내야 한다. 새 메서드가 캐시 가능하다면, 그 정의는 캐시가 어떻게, 어떤 조건에서 응답을 저장하고 이후 요청을 충족하는 데 쓸 수 있는지 설명해야 마땅하다. 새 메서드는 조건부로 만들 수 있는지(13.1절), 그렇다면 조건이 거짓일 때 서버가 어떻게 응답하는지 설명해야 마땅하다. 마찬가지로 새 메서드가 부분 응답 의미(14.2절)를 쓸 수 있다면 그것도 문서화해야 마땅하다.
참고: “M-“로 시작하는 메서드 이름은 정의하지 않도록 한다. 그 접두사는 [RFC2774]가 부여한 의미를 가진 것으로 잘못 해석될 수 있기 때문이다.
IANA가 https://www.iana.org/assignments/http-status-codes에서 관리하는 “HTTP 상태 코드 레지스트리”는 상태 코드 번호를 등록한다.
등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
HTTP 상태 코드 네임스페이스에 값을 추가하려면 IETF Review가 필요하다([RFC8126]의 4.8절 참고).
현재 상태 코드로 정의되지 않은 응답 의미를 표현해야 할 때는 새 상태 코드를 등록할 수 있다. 상태 코드는 범용이다. 즉, 특정한 미디어 유형, 리소스 종류, HTTP 애플리케이션 하나에만이 아니라 잠재적으로 어떤 리소스에도 적용할 수 있다. 그러므로 새 상태 코드는 단일 애플리케이션에 특정하지 않은 문서에 등록하는 것이 선호된다.
새 상태 코드는 15장에 정의된 범주 중 하나에 속해야 한다. 기존 파서가 응답 메시지를 처리할 수 있도록, 새 상태 코드는 콘텐츠를 금지할 수 없다. 다만 길이가 0인 콘텐츠를 의무화할 수는 있다.
아직 널리 배포되지 않은 새 상태 코드의 제안은, 등록될 것이라는 분명한 합의가 있을 때까지 그 코드에 특정 번호를 할당하는 것을 피해야 마땅하다. 대신 초기 초안은 “4NN”이나 “3N0” .. “3N9” 같은 표기를 써서, 번호를 미리 소모하지 않고 제안된 상태 코드(들)의 클래스를 나타낼 수 있다.
새 상태 코드의 정의는 그 상태 코드를 담은 응답을 일으키는 요청 조건(예: 요청 헤더 필드와 메서드의 조합)과, 응답 헤더 필드에 대한 의존성(예: 어떤 필드가 필요한지, 어떤 필드가 의미를 수정할 수 있는지, 새 상태 코드와 함께 쓰일 때 어떤 필드 의미가 더 세분되는지)을 설명해야 마땅하다.
기본적으로 상태 코드는 그것이 나타나는 응답에 해당하는 요청에만 적용된다. 상태 코드가 더 넓은 적용 범위(예: 해당 리소스에 대한 모든 요청이나 서버에 대한 모든 요청)에 적용된다면, 이를 명시적으로 밝혀야 한다. 그렇게 할 때는 모든 클라이언트가 새 상태 코드를 이해하지는 못할 수 있으므로 더 넓은 범위를 일관되게 적용하리라고 기대할 수 없다는 점에 유의해야 한다.
새 최종 상태 코드의 정의는 그것이 휴리스틱 캐시 가능한지 명시해야 마땅하다. 최종 상태 코드를 가진 응답은 명시적 신선도 정보가 있으면 무엇이든 캐시될 수 있다는 점에 유의한다. 휴리스틱 캐시 가능으로 정의된 상태 코드는 명시적 신선도 정보 없이도 캐시될 수 있다. 마찬가지로 상태 코드의 정의는 must-understand 캐시 지시자가 쓰이는 경우 캐시 동작에 제약을 둘 수 있다. 더 자세한 내용은 [CACHING]을 참고한다.
마지막으로 새 상태 코드의 정의는 콘텐츠가 식별된 리소스와 암묵적인 연관을 가지는지(6.4.2절) 나타내야 마땅하다.
HTTP에서 가장 널리 쓰이는 확장 지점은 새 헤더 필드와 트레일러 필드의 정의다.
새 필드는 수신자가 이해할 때 이전에 정의된 필드의 해석을 대체하거나 보강하거나, 요청 평가에 전제 조건을 정의하거나, 응답의 의미를 세분하도록 정의할 수 있다.
그러나 필드를 정의한다고 해서 그 필드의 배포나 수신자의 인식이 보장되지는 않는다. 대부분의 필드는 수신자가 인식하지 못하는 필드를 안전하게 무시할 수 있다(그러나 다운스트림으로는 전달한다)는 기대를 바탕으로 설계된다. 다른 경우에는 송신자가 어떤 필드를 이해할 능력이 있는지가 이전 통신, 아마도 이전 메시지에서 보낸 프로토콜 버전이나 필드, 또는 특정 미디어 유형의 사용으로 나타날 수 있다. 마찬가지로 도입하는 필드와 함께 그런 검사 방법이 정의되어 있다면, OPTIONS 요청이나 정의된 well-known URI([RFC8615])와의 상호 작용을 통해 지원 여부를 직접 검사할 수도 있다.
“HTTP 필드 이름 레지스트리”는 HTTP 필드 이름의 네임스페이스를 정의한다.
누구든 HTTP 필드의 등록을 요청할 수 있다. 새 HTTP 필드를 만들 때 고려할 사항은 16.3.2절을 참고한다.
“HTTP 필드 이름 레지스트리”는 https://www.iana.org/assignments/http-fields/에 있다. 등록 요청은 그곳의 안내를 따르거나 “ietf-http-wg@w3.org” 메일링 리스트로 이메일을 보내 할 수 있다.
필드 이름은 (IESG나 그 위임자가 임명한) 지정 전문가(designated expert)의 조언에 따라 등록된다. 상태가 ‘permanent’인 필드는 Specification Required다([RFC8126]의 4.6절).
등록 요청은 다음 정보로 이루어진다.
필드 이름(Field name): 요청하는 필드 이름. 5.1절에 정의된 field-name 구문을 반드시 따라야 하며(MUST), 첫 문자가 영문자이고 영문자, 숫자, 하이픈(‘-‘) 문자로만 제한되어야 한다(SHOULD).
상태(Status): “permanent”, “provisional”, “deprecated”, “obsoleted” 중 하나.
명세 문서(Specification document(s)): 필드를 명시하는 문서에 대한 참조. 문서 사본을 가져오는 데 쓸 수 있는 URI를 포함하는 것이 바람직하다. 선택 사항이지만 provisional 등록에는 권장된다. 관련 절을 함께 표시할 수도 있지만 필수는 아니다.
그리고 선택적으로:
전문가는 커뮤니티와 협의하여 레지스트리에 수집할 추가 항목을 정의할 수 있다.
표준이 정의한 이름의 상태는 “permanent”다. 다른 이름도 전문가가 커뮤니티와 협의하여 그 이름이 쓰이고 있다고 판단하면 permanent로 등록될 수 있다. 그 밖의 이름은 “provisional”로 등록해야 한다.
provisional 항목은 전문가가 커뮤니티와 협의하여 쓰이지 않는다고 판단하면 전문가가 제거할 수 있다. 전문가는 언제든지 provisional 항목의 상태를 permanent로 바꿀 수 있다.
등록되지 않은 이름이 널리 배포되어 있고 그대로 두면 제때 등록될 가능성이 낮다고 전문가가 판단하면, 제3자(전문가 포함)가 그 이름을 등록할 수 있다는 점에 유의한다.
HTTP 헤더 필드와 트레일러 필드는 프로토콜에서 널리 쓰이는 확장 지점이다. 필드는 임시방편으로 쓸 수도 있지만, 더 널리 쓰려는 필드는 상호 운용성을 보장하기 위해 신중하게 문서화해야 한다.
특히 새 필드를 정의하는 명세의 저자는 다음 측면을 고려하고, 적절하다면 문서화하는 것이 좋다.
Connection 헤더 필드에 필드 이름을 나열하는 것이 적절하거나 심지어 필수인지(즉, 필드가 홉 단위라면, 7.6.1절 참고).요청 헤더 필드에는 기본 동작이 적절하지 않을 때 문서화해야 하는 추가 고려 사항이 있다.
Vary 응답 헤더 필드에 필드 이름을 나열하는 것이 적절한지(예: 원본 서버의 콘텐츠 선택 알고리즘이 그 요청 헤더 필드를 쓰는 경우, 12.5.5절 참고).PUT 요청에서 받았을 때 필드를 저장하도록 의도되었는지(9.3.4절 참고).새 필드를 정의하는 명세의 저자는 짧으면서도 설명적인 필드 이름을 고르는 것이 좋다. 짧은 이름은 불필요한 데이터 전송을 피하고, 설명적인 이름은 혼동과, 더 넓은 용도가 있을 수 있는 이름의 “선점(squatting)”을 피한다.
이를 위해 (단일 애플리케이션이나 사용 사례에 한정된 헤더처럼) 용도가 제한된 필드는 그 용도(또는 그 약어)를 접두사로 포함하는 이름을 쓰는 것이 좋다. 예를 들어 Foo 애플리케이션에 Description 필드가 필요하다면 “Foo-Desc”를 쓸 수 있다. “Description”은 너무 일반적이고, “Foo-Description”은 쓸데없이 길다.
field-name 구문은 어떤 토큰 문자든 허용하도록 정의되어 있지만, 실제로 일부 구현은 field-name에서 받아들이는 문자를 제한한다. 상호 운용되려면 새 필드 이름은 영숫자 문자, “-“, “.”로 제한해야 하고(SHOULD), 영문자로 시작해야 한다(SHOULD). 예를 들어 밑줄(“_”) 문자는 HTTP가 아닌 게이트웨이 인터페이스를 거칠 때 문제가 될 수 있다(17.10절 참고).
필드 이름에는 “X-“ 접두사를 붙이지 않아야 마땅하다. 더 자세한 내용은 [BCP178]을 참고한다.
HTTP 필드 이름에는 다른 접두사가 쓰이기도 한다. 예를 들어 “Accept-“는 많은 콘텐츠 협상 헤더에 쓰이고, “Content-“는 6.4절에서 설명하듯 쓰인다. 이 접두사들은 필드의 목적을 알아보는 데 도움을 줄 뿐이며 자동 처리를 일으키지 않는다.
새 HTTP 필드를 정의할 때 주요한 작업은 필드 값 구문의 명세, 즉 송신자가 무엇을 생성해야 하고 수신자가 받은 것에서 의미를 어떻게 추론해야 하는지다.
저자는 새 필드 값의 구문을 정의할 때 이 명세의 ABNF 규칙이나 [RFC8941]의 규칙을 쓰는 것이 좋다(필수는 아니다).
저자는 여러 필드 줄의 결합이 필드 값에 어떤 영향을 줄지(5.3절 참고) 신중하게 고려하는 것이 좋다. 송신자가 실수로 여러 값을 보낼 수 있고 중개자와 HTTP 라이브러리 모두 자동으로 결합을 수행할 수 있으므로, 이는 단일 값만 예상되는 경우에도 모든 필드 값에 적용된다.
따라서 저자는 쉼표를 담은 값을 구분하거나 인코딩하는 것이 좋다(예: 5.6.4절의 quoted-string 규칙, [RFC8941]의 String 데이터 유형, 또는 필드별 인코딩으로). 그러면 필드 데이터 안의 쉼표가 목록 값을 구분하는 쉼표와 혼동되지 않는다.
예를 들어 Content-Type 필드 값은 따옴표 문자열 안에서만 쉼표를 허용하므로, 여러 값이 있어도 안정적으로 파싱할 수 있다. Location 필드 값은 따라 하지 말아야 할 반례다. URI에 쉼표가 들어갈 수 있으므로, 쉼표를 포함한 단일 값과 두 값을 안정적으로 구별할 수 없다.
단일 값 필드(5.5절 참고)의 저자는 여러 구성원이 있는 메시지를 어떻게 다룰지도 문서화하는 것이 좋다(필드를 무시하는 것이 합리적인 기본값이겠지만, 그것이 언제나 옳은 선택은 아닐 수 있다).
“HTTP 인증 스킴 레지스트리”는 챌린지와 자격 증명에서 쓰는 인증 스킴의 네임스페이스를 정의한다. 이 레지스트리는 https://www.iana.org/assignments/http-authschemes에서 관리된다.
등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
이 네임스페이스에 값을 추가하려면 IETF Review가 필요하다([RFC8126]의 4.8절 참고).
HTTP 인증 프레임워크에는 새 인증 스킴이 동작하는 방식에 제약을 두는 몇 가지 측면이 있다.
auth-param 구문을 써야 마땅하다. 그렇지 않으면 앞으로의 확장이 불가능해지기 때문이다.챌린지와 자격 증명의 파싱은 이 명세가 정의하며 새 인증 스킴이 수정할 수 없다. auth-param 구문을 쓸 때 모든 매개 변수는 token과 quoted-string 구문을 모두 지원해야 마땅하고, 구문 제약은 파싱(즉, quoted-string 처리) 뒤의 필드 값에 대해 정의해야 마땅하다. 그래야 수신자가 모든 인증 스킴에 적용되는 범용 파서를 쓸 수 있다.
참고: “realm” 매개 변수의 값 구문을
quoted-string으로 제한한 것은 새 매개 변수에서 되풀이하지 말아야 할 나쁜 설계 선택이었다.
WWW-Authenticate 사용)에 쓸 수 있는지, 프록시 인증(즉, Proxy-Authenticate 사용)에 쓸 수 있는지, 또는 둘 다인지 문서화해야 한다.Authorization 헤더 필드로 전달되는 자격 증명은 사용자 에이전트에 특정하므로, 그것이 나타나는 요청의 범위 안에서 HTTP 캐시에 “private” 캐시 응답 지시자([CACHING]의 5.2.2.7절)와 같은 효과를 낸다.
따라서 Authorization 헤더 필드로 자격 증명을 전달하지 않기로 한 새 인증 스킴(예: 새로 정의한 헤더 필드를 쓰는 경우)은 캐시 응답 지시자(예: “private”)의 사용을 의무화하여 캐싱을 명시적으로 금지해야 할 것이다.
Authentication-Info, Proxy-Authentication-Info, 또는 그 밖의 인증 관련 응답 헤더 필드를 쓰는 스킴은 관련 보안 고려 사항을 고려하고 문서화해야 한다(17.16.4절 참고).“HTTP 범위 단위 레지스트리”는 범위 단위 이름의 네임스페이스를 정의하고 그에 해당하는 명세를 가리킨다. 이 레지스트리는 https://www.iana.org/assignments/http-parameters에서 관리된다.
HTTP 범위 단위 등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
이 네임스페이스에 값을 추가하려면 IETF Review가 필요하다([RFC8126]의 4.8절 참고).
페이지, 절, 레코드, 행, 시간 같은 형식별 경계 등 다른 범위 단위도 애플리케이션별 목적으로 HTTP에서 쓸 수는 있지만, 실제로 흔히 쓰이지는 않는다. 대체 범위 단위의 구현자는 그 단위가 콘텐츠 코딩 및 범용 중개자와 어떻게 함께 동작할지 고려해야 마땅하다.
IANA가 https://www.iana.org/assignments/http-parameters/에서 관리하는 “HTTP 콘텐츠 코딩 레지스트리”는 콘텐츠 코딩 이름을 등록한다.
콘텐츠 코딩 등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
콘텐츠 코딩의 이름은, 인코딩 변환이 동일한 경우(8.4.1절에 정의된 압축 코딩이 그렇다)가 아니라면, (https://www.iana.org/assignments/http-parameters/에 있는 “HTTP 전송 코딩 레지스트리(HTTP Transfer Coding Registry)”에 따른) 전송 코딩의 이름과 겹쳐서는 안 된다(MUST NOT).
이 네임스페이스에 값을 추가하려면 IETF Review가 필요하며([RFC8126]의 4.8절 참고), 8.4.1절에 정의된 콘텐츠 코딩의 목적에 반드시 부합해야 한다(MUST).
새 콘텐츠 코딩은 가능하면 자기 서술적이어야 마땅하다. 즉, 전송 중에 잃어버릴 수 있는 외부 메타데이터에 의존하기보다, 선택적 매개 변수를 코딩 형식 자체 안에서 알아낼 수 있어야 마땅하다.
“HTTP 업그레이드 토큰 레지스트리(Hypertext Transfer Protocol (HTTP) Upgrade Token Registry)”는 Upgrade 헤더 필드에서 프로토콜을 식별하는 데 쓰는 프로토콜 이름 토큰의 네임스페이스를 정의한다. 이 레지스트리는 https://www.iana.org/assignments/http-upgrade-tokens에서 관리된다.
등록된 프로토콜 이름은 각각 연락처 정보, 그리고 연결이 업그레이드된 뒤 어떻게 처리될지 자세히 기술하는 선택적인 명세 집합과 연관된다.
등록은 “First Come First Served” 방식으로 이루어지며([RFC8126]의 4.4절 참고), 다음 규칙을 따른다.
역주: 16장의 소문자 “must”(16.2.2절 “must be explicitly specified”), “should”(16.2.2절 “should be noted”, 16.3.1절 “should be registered”, 16.3.2.2절 “should generate”, “should not be emulated”), “ought to”·”need to”·”are advised to”·”are encouraged to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. IANA 등록 정책 이름(IETF Review, Specification Required, First Come First Served)과 등록 상태 값(permanent, provisional, deprecated, obsoleted)은 원어로 두었다. 레지스트리 이름과 등록 항목 이름은 번역하고 처음 나올 때 원어를 병기했다.
이 절은 개발자, 정보 제공자, 사용자에게 HTTP 의미론과 인터넷으로 정보를 전송하는 데 HTTP를 쓰는 것과 관련해 알려진 보안 문제를 알리기 위한 것이다. 캐싱과 관련된 고려 사항은 [CACHING]의 7장에서, HTTP/1.1 메시지 구문 및 파싱과 관련된 고려 사항은 [HTTP/1.1]의 11장에서 다룬다.
아래 고려 사항 목록은 완전하지 않다. HTTP 의미론과 관련된 보안 문제는 대부분 프로토콜의 보안보다는, 서버 측 애플리케이션(HTTP 인터페이스 뒤의 코드)의 보안, HTTP로 받은 콘텐츠를 사용자 에이전트가 처리하는 과정의 보안, 또는 인터넷의 안전한 사용 전반에 관한 것이다. HTTP 동작의 기본인 URI에 대한 보안 고려 사항은 [URI]의 7장에서 다룬다. 여러 기관이 웹 애플리케이션 보안에 관한 주제별 정보와 최신 연구로의 링크를 관리한다(예: [OWASP]).
HTTP는 “권한 있는 응답(authoritative response)”이라는 개념에 의존한다. 이는 대상 URI 안에서 식별된 원본 서버가(또는 그 지시에 따라), 응답 메시지를 만들 때의 대상 리소스 상태를 고려하여 그 요청에 가장 적절한 응답이라고 판단한 응답이다.
authority 구성 요소에 등록된 이름이 쓰이면, “http” URI 스킴(4.2.1절)은 권한 있는 응답을 어디서 찾을 수 있는지 판단하기 위해 사용자의 로컬 이름 해석 서비스에 의존한다. 이는 사용자의 네트워크 호스트 테이블, 캐시된 이름, 이름 해석 라이브러리에 대한 어떤 공격이든 “http” URI의 권한 확립을 공격하는 경로가 된다는 뜻이다. 마찬가지로 사용자가 고른 도메인 이름 서비스(DNS) 서버와, 그 서버가 해석 결과를 얻는 서버 계층은 주소 매핑의 진정성에 영향을 줄 수 있다. DNS 보안 확장(DNSSEC, [RFC4033])은 진정성을 높이는 한 가지 방법이며, 더 안전한 전송 프로토콜로 DNS 요청을 하는 여러 메커니즘도 마찬가지다.
또한 IP 주소를 얻은 뒤에도 “http” URI의 권한 확립은 인터넷 프로토콜 라우팅에 대한 공격에 취약하다.
“https” 스킴(4.2.2절)은 협상된 연결이 보안 처리되어 있고 클라이언트가 통신하는 서버의 신원이 대상 URI의 authority 구성 요소와 일치하는지 제대로 검증한다면(4.3.4절), 권한 확립에 대한 이런 잠재적 공격의 상당수를 막도록(또는 적어도 드러내도록) 의도되었다. 그런 검증을 올바르게 구현하기는 어려울 수 있다([Georgiev] 참고).
주어진 원본 서버에 대한 권한은 프로토콜 확장을 통해 위임될 수 있다. 예: [ALTSVC]. 마찬가지로 연결이 권한 있는 것으로 여겨지는 서버 집합은 [RFC8336] 같은 프로토콜 확장으로 바뀔 수 있다.
공유 프록시 캐시처럼 권한 없는 원천에서 응답을 제공하는 것은 성능과 가용성을 높이는 데 쓸모 있는 경우가 많지만, 그 원천을 신뢰할 수 있거나 신뢰하지 않는 응답을 안전하게 쓸 수 있는 범위 안에서만 그렇다.
안타깝게도 사용자에게 권한을 전달하기는 어려울 수 있다. 예를 들어 “피싱(phishing)”은 사용자의 권한 인식에 대한 공격으로, 하이퍼텍스트에서 비슷한 브랜딩을 보여 주어, 경우에 따라서는 authority 구성 요소를 가리는 userinfo의 도움을 받아 그 인식을 오도할 수 있다(4.2.1절 참고). 사용자 에이전트는 사용자가 동작을 하기 전에 대상 URI를 쉽게 살펴볼 수 있게 하고, userinfo가 있으면 눈에 띄게 구별하거나(또는 거부하고), 참조하는 문서가 알 수 없거나 신뢰할 수 없는 곳에서 왔을 때 저장된 자격 증명과 쿠키를 보내지 않음으로써 피싱 공격의 영향을 줄일 수 있다.
HTTP 중개자는 본질적으로 경로상 공격이 가능한 위치에 있다. 중개자가 실행되는 시스템이 침해되면 심각한 보안 및 개인 정보 보호 문제가 생길 수 있다. 중개자는 보안 관련 정보, 개별 사용자와 조직에 관한 개인 정보, 사용자와 콘텐츠 제공자에게 속한 독점 정보에 접근할 수 있다. 침해된 중개자, 또는 보안과 개인 정보 보호 고려 사항을 무시하고 구현하거나 설정한 중개자는 광범위한 잠재적 공격에 쓰일 수 있다.
공유 캐시를 가진 중개자는 [CACHING]의 7장에서 설명하는 캐시 중독 공격에 특히 취약하다.
구현자는 자신의 설계와 코딩 결정, 그리고 운영자에게 제공하는 설정 옵션(특히 기본 설정)이 개인 정보 보호와 보안에 미치는 영향을 고려해야 한다.
중개자는 자신을 운영하는 사람과 운영 정책보다 더 믿을 만하지는 않다. HTTP는 이 문제를 해결할 수 없다.
원본 서버는 대상 URI에서 리소스 표현으로의 매핑을 관리하기 위해 로컬 파일 시스템을 쓰는 경우가 많다. 대부분의 파일 시스템은 악의적인 파일 이름이나 경로 이름을 막도록 설계되지 않았다. 따라서 원본 서버는 대상 리소스를 파일, 폴더, 디렉터리에 매핑할 때 시스템에 특별한 의미가 있는 이름에 접근하지 않도록 해야 한다.
예를 들어 UNIX, Microsoft Windows 등의 운영 체제는 현재 디렉터리의 한 단계 위를 나타내는 경로 구성 요소로 “..”를 쓰고, 시스템 장치로 데이터를 보내기 위해 특별한 이름의 경로나 파일 이름을 쓴다. 다른 유형의 저장 시스템에도 비슷한 이름 규칙이 있을 수 있다. 마찬가지로 로컬 저장 시스템은 유효하지 않거나 예상하지 못한 문자의 처리, 분해된 문자의 재조합, 대소문자를 구분하지 않는 이름의 대소문자 정규화에서 보안보다 사용자 편의를 우선하는 성가신 경향이 있다.
이런 특별한 이름에 기반한 공격은 서비스 거부(예: 서버에 COM 포트에서 읽으라고 지시하기)나, 제공할 의도가 없는 설정 파일과 소스 파일의 노출에 초점을 맞추는 경향이 있다.
원본 서버는 시스템 서비스를 식별하거나, 데이터베이스 항목을 선택하거나, 데이터 원천을 고르는 수단으로 URI 안의 매개 변수를 쓰는 경우가 많다. 그러나 요청으로 받은 데이터는 신뢰할 수 없다. 공격자는 요청 데이터 요소(메서드, 대상 URI, 헤더 필드, 콘텐츠) 중 어느 것이든, 명령 호출, 언어 인터프리터, 데이터베이스 인터페이스를 거칠 때 명령, 코드, 쿼리로 잘못 해석될 수 있는 데이터를 담도록 구성할 수 있다.
예를 들어 SQL 인젝션은 대상 URI나 헤더 필드(예: Host, Referer 등)의 어느 부분에 추가 쿼리 언어를 끼워 넣는 흔한 공격이다. 받은 데이터를 SELECT 문 안에서 그대로 쓰면, 쿼리 언어가 단순한 문자열 값이 아니라 데이터베이스 명령으로 해석될 수 있다. 이런 유형의 구현 취약점은 막기 쉬운데도 매우 흔하다.
일반적으로 리소스 구현은 지시로 처리되거나 해석되는 문맥에서 요청 데이터를 쓰지 않아야 마땅하다. 매개 변수는 신뢰할 수 없는 데이터에 대비하지 않은 인터페이스로 넘기기보다, 고정된 문자열과 비교하고 그 비교 결과에 따라 처리해야 마땅하다. 고정된 매개 변수에 기반하지 않은 받은 데이터는 잘못 해석되지 않도록 신중하게 필터링하거나 인코딩해야 마땅하다.
요청 데이터를 저장해 두었다가 나중에 처리할 때, 예를 들어 로그 파일이나 모니터링 도구 안에서, 또는 내장 스크립트를 허용하는 데이터 형식 안에 포함될 때도 비슷한 고려 사항이 적용된다.
HTTP는 대부분 텍스트이고 문자로 구분되는 필드를 쓰므로, 파서는 특히 구현이 미리 정해진 길이가 없는 프로토콜 요소를 기대하는 곳에서(2.3절) 매우 긴(또는 매우 느린) 데이터 스트림을 보내는 공격에 취약한 경우가 많다.
상호 운용성을 높이기 위해, 필드의 최소 크기 제한에 대한 구체적인 권고가 있다(5.4절). 이는 자원이 제한된 구현도 지원할 수 있도록 고른 최소 권고이며, 대부분의 구현은 훨씬 높은 제한을 고를 것으로 예상된다.
서버는 대상 URI가 너무 길거나(15.5.15절) 요청 콘텐츠가 너무 큰(15.5.14절) 메시지를 거부할 수 있다. 용량 제한과 관련된 추가 상태 코드는 HTTP 확장으로 정의되어 있다([RFC6585]).
수신자는 요청 메서드, 응답 상태 구문, 필드 이름, 숫자 값, 청크 길이를 포함하되 그에 한정되지 않는 다른 프로토콜 요소를 어디까지 처리할지 신중하게 제한해야 마땅하다. 그런 처리를 제한하지 않으면 버퍼 오버플로나 산술 오버플로로 인한 임의 코드 실행으로 이어지거나, 서비스 거부 공격에 더 취약해질 수 있다.
암호화된 프로토콜에 대한 일부 공격은 동적 압축이 만드는 크기 차이를 이용해 기밀 정보를 드러낸다. 예: [BREACH]. 이 공격들은 공격자가 제어하는 콘텐츠와 기밀 정보 사이에 중복을 만들어, 두 콘텐츠에 같은 사전을 쓰는 동적 압축 알고리즘이 공격자가 제어하는 콘텐츠가 기밀 콘텐츠의 일부와 일치할 때 더 효율적으로 압축하게 하는 데 의존한다.
HTTP 메시지는 TLS 압축, 콘텐츠 코딩, 전송 코딩, 그 밖의 확장이나 버전별 메커니즘 등 여러 방식으로 압축될 수 있다.
이 위험에 대한 가장 효과적인 완화책은 민감한 데이터에 대해 압축을 끄거나, 민감한 데이터와 공격자가 제어하는 데이터를 엄격하게 분리하여 같은 압축 사전을 공유할 수 없게 하는 것이다. 신중하게 설계하면, HPACK([HPACK])처럼 제한된 사용 사례에서는 악용할 수 없다고 여겨지는 압축 방식을 만들 수 있다.
클라이언트는 많은 개인 정보를 알고 있는 경우가 많다. 여기에는 사용자가 리소스와 상호 작용하기 위해 제공한 정보(예: 사용자의 이름, 위치, 메일 주소, 비밀번호, 암호화 키 등)와 시간에 따른 사용자의 브라우징 활동에 관한 정보(예: 방문 기록, 북마크 등)가 모두 포함된다. 구현은 의도하지 않은 개인 정보 노출을 막아야 한다.
서버는 시간에 따른 사용자의 요청에 관한 개인 데이터를 저장할 수 있는 위치에 있으며, 이는 사용자의 읽기 패턴이나 관심 주제를 식별할 수 있다. 특히 중개자에서 수집한 로그 정보에는 여러 사이트에 걸친 사용자 에이전트 상호 작용 이력이 들어 있는 경우가 많으며, 이는 개별 사용자까지 추적될 수 있다.
HTTP 로그 정보는 본질적으로 기밀이며, 그 처리는 법과 규정의 제약을 받는 경우가 많다. 로그 정보는 안전하게 저장해야 하며, 분석할 때는 적절한 지침을 따라야 한다. 개별 항목 안의 개인 정보를 익명화하면 도움이 되지만, 실제 로그 기록이 다른 접근 특성과의 상관관계를 바탕으로 재식별되는 것을 막기에는 일반적으로 충분하지 않다. 그러므로 특정 클라이언트를 키로 삼는 접근 기록은 그 키가 가명이더라도 공개하기에 안전하지 않다.
도난이나 실수로 인한 공개의 위험을 최소화하려면, 로그 정보에서 사용자 식별자, IP 주소, 사용자가 제공한 쿼리 매개 변수를 포함한 개인 식별 정보를, 보안, 감사, 부정행위 통제를 위한 운영상 필요를 지원하는 데 더 이상 필요하지 않게 되는 즉시 삭제해야 마땅하다.
URI는 안전한 리소스를 식별하더라도 보호되기보다 공유되도록 의도된 것이다. URI는 화면에 표시되거나, 페이지를 인쇄할 때 템플릿에 추가되거나, 보호되지 않는 갖가지 북마크 목록에 저장되는 경우가 많다. 많은 서버, 프록시, 사용자 에이전트는 대상 URI를 제3자가 볼 수 있는 곳에 기록하거나 표시한다. 그러므로 민감하거나, 개인을 식별할 수 있거나, 노출되면 위험한 정보를 URI에 넣는 것은 현명하지 않다.
애플리케이션이 GET을 쓰는 양식의 쿼리 필드처럼 사용자가 제공한 정보로 대상 URI를 구성하는 클라이언트 측 메커니즘을 쓸 때, URI 안에 노출하기에 적절하지 않은, 민감할 수 있는 데이터가 제공될 수 있다. 이런 경우에는 POST가 선호될 때가 많다. POST는 보통 URI를 구성하지 않고, 대신 양식의 POST는 잠재적으로 민감한 데이터를 요청 콘텐츠로 전송하기 때문이다. 그러나 이는 캐싱을 방해하고, 그렇지 않았다면 안전한 요청이었을 것에 안전하지 않은 메서드를 쓰게 된다. 다른 우회책으로는 URI를 구성하기 전에 사용자가 제공한 데이터를 변환하거나, 민감하지 않은 공통 값만 포함하도록 데이터를 필터링하는 방법이 있다. 마찬가지로 쿼리 결과를 (서버가 생성한) 다른 URI로 리다이렉트하면, 이후 링크에서 잠재적으로 민감한 데이터를 없애고 나중에 재사용할 수 있는 캐시 가능한 응답을 제공할 수 있다.
Referer 헤더 필드는 대상 사이트에 요청을 일으킨 문맥을 알려 주므로, 사용자의 바로 직전 브라우징 기록과 참조하는 리소스의 URI에 있을 수 있는 개인 정보를 드러낼 가능성이 있다. Referer 헤더 필드에 대한 제한은 보안 고려 사항의 일부를 다루기 위해 10.1.3절에 설명되어 있다.
서버는 받은 요청을 처리하고 응답 콘텐츠를 만들기 위해 HTTP가 아닌 게이트웨이 인터페이스와 프레임워크를 쓰는 경우가 많다. 역사적인 이유로, 그런 인터페이스는 받은 필드 이름을 환경 변수에 맞는 이름 매핑을 써서 외부 변수 이름으로 넘기는 경우가 많다.
예를 들어 [RFC3875]의 4.1.18절에 정의된 공통 게이트웨이 인터페이스(CGI)의 프로토콜별 메타 변수 매핑은, CGI의 표준 변수 중 하나에 해당하지 않는 받은 헤더 필드에 적용된다. 그 매핑은 각 이름 앞에 “HTTP_“를 붙이고 하이픈(“-“)을 모두 밑줄(“_“)로 바꾸는 것이다. 이 같은 매핑은 애플리케이션을 한 플랫폼에서 다른 플랫폼으로 옮기기 쉽게 하려고 다른 많은 애플리케이션 프레임워크에도 이어졌다.
CGI에서는 받은 Content-Length 필드가 받은 필드 값과 같은 문자열 값을 가진 메타 변수 “CONTENT_LENGTH”로 넘겨진다. 반면 받은 “Content_Length” 헤더 필드는 프로토콜별 메타 변수 “HTTP_CONTENT_LENGTH”로 넘겨지며, 애플리케이션이 기본 메타 변수 대신 프로토콜별 메타 변수를 잘못 읽으면 혼동이 생길 수 있다. (이 역사적 관행 때문에 16.3.2.1절은 밑줄을 담은 새 필드 이름을 만들지 않도록 권한다.)
안타깝게도 필드 이름을 다른 인터페이스 이름으로 매핑하면, 그 매핑이 불완전하거나 모호할 때 보안 취약점으로 이어질 수 있다. 예를 들어 공격자가 “Transfer_Encoding”이라는 이름의 필드를 보내면, 단순한 인터페이스는 그것을 “Transfer-Encoding” 필드와 같은 변수 이름으로 매핑할 수 있고, 그 결과 요청 스머글링 취약점([HTTP/1.1]의 11.2절)이 생길 수 있다.
관련 위험을 완화하려면, 그런 매핑을 수행하는 구현은 이름으로 받을 수 있는 옥텟의 전체 범위(HTTP 문법이 권장하지 않거나 금지하는 것 포함)에 대해 매핑을 모호하지 않고 완전하게 만드는 것이 좋다. 예를 들어 이름에 특이한 문자가 있는 필드가 오면 요청을 차단하거나, 그 필드를 제거하거나, 다른 필드와 구별되게 다른 접두사를 붙여 이름을 넘길 수 있다.
URI 참조에 쓰인 프래그먼트 식별자는 요청에 실려 보내지지 않지만, 구현자는 그것이 사용자 에이전트와 응답의 결과로 실행되는 확장이나 스크립트에 보인다는 점을 알아 두어야 마땅하다. 특히 리다이렉트가 일어나고 원래 요청의 프래그먼트 식별자가 Location(10.2.2절)의 새 참조로 전달되면, 한 사이트의 프래그먼트가 다른 사이트에 노출되는 효과가 생길 수 있다. 첫 사이트가 프래그먼트에 개인 정보를 쓴다면, 다른 사이트로 리다이렉트할 때는 그렇게 전달되는 것을 막기 위해 (비어 있을지라도) 프래그먼트 구성 요소를 포함하도록 해야 마땅하다.
User-Agent(10.1.5절), Via(7.6.3절), Server(10.2.4절) 헤더 필드는 각 송신자의 소프트웨어 시스템에 관한 정보를 드러내는 경우가 많다. 이론상 이는 공격자가 알려진 보안 허점을 악용하기 쉽게 만들 수 있다. 실제로는 공격자가 겉보기 소프트웨어 버전과 상관없이 잠재적인 허점을 모두 시도하는 경향이 있다.
네트워크 방화벽을 통과하는 관문 역할을 하는 프록시는 방화벽 뒤의 호스트를 식별할 수 있는 헤더 정보의 전송에 특별히 주의해야 마땅하다. Via 헤더 필드는 중개자가 민감한 기기 이름을 가명으로 바꿀 수 있게 한다.
브라우저 핑거프린팅은 특정 사용자 에이전트를 그 고유한 특성 집합으로 시간에 걸쳐 식별하는 기법들의 집합이다. 이런 특성에는 기반 전송 프로토콜을 쓰는 방식, 기능 역량, 스크립트 환경에 관련된 정보가 포함될 수 있지만, 여기서 특히 관심을 두는 것은 HTTP로 전달될 수 있는 고유한 특성 집합이다. 핑거프린팅은 사용자가 다른 형태의 데이터 수집(예: 쿠키)에 대해 가질 수 있는 통제 없이 시간에 걸친 사용자 에이전트의 행동을 추적할 수 있게 하므로([Bujlow]) 개인 정보 보호 문제로 여겨진다. 많은 범용 사용자 에이전트(즉, 웹 브라우저)는 자신의 핑거프린트를 줄이는 조치를 취해 왔다.
핑거프린팅을 가능하게 할 만큼 고유한 정보를 서버에 드러낼 수 있는 요청 헤더 필드가 여럿 있다. From 헤더 필드가 가장 명백하지만, From은 사용자가 자기 식별을 원할 때에만 보낼 것으로 기대된다. 마찬가지로 Cookie 헤더 필드는 의도적으로 재식별을 가능하게 하도록 설계되었으므로, 핑거프린팅 우려는 사용자 에이전트의 설정으로 쿠키가 꺼지거나 제한된 상황에만 해당한다.
User-Agent 헤더 필드는 보통 다른 특성과 결합될 때, 특히 사용자 에이전트가 사용자의 시스템이나 확장에 관해 지나치게 자세한 정보를 보내면, 특정 기기를 고유하게 식별할 만큼의 정보를 담을 수 있다. 그러나 사용자가 가장 예상하지 못하는 고유 정보의 원천은 Accept, Accept-Charset, Accept-Encoding, Accept-Language 헤더 필드를 포함한 사전 협상(12.1절)이다.
핑거프린팅 우려 외에도, Accept-Language 헤더 필드를 자세히 쓰면 사용자가 사적이라고 여길 수 있는 정보가 드러날 수 있다. 예를 들어 어떤 언어 집합을 이해한다는 것은 특정 민족 집단에 속한다는 것과 강하게 상관될 수 있다. 이런 개인 정보 손실을 제한하는 한 가지 방법은, 사용자 에이전트가 명시적으로 허락된 사이트를 제외하고 Accept-Language 전송을 생략하는 것이다. 허락은 아마도 언어 협상이 쓸모 있을 수 있음을 나타내는 Vary 헤더 필드를 감지한 뒤의 상호 작용을 통해 받을 수 있다.
개인 정보 보호를 강화하기 위해 프록시를 쓰는 환경에서, 사용자 에이전트는 사전 협상 헤더 필드를 보내는 데 보수적이어야 마땅하다. 헤더 필드를 폭넓게 설정할 수 있게 하는 범용 사용자 에이전트는 너무 자세한 정보를 제공하면 생길 수 있는 개인 정보 손실을 사용자에게 알려야 마땅하다. 극단적인 개인 정보 보호 조치로, 프록시는 중계하는 요청에서 사전 협상 헤더 필드를 걸러 낼 수도 있다.
이 명세가 정의한 검증자는 표현의 유효성을 보장하거나, 악의적인 변경을 막거나, 경로상 공격을 감지하기 위한 것이 아니다. 기껏해야 모든 참여자가 바르게 행동할 때 더 효율적인 캐시 갱신과 낙관적 동시 쓰기를 가능하게 한다. 최악의 경우 조건이 실패하고, 클라이언트는 조건부 요청이 없는 HTTP 교환보다 더 해롭지 않은 응답을 받게 된다.
엔터티 태그는 개인 정보 위험을 만드는 방식으로 남용될 수 있다. 예를 들어 사이트는 사용자나 사용자 에이전트에 고유하고 의미상 유효하지 않은 엔터티 태그를 일부러 만들어, 신선도 수명이 긴 캐시 가능한 응답으로 보낸 다음, 이후 조건부 요청에서 그 엔터티 태그를 읽어 그 사용자나 사용자 에이전트를 재식별하는 수단으로 쓸 수 있다. 그런 식별용 태그는 사용자 에이전트가 원래 캐시 항목을 유지하는 동안 지속적인 식별자가 된다. 표현을 캐시하는 사용자 에이전트는 사용자가 저장된 쿠키를 지우거나 사생활 보호 브라우징 모드로 바꾸는 것처럼 개인 정보 보호 조치를 할 때마다 캐시가 비워지거나 교체되도록 해야 마땅하다.
제약 없는 다중 범위 요청은 서비스 거부 공격에 취약하다. 같은 데이터의 겹치는 범위를 많이 요청하는 데 드는 노력이, 요청된 데이터를 여러 파트로 제공하려고 할 때 소모되는 시간, 메모리, 대역폭에 비해 아주 작기 때문이다. 서버는 겹치는 범위가 둘을 넘거나 한 집합에 작은 범위가 많은 요청처럼 터무니없는 범위 요청을, 특히 범위가 뚜렷한 이유 없이 순서에 맞지 않게 요청될 때 무시하거나, 병합하거나, 거부해야 마땅하다. 멀티파트 범위 요청은 임의 접근을 지원하도록 설계되지 않았다.
HTTP 인증이라는 주제는 모든 것이 보안 고려 사항이므로, 아래 고려 사항 목록은 완전하지 않다. 또한 이 목록은 특정 인증 스킴에 대한 잠재적 고려 사항(그 스킴을 정의하는 명세에서 문서화해야 마땅하다)을 모두 논의하기보다, 인증 프레임워크 전반에 관한 보안 고려 사항으로 한정한다. 여러 기관이 실제로 쓰이는 인증 스킴을 구현하고 사용할 때 흔히 빠지는 함정을 포함해, 웹 애플리케이션 보안에 관한 주제별 정보와 최신 연구로의 링크를 관리한다(예: [OWASP]).
HTTP 인증 프레임워크는 자격 증명의 기밀성을 유지하는 단일 메커니즘을 정의하지 않는다. 대신 각 인증 스킴이 전송 전에 자격 증명을 어떻게 인코딩할지 정의한다. 이는 앞으로의 인증 스킴 개발에 유연성을 주지만, 스스로 기밀성을 제공하지 않거나 재전송 공격을 충분히 막지 못하는 기존 스킴을 보호하기에는 부족하다. 또한 서버가 개별 사용자마다 특정한 자격 증명을 기대한다면, 자격 증명 안의 내용이 기밀로 유지되더라도 그 자격 증명을 교환하는 것만으로 그 사용자를 식별하는 효과가 생긴다.
HTTP는 필드를 기밀로 전송하기 위해 기반 전송 수준 또는 세션 수준 연결의 보안 속성에 의존한다. 개별 사용자 인증에 의존하는 서비스는 자격 증명을 교환하기 전에 보안 처리된 연결을 요구한다(4.2.2절).
기존 HTTP 클라이언트와 사용자 에이전트는 보통 인증 정보를 무기한 유지한다. 프로토콜은 사용자 에이전트가 자격 증명을 어떻게 얻거나 관리하는지 알지 못하므로, HTTP는 원본 서버가 클라이언트에게 이렇게 캐시된 자격 증명을 버리라고 지시하는 메커니즘을 제공하지 않는다. 자격 증명을 만료하거나 폐기하는 메커니즘은 인증 스킴 정의의 일부로 명시할 수 있다.
자격 증명 캐싱이 애플리케이션의 보안 모델을 방해할 수 있는 상황은 다음을 포함하며 그에 한정되지 않는다.
자격 증명을 캐시하는 사용자 에이전트는 사용자가 제어하는 상태에서 캐시된 자격 증명을 버릴 수 있는, 쉽게 접근할 수 있는 메커니즘을 제공하는 것이 좋다.
보호 공간을 확립하는 데 “realm” 메커니즘에만 의존하는 인증 스킴은 원본 서버의 모든 리소스에 자격 증명을 노출한다. 어떤 리소스에 인증된 요청을 성공적으로 한 클라이언트는 같은 원본 서버의 다른 리소스에 같은 인증 자격 증명을 쓸 수 있다. 이 때문에 다른 리소스가 또 다른 리소스용 인증 자격 증명을 수집할 수 있게 된다.
이는 원본 서버가 같은 출처 아래에서 여러 당사자를 위한 리소스를 호스팅할 때 특히 우려된다(11.5절). 가능한 완화 전략으로는 인증 자격 증명에 대한 직접 접근을 제한하는 것(즉, Authorization 요청 헤더 필드의 내용을 사용할 수 없게 하는 것)과, 당사자마다 다른 호스트 이름(또는 포트 번호)을 써서 보호 공간을 분리하는 것이 있다.
암호화되지 않은 채널로 보내는 응답에 정보를 추가하면 보안과 개인 정보 보호에 영향을 줄 수 있다. Authentication-Info와 Proxy-Authentication-Info 헤더 필드가 있다는 것만으로 HTTP 인증이 쓰이고 있음이 드러난다. 인증 스킴별 매개 변수의 내용으로 추가 정보가 노출될 수 있으며, 이는 그 스킴들의 정의에서 고려해야 할 것이다.
역주: 17장에는 대문자 키워드가 없다. “ought to”·”need to”·”are advised to”·”are encouraged to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다. 17.4절 제목의 “Injection”과 본문의 “SQL injection”은 정보통신기술용어해설 표기인 “인젝션”으로 옮겼다(TTA: 주입 공격).
다음 등록들의 변경 관리자는 “IETF (iesg@ietf.org) - Internet Engineering Task Force”다.
IANA는 https://www.iana.org/assignments/uri-schemes/의 “URI 스킴(Uniform Resource Identifier (URI) Schemes)” 레지스트리([BCP35])를 4.2절의 표 2에 나열된 permanent 스킴으로 갱신했다.
IANA는 https://www.iana.org/assignments/http-methods의 “HTTP 메서드 레지스트리”를 16.1.1절의 등록 절차와 다음 표에 요약한 메서드 이름으로 갱신했다.
| 메서드(Method) | 안전(Safe) | 멱등(Idempotent) | 절(Section) |
|---|---|---|---|
| CONNECT | no | no | 9.3.6 |
| DELETE | no | yes | 9.3.5 |
| GET | yes | yes | 9.3.1 |
| HEAD | yes | yes | 9.3.2 |
| OPTIONS | yes | yes | 9.3.7 |
| POST | no | no | 9.3.3 |
| PUT | no | yes | 9.3.4 |
| TRACE | yes | yes | 9.3.8 |
| * | no | no | 18.2 |
표 7
메서드 이름 ““는 예약되어 있다. ““를 메서드 이름으로 쓰면 일부 필드(예: “Access-Control-Request-Method”)에서 와일드카드로 쓰이는 용법과 충돌하기 때문이다.
IANA는 https://www.iana.org/assignments/http-status-codes의 “HTTP 상태 코드 레지스트리”를 16.2.1절의 등록 절차와 다음 표에 요약한 상태 코드 값으로 갱신했다.
| 값(Value) | 설명(Description) | 절(Section) |
|---|---|---|
| 100 | Continue | 15.2.1 |
| 101 | Switching Protocols | 15.2.2 |
| 200 | OK | 15.3.1 |
| 201 | Created | 15.3.2 |
| 202 | Accepted | 15.3.3 |
| 203 | Non-Authoritative Information | 15.3.4 |
| 204 | No Content | 15.3.5 |
| 205 | Reset Content | 15.3.6 |
| 206 | Partial Content | 15.3.7 |
| 300 | Multiple Choices | 15.4.1 |
| 301 | Moved Permanently | 15.4.2 |
| 302 | Found | 15.4.3 |
| 303 | See Other | 15.4.4 |
| 304 | Not Modified | 15.4.5 |
| 305 | Use Proxy | 15.4.6 |
| 306 | (Unused) | 15.4.7 |
| 307 | Temporary Redirect | 15.4.8 |
| 308 | Permanent Redirect | 15.4.9 |
| 400 | Bad Request | 15.5.1 |
| 401 | Unauthorized | 15.5.2 |
| 402 | Payment Required | 15.5.3 |
| 403 | Forbidden | 15.5.4 |
| 404 | Not Found | 15.5.5 |
| 405 | Method Not Allowed | 15.5.6 |
| 406 | Not Acceptable | 15.5.7 |
| 407 | Proxy Authentication Required | 15.5.8 |
| 408 | Request Timeout | 15.5.9 |
| 409 | Conflict | 15.5.10 |
| 410 | Gone | 15.5.11 |
| 411 | Length Required | 15.5.12 |
| 412 | Precondition Failed | 15.5.13 |
| 413 | Content Too Large | 15.5.14 |
| 414 | URI Too Long | 15.5.15 |
| 415 | Unsupported Media Type | 15.5.16 |
| 416 | Range Not Satisfiable | 15.5.17 |
| 417 | Expectation Failed | 15.5.18 |
| 418 | (Unused) | 15.5.19 |
| 421 | Misdirected Request | 15.5.20 |
| 422 | Unprocessable Content | 15.5.21 |
| 426 | Upgrade Required | 15.5.22 |
| 500 | Internal Server Error | 15.6.1 |
| 501 | Not Implemented | 15.6.2 |
| 502 | Bad Gateway | 15.6.3 |
| 503 | Service Unavailable | 15.6.4 |
| 504 | Gateway Timeout | 15.6.5 |
| 505 | HTTP Version Not Supported | 15.6.6 |
표 8
이 명세는 [RFC3864]에 정의된 메시지 헤더 필드의 기존 등록 절차 가운데 HTTP와 관련된 부분을 갱신한다. 새 등록 절차를 정의하고 HTTP 필드 정의를 별도의 레지스트리로 옮김으로써, HTTP와 관련된 범위에서 이전 절차를 대체한다.
IANA는 16.3.1절에 설명된 대로 “HTTP 필드 이름 레지스트리”라는 새 레지스트리를 만들었다.
IANA는 “영구 메시지 헤더 필드 이름(Permanent Message Header Field Names)”과 “임시 메시지 헤더 필드 이름(Provisional Message Header Field Names)” 레지스트리(https://www.iana.org/assignments/message-headers/ 참고)에서 프로토콜이 ‘http’인 항목을 모두 이 레지스트리로 옮기고 다음 변경을 적용했다.
IANA는 HTTP 필드 이름 등록이 옮겨졌음을 나타내기 위해 “영구 메시지 헤더 필드 이름”과 “임시 메시지 헤더 필드 이름” 레지스트리에 다음 안내를 달았다.
Note
HTTP field name registrations have been moved to [https://www.iana.org/assignments/http-fields] per [RFC9110].
IANA는 “HTTP 필드 이름 레지스트리”를 다음 표에 나열한 필드 이름으로 갱신했다.
| 필드 이름(Field Name) | 상태(Status) | 절(Section) | 비고(Comments) |
|---|---|---|---|
| Accept | permanent | 12.5.1 | |
| Accept-Charset | deprecated | 12.5.2 | |
| Accept-Encoding | permanent | 12.5.3 | |
| Accept-Language | permanent | 12.5.4 | |
| Accept-Ranges | permanent | 14.3 | |
| Allow | permanent | 10.2.1 | |
| Authentication-Info | permanent | 11.6.3 | |
| Authorization | permanent | 11.6.2 | |
| Connection | permanent | 7.6.1 | |
| Content-Encoding | permanent | 8.4 | |
| Content-Language | permanent | 8.5 | |
| Content-Length | permanent | 8.6 | |
| Content-Location | permanent | 8.7 | |
| Content-Range | permanent | 14.4 | |
| Content-Type | permanent | 8.3 | |
| Date | permanent | 6.6.1 | |
| ETag | permanent | 8.8.3 | |
| Expect | permanent | 10.1.1 | |
| From | permanent | 10.1.2 | |
| Host | permanent | 7.2 | |
| If-Match | permanent | 13.1.1 | |
| If-Modified-Since | permanent | 13.1.3 | |
| If-None-Match | permanent | 13.1.2 | |
| If-Range | permanent | 13.1.5 | |
| If-Unmodified-Since | permanent | 13.1.4 | |
| Last-Modified | permanent | 8.8.2 | |
| Location | permanent | 10.2.2 | |
| Max-Forwards | permanent | 7.6.2 | |
| Proxy-Authenticate | permanent | 11.7.1 | |
| Proxy-Authentication-Info | permanent | 11.7.3 | |
| Proxy-Authorization | permanent | 11.7.2 | |
| Range | permanent | 14.2 | |
| Referer | permanent | 10.1.3 | |
| Retry-After | permanent | 10.2.3 | |
| Server | permanent | 10.2.4 | |
| TE | permanent | 10.1.4 | |
| Trailer | permanent | 6.6.2 | |
| Upgrade | permanent | 7.8 | |
| User-Agent | permanent | 10.1.5 | |
| Vary | permanent | 12.5.5 | |
| Via | permanent | 7.6.3 | |
| WWW-Authenticate | permanent | 11.6.1 | |
| * | permanent | 12.5.5 | (reserved) |
표 9
필드 이름 “*“는 예약되어 있다. 그 이름을 HTTP 헤더 필드로 쓰면 Vary 헤더 필드(12.5.5절)에서의 특별한 의미와 충돌할 수 있기 때문이다.
IANA는 새 레지스트리의 “Content-MD5” 항목을 상태 ‘obsoleted’로 갱신하고, [RFC2616]의 14.15절(헤더 필드 정의)과 [RFC7231]의 부록 B(갱신된 명세에서 필드 정의를 제거함)를 참조로 달았다.
IANA는 https://www.iana.org/assignments/http-authschemes의 “HTTP 인증 스킴 레지스트리”를 16.4.1절의 등록 절차로 갱신했다. 이 문서는 인증 스킴을 정의하지 않는다.
IANA는 https://www.iana.org/assignments/http-parameters/의 “HTTP 콘텐츠 코딩 레지스트리”를 16.6.1절의 등록 절차와 아래 표에 요약한 콘텐츠 코딩 이름으로 갱신했다.
| 이름(Name) | 설명(Description) | 절(Section) |
|---|---|---|
| compress | UNIX “compress” data format [Welch] | 8.4.1.1 |
| deflate | “deflate” compressed data ([RFC1951]) inside the “zlib” data format ([RFC1950]) | 8.4.1.2 |
| gzip | GZIP file format [RFC1952] | 8.4.1.3 |
| identity | Reserved | 12.5.3 |
| x-compress | Deprecated (alias for compress) | 8.4.1.1 |
| x-gzip | Deprecated (alias for gzip) | 8.4.1.3 |
표 10
IANA는 https://www.iana.org/assignments/http-parameters/의 “HTTP 범위 단위 레지스트리”를 16.5.1절의 등록 절차와 아래 표에 요약한 범위 단위 이름으로 갱신했다.
| 범위 단위 이름(Range Unit Name) | 설명(Description) | 절(Section) |
|---|---|---|
| bytes | a range of octets | 14.1.2 |
| none | reserved as keyword to indicate range requests are not supported | 14.3 |
표 11
IANA는 https://www.iana.org/assignments/media-types의 “미디어 유형(Media Types)” 레지스트리를 미디어 유형 “multipart/byteranges”에 대한 14.6절의 등록 정보로 갱신했다.
IANA는 “q” 매개 변수에 관한 레지스트리 안내를 이 문서의 12.5.1절로 연결되는 링크로 갱신했다.
IANA는 https://www.iana.org/assignments/service-names-port-numbers/의 “서비스 이름 및 전송 프로토콜 포트 번호 레지스트리(Service Name and Transport Protocol Port Number Registry)”에서 UDP나 TCP를 쓰는 80번과 443번 포트의 서비스를 다음과 같이 갱신했다.
IANA는 https://www.iana.org/assignments/http-upgrade-tokens의 “HTTP 업그레이드 토큰 레지스트리”를 16.7절에 설명된 등록 절차와 다음 표에 요약한 업그레이드 토큰 이름으로 갱신했다.
| 이름(Name) | 설명(Description) | 예상 버전 토큰(Expected Version Tokens) | 절(Section) |
|---|---|---|---|
| HTTP | Hypertext Transfer Protocol | any DIGIT.DIGIT (e.g., “2.0”) | 2.5 |
표 12
역주: 18장에는 대문자 키워드가 없다. 표 7~12의 값(메서드 이름, 상태 코드 설명, 등록 상태, 콘텐츠 코딩·범위 단위·업그레이드 토큰의 설명)은 IANA 레지스트리에 그대로 등록되는 값이라 원어로 두고, 열 이름만 번역하고 원어를 병기했다. 표의 “절” 열은 이 문서의 해당 절로 링크했다. 18.4절의 레지스트리 안내 문구도 IANA 레지스트리에 실린 문장이라 원문 그대로 두었다.
역주: RFC 8446은 2026년 7월 RFC 9846으로 대체되었다(번역). RFC 9846도 같은 TLS 버전 1.3을 규정하며, RFC 8446의 절 구성을 대체로 그대로 유지한다.
역주: 참고 문헌 항목은 원문 그대로 두고 Markdown 목록으로 바꾸었다. 여러 줄에 걸친 항목은 한 줄로 이었고, 줄바꿈으로 끊긴 URL은 공백 없이 이었다.
아래 수집한 ABNF에서 목록 규칙은 5.6.1절에 따라 펼쳐 놓았다.
Accept = [ ( media-range [ weight ] ) *( OWS "," OWS ( media-range [
weight ] ) ) ]
Accept-Charset = [ ( ( token / "*" ) [ weight ] ) *( OWS "," OWS ( (
token / "*" ) [ weight ] ) ) ]
Accept-Encoding = [ ( codings [ weight ] ) *( OWS "," OWS ( codings [
weight ] ) ) ]
Accept-Language = [ ( language-range [ weight ] ) *( OWS "," OWS (
language-range [ weight ] ) ) ]
Accept-Ranges = acceptable-ranges
Allow = [ method *( OWS "," OWS method ) ]
Authentication-Info = [ auth-param *( OWS "," OWS auth-param ) ]
Authorization = credentials
BWS = OWS
Connection = [ connection-option *( OWS "," OWS connection-option )
]
Content-Encoding = [ content-coding *( OWS "," OWS content-coding )
]
Content-Language = [ language-tag *( OWS "," OWS language-tag ) ]
Content-Length = 1*DIGIT
Content-Location = absolute-URI / partial-URI
Content-Range = range-unit SP ( range-resp / unsatisfied-range )
Content-Type = media-type
Date = HTTP-date
ETag = entity-tag
Expect = [ expectation *( OWS "," OWS expectation ) ]
From = mailbox
GMT = %x47.4D.54 ; GMT
HTTP-date = IMF-fixdate / obs-date
Host = uri-host [ ":" port ]
IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
If-Match = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
If-Modified-Since = HTTP-date
If-None-Match = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
If-Range = entity-tag / HTTP-date
If-Unmodified-Since = HTTP-date
Last-Modified = HTTP-date
Location = URI-reference
Max-Forwards = 1*DIGIT
OWS = *( SP / HTAB )
Proxy-Authenticate = [ challenge *( OWS "," OWS challenge ) ]
Proxy-Authentication-Info = [ auth-param *( OWS "," OWS auth-param )
]
Proxy-Authorization = credentials
RWS = 1*( SP / HTAB )
Range = ranges-specifier
Referer = absolute-URI / partial-URI
Retry-After = HTTP-date / delay-seconds
Server = product *( RWS ( product / comment ) )
TE = [ t-codings *( OWS "," OWS t-codings ) ]
Trailer = [ field-name *( OWS "," OWS field-name ) ]
URI-reference = <URI-reference, see [URI], Section 4.1>
Upgrade = [ protocol *( OWS "," OWS protocol ) ]
User-Agent = product *( RWS ( product / comment ) )
Vary = [ ( "*" / field-name ) *( OWS "," OWS ( "*" / field-name ) )
]
Via = [ ( received-protocol RWS received-by [ RWS comment ] ) *( OWS
"," OWS ( received-protocol RWS received-by [ RWS comment ] ) ) ]
WWW-Authenticate = [ challenge *( OWS "," OWS challenge ) ]
absolute-URI = <absolute-URI, see [URI], Section 4.3>
absolute-path = 1*( "/" segment )
acceptable-ranges = range-unit *( OWS "," OWS range-unit )
asctime-date = day-name SP date3 SP time-of-day SP year
auth-param = token BWS "=" BWS ( token / quoted-string )
auth-scheme = token
authority = <authority, see [URI], Section 3.2>
challenge = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
OWS auth-param ) ] ) ]
codings = content-coding / "identity" / "*"
comment = "(" *( ctext / quoted-pair / comment ) ")"
complete-length = 1*DIGIT
connection-option = token
content-coding = token
credentials = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
OWS auth-param ) ] ) ]
ctext = HTAB / SP / %x21-27 ; '!'-'''
/ %x2A-5B ; '*'-'['
/ %x5D-7E ; ']'-'~'
/ obs-text
date1 = day SP month SP year
date2 = day "-" month "-" 2DIGIT
date3 = month SP ( 2DIGIT / ( SP DIGIT ) )
day = 2DIGIT
day-name = %x4D.6F.6E ; Mon
/ %x54.75.65 ; Tue
/ %x57.65.64 ; Wed
/ %x54.68.75 ; Thu
/ %x46.72.69 ; Fri
/ %x53.61.74 ; Sat
/ %x53.75.6E ; Sun
day-name-l = %x4D.6F.6E.64.61.79 ; Monday
/ %x54.75.65.73.64.61.79 ; Tuesday
/ %x57.65.64.6E.65.73.64.61.79 ; Wednesday
/ %x54.68.75.72.73.64.61.79 ; Thursday
/ %x46.72.69.64.61.79 ; Friday
/ %x53.61.74.75.72.64.61.79 ; Saturday
/ %x53.75.6E.64.61.79 ; Sunday
delay-seconds = 1*DIGIT
entity-tag = [ weak ] opaque-tag
etagc = "!" / %x23-7E ; '#'-'~'
/ obs-text
expectation = token [ "=" ( token / quoted-string ) parameters ]
field-content = field-vchar [ 1*( SP / HTAB / field-vchar )
field-vchar ]
field-name = token
field-value = *field-content
field-vchar = VCHAR / obs-text
first-pos = 1*DIGIT
hour = 2DIGIT
http-URI = "http://" authority path-abempty [ "?" query ]
https-URI = "https://" authority path-abempty [ "?" query ]
incl-range = first-pos "-" last-pos
int-range = first-pos "-" [ last-pos ]
language-range = <language-range, see [RFC4647], Section 2.1>
language-tag = <Language-Tag, see [RFC5646], Section 2.1>
last-pos = 1*DIGIT
mailbox = <mailbox, see [RFC5322], Section 3.4>
media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) )
parameters
media-type = type "/" subtype parameters
method = token
minute = 2DIGIT
month = %x4A.61.6E ; Jan
/ %x46.65.62 ; Feb
/ %x4D.61.72 ; Mar
/ %x41.70.72 ; Apr
/ %x4D.61.79 ; May
/ %x4A.75.6E ; Jun
/ %x4A.75.6C ; Jul
/ %x41.75.67 ; Aug
/ %x53.65.70 ; Sep
/ %x4F.63.74 ; Oct
/ %x4E.6F.76 ; Nov
/ %x44.65.63 ; Dec
obs-date = rfc850-date / asctime-date
obs-text = %x80-FF
opaque-tag = DQUOTE *etagc DQUOTE
other-range = 1*( %x21-2B ; '!'-'+'
/ %x2D-7E ; '-'-'~'
)
parameter = parameter-name "=" parameter-value
parameter-name = token
parameter-value = ( token / quoted-string )
parameters = *( OWS ";" OWS [ parameter ] )
partial-URI = relative-part [ "?" query ]
path-abempty = <path-abempty, see [URI], Section 3.3>
port = <port, see [URI], Section 3.2.3>
product = token [ "/" product-version ]
product-version = token
protocol = protocol-name [ "/" protocol-version ]
protocol-name = token
protocol-version = token
pseudonym = token
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'['
/ %x5D-7E ; ']'-'~'
/ obs-text
query = <query, see [URI], Section 3.4>
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qvalue = ( "0" [ "." *3DIGIT ] ) / ( "1" [ "." *3"0" ] )
range-resp = incl-range "/" ( complete-length / "*" )
range-set = range-spec *( OWS "," OWS range-spec )
range-spec = int-range / suffix-range / other-range
range-unit = token
ranges-specifier = range-unit "=" range-set
received-by = pseudonym [ ":" port ]
received-protocol = [ protocol-name "/" ] protocol-version
relative-part = <relative-part, see [URI], Section 4.2>
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
second = 2DIGIT
segment = <segment, see [URI], Section 3.3>
subtype = token
suffix-length = 1*DIGIT
suffix-range = "-" suffix-length
t-codings = "trailers" / ( transfer-coding [ weight ] )
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
"^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
time-of-day = hour ":" minute ":" second
token = 1*tchar
token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" )
*"="
transfer-coding = token *( OWS ";" OWS transfer-parameter )
transfer-parameter = token BWS "=" BWS ( token / quoted-string )
type = token
unsatisfied-range = "*/" complete-length
uri-host = <host, see [URI], Section 3.2.2>
weak = %x57.2F ; W/
weight = OWS ";" OWS "q=" qvalue
year = 4DIGIT
역주: 정오표 9164(Reported, 미확인)는 부록 A가 본문 규칙을 일부 정규화해 옮겼다는 설명을 덧붙이자는 제안이다.
역주: 정오표 7306(Verified)은 14.1.1절의
ranges-specifier만 고쳤으므로, 부록 A의 같은 규칙은 원문대로 두었다.
없음.
CN-ID는 사용이 중단되었다.
역주: 정오표 7105(Verified)에 따라 “없음.”을 고친 내용이다.
HTTP의 설계 목표, 역사, 아키텍처, 적합성 기준, 프로토콜 버전 관리, URI, 메시지 라우팅, 헤더 필드를 소개하는 절들을 이 문서로 옮겼다.
의미 적합성에 관한 요구 사항을, 구현별 실패를 무시하거나 우회해도 된다는 허용으로 바꾸었다. (2.2절)
출처의 설명과 원본 서버에 대한 권한 있는 접근의 설명을 “http”와 “https” URI 모두에 대해 확장하여, 대체 서비스와 꼭 TCP에 기반하지 않는 보안 처리된 연결을 고려하도록 했다. (4.2.1절, 4.2.2절, 4.3.1절, 7.3.3절)
대상 URI 스킴의 의미를 확인하고, 연관된 요구 사항을 충족하지 않는 요청을 거부하라는 명시적인 요구 사항을 추가했다. (7.4절)
미디어 유형, 미디어 범위, 기대의 매개 변수는 끝에 오는 세미콜론 하나 이상으로 비어 있을 수 있다. (5.6.6절)
“필드 값”은 이제 여러 필드 줄을 쉼표로 결합한 뒤의 값을 가리킨다. 이것이 단연 가장 흔한 용법이다. 헤더 줄 하나의 값을 가리킬 때는 “필드 줄 값”을 쓴다. (6.3절)
트레일러 필드의 의미는 이제 청크 전송 코딩의 세부 사항을 넘어선다. 트레일러 필드의 사용은 더 제한되어, 송신자가 그 필드가 그런 용법을 정의한다는 것을 알 때에만 트레일러 필드로 생성할 수 있고, 수신자가 해당 필드 정의가 병합을 허용하고 병합 방법을 정의한다는 것을 알 때에만 헤더 섹션으로 병합할 수 있다. 그 밖의 모든 경우에 구현은 트레일러 필드를 병합하지 말고 따로 저장하거나 버리는 것이 좋다. (6.5.1절)
프록시 처리와 맞추기 위해, 원본 서버가 요청 URI의 절대 형식을 Host 헤더 필드보다 우선한다는 점을 명시했다. (7.2절)
역주: 정오표 8268(Verified)에 따르면 RFC 9112(부록 C.3)에 있어야 할 내용이다.
Via 필드의 “received-by” 문법 정의는, Via에 바람직하지 않은 host에 대한 URI 문법 변경([URI]) 때문에 RFC 7230에서 확장되었다. 간단하게 하기 위해 received-by 생성 규칙에서 uri-host를 없앴다. 기존의 pseudonym 문법으로 포괄할 수 있기 때문이다. 특히 이 변경으로 received-by의 호스트 이름에 허용되는 문자 집합에서 쉼표가 빠졌다. (7.6.3절)
구현이 지원해야 하는 최소 URI 길이를 이제 권고한다. (4.1절)
다음을 명확히 했다. 필드 값의 CR과 NUL은 거부하거나 SP로 바꿔야 하고, 필드 값을 사용하기 전에 앞뒤 공백을 없애야 한다. (5.5절)
미디어 유형, 미디어 범위, 기대의 매개 변수는 끝에 오는 세미콜론 하나 이상으로 비어 있을 수 있다. (5.6.6절)
HTTP 메시지를 위한 추상 데이터형을 도입했다. 이를 통해 메시지의 구성 요소와 그 의미를 [HTTP/1.1]의 HTTP/1.1 구문 형식이 아니라 여러 HTTP 버전에 걸친 추상화로 정의하고, 메시지를 파싱한 뒤의 내용을 반영한다. 이렇게 하면 콘텐츠에 대한 요구 사항(무엇을 전달하는가)과 메시지 구문에 대한 요구 사항(어떻게 전달하는가)을 구별하기 쉬워지고, 초기 프로토콜 버전의 한계를 HTTP의 미래에 굳혀 넣는 일을 피할 수 있다. (6장)
“페이로드(payload)”와 “페이로드 본문(payload body)”이라는 용어를 “콘텐츠(content)”로 바꾸었다. 다른 곳(예: 필드 이름)에서의 용법과 더 잘 맞추고, HTTP/2와 HTTP/3의 프레임 페이로드와 혼동되지 않게 하기 위해서다. (6.4절)
“유효 요청 URI(effective request URI)”라는 용어를 “대상 URI(target URI)”로 바꾸었다. (7.1절)
클라이언트 재시도에 대한 제한을 구현 동작에 맞게 완화했다. (9.2.2절)
GET, HEAD, DELETE의 요청 본문이 상호 운용되지 않는다는 사실을 명확히 했다. (9.3.1절, 9.3.2절, 9.3.5절)
Content-Range 헤더 필드(14.4절)를 PUT의 요청 수정자로 쓰는 것을 허용한다. (9.3.4절)
OPTIONS 메서드의 설명에서 Content-Length 설정에 관한 불필요한 요구 사항을 없앴다. (9.3.7절)
TRACE 응답에서 “message/http” 미디어 유형을 쓰라는 규범적 요구 사항을 없앴다. (9.3.8절)
RFC 2616과의 호환성을 위해 Expect의 목록 기반 문법을 되살렸다. (10.1.1절)
Accept와 Accept-Encoding을 응답 메시지에서 쓸 수 있다. 후자는 [RFC7694]가 도입했다. (12.3절)
Accept 필드의 정의에서 “Accept Parameters”(accept-params와 accept-ext ABNF 생성 규칙)를 없앴다. (12.5.1절)
Accept-Charset 필드는 이제 사용이 중단되었다. (12.5.2절)
Vary 헤더 필드에 다른 값이 함께 있을 때 “*“의 의미를 명확히 했다. (12.5.5절)
범위 단위는 대소문자를 구분하지 않고 비교한다. (14.1절)
Accept-Ranges 필드의 사용은 원본 서버로 제한되지 않는다. (14.3절)
리다이렉트된 요청을 만드는 과정을 명확히 했다. (15.4절)
상태 코드 308(이전에는 [RFC7538]에서 정의)을 추가해 상태 코드 301, 302, 307과 가까운 곳에 정의되도록 했다. (15.4.9절)
상태 코드 421(이전에는 [RFC7540]의 9.1.2절에서 정의)을 일반적으로 적용할 수 있어서 추가했다. 421 응답은 (대상 리소스가 아니라) 연결에 특정하므로 더 이상 휴리스틱 캐시 가능으로 정의하지 않는다. (15.5.20절)
상태 코드 422(이전에는 [WEBDAV]의 11.2절에서 정의)를 일반적으로 적용할 수 있어서 추가했다. (15.5.21절)
이전 판의 HTTP는 Date와 Last-Modified 값이 서로 다른 시계로, 또는 응답을 준비하는 동안 조금씩 다른 시점에 생성되었을 가능성에 대비하기 위해, Last-Modified가 강한 검증자인지 판단할 때 임의로 60초 제한을 두었다. 이 명세는 이를 완화하여 합리적인 재량을 허용한다. (8.8.2.2절)
If-Match와 If-Unmodified-Since에 관한 특수한 경우의 요구 사항, 즉 변경 요청이 이미 적용되어 검증이 실패하면 2xx 응답에 검증자를 보내지 말라는 요구 사항을 없앴다. (13.1.1절, 13.1.4절)
If-Unmodified-Since가 수정 시간 개념이 없는 리소스에는 적용되지 않는다는 사실을 명확히 했다. (13.1.4절)
이제 전제 조건은 응답이 성공할 때까지 기다리지 않고 요청 콘텐츠를 처리하기 전에 평가할 수 있다. (13.2절)
range-unit과 ranges-specifier 문법을 정리하여, bytes와 다른 (확장) 범위 단위 사이의 인위적인 구분을 단순화하고 줄였다. 범위 단위를 일반적으로 토큰으로 정의하고 확장을 range-spec(other-range)의 범위 안에 두어, other-range-unit의 겹치는 문법을 없앴다. 이로써 확장 범위 단위를 포함한 모든 범위 집합에서, 둘 이상의 범위로 이루어진 range-set을 나타내는 목록 구문(쉼표)의 역할이 모호하지 않게 되었다. 확장 문법을 범위 지정자 안으로 옮겨, 바이트 범위에 특정한 프로토콜을 따로 명시할 수도 있게 되었다.
이제 확장 메서드에 대한 Range 처리를 정의할 수 있다. (14.2절)
부분 PUT을 수행하기 위해 Content-Range 헤더 필드(14.4절)를 요청 수정자로 쓰는 방법을 설명했다. (14.5절)
없음.
없음.
없음.
이 명세는 [RFC7694]에서 정의한 확장을 포함하지만, 예와 배포 고려 사항은 뺐다.
역주: 부록 B에는 대문자 키워드가 없다. “are encouraged to”(B.2절)와 “are to be”, “needs to”(B.3절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
현재 편집자들 외에도, 다음 사람들은 HTTP와 그 핵심 명세의 초기 형태에 기여한 공로로 특별히 감사를 받을 만하다. Marc Andreessen, Tim Berners-Lee, Robert Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jim Gettys, Jean-François Groff, Phillip M. Hallam-Baker, Koen Holtman, Jeffery L. Hostetler, Shel Kaphan, Dave Kristol, Yves Lafon, Scott D. Lawrence, Paul J. Leach, Håkon W. Lie, Ari Luotonen, Larry Masinter, Rob McCool, Jeffrey C. Mogul, Lou Montulli, David Morris, Henrik Frystyk Nielsen, Dave Raggett, Eric Rescorla, Tony Sanders, Lawrence C. Stewart, Marc VanHeyningen, Steve Zilles.
이 문서는 [HTTP/1.0], [RFC2068], [RFC2145], [RFC2616], [RFC2617], [RFC2818], [RFC7230], [RFC7231], [RFC7232], [RFC7233], [RFC7234], [RFC7235]를 비롯한 HTTP의 이전 명세들에 들어간 많은 기여를 바탕으로 한다. 그 문서들의 감사의 글은 여전히 유효하다.
2014년 이후 다음 기여자들이 버그를 보고하고, 날카로운 질문을 하고, 글을 쓰거나 검토하고, 이슈를 평가하여 이 명세를 개선하는 데 도움을 주었다.
Alan Egerton, Alex Rousskov, Amichai Rothman, Amos Jeffries, Anders Kaseorg, Andreas Gebhardt, Anne van Kesteren, Armin Abfalterer, Aron Duby, Asanka Herath, Asbjørn Ulsberg, Asta Olofsson, Attila Gulyas, Austin Wright, Barry Pollard, Ben Burkert, Benjamin Kaduk, Björn Höhrmann, Brad Fitzpatrick, Chris Pacejo, Colin Bendell, Cory Benfield, Cory Nelson, Daisuke Miyakawa, Dale Worley, Daniel Stenberg, Danil Suits, David Benjamin, David Matson, David Schinazi, Дилян Палаузов (Dilyan Palauzov), Eric Anderson, Eric Rescorla, Éric Vyncke, Erik Kline, Erwin Pe, Etan Kissling, Evert Pot, Evgeny Vrublevsky, Florian Best, Francesca Palombini, Igor Lubashev, James Callahan, James Peach, Jeffrey Yasskin, Kalin Gyokov, Kannan Goundan, 奥 一穂 (Kazuho Oku), Ken Murchison, Krzysztof Maczyński, Lars Eggert, Lucas Pardue, Martin Duke, Martin Dürst, Martin Thomson, Martynas Jusevičius, Matt Menke, Matthias Pigulla, Mattias Grenfeldt, Michael Osipov, Mike Bishop, Mike Pennisi, Mike Taylor, Mike West, Mohit Sethi, Murray Kucherawy, Nathaniel J. Smith, Nicholas Hurley, Nikita Prokhorov, Patrick McManus, Piotr Sikora, Poul-Henning Kamp, Rick van Rein, Robert Wilton, Roberto Polli, Roman Danyliw, Samuel Williams, Semyon Kholodnov, Simon Pieters, Simon Schüppel, Stefan Eissing, Taylor Hunt, Todd Greer, Tommy Pauly, Vasiliy Faronov, Vladimir Lashchev, Wenbo Zhu, William A. Rowe Jr., Willy Tarreau, Xingwei Liu, Yishuai Li, Zaheduzzaman Sarker.
역주: 사람 이름은 원문 표기 그대로 두었다.
1 2 3 4 5 A B C D E F G H I L M N O P R S T U V W X
역주: 색인 항목은 원문 그대로 두고, 절 번호만 이 문서의 해당 절 링크로 바꾸었다. 원문에서 강조(
*_…_*)된 참조는 굵게 표시했다.
Roy T. Fielding (editor)
Adobe
345 Park Ave
San Jose, CA 95110
United States of America
Email: fielding@gbiv.com
URI: https://roy.gbiv.com/
Mark Nottingham (editor)
Fastly
Prahran
Australia
Email: mnot@mnot.net
URI: https://www.mnot.net/
Julian Reschke (editor)
greenbytes GmbH
Hafenweg 16
48155 Münster
Germany
Email: julian.reschke@greenbytes.de
URI: https://greenbytes.de/tech/webdav/
역주: 저자 주소와 URL은 2022년 발행 당시의 정보를 원문 그대로 옮긴 것이다.
김마리 (Mariel Kim)
Email: marikim.code@gmail.com
GitHub: https://github.com/marik0de