obs-text의 참조 5.6.4절 → 5.5절, 원문 규칙은 주석).역주: 용어는 glossary.md, 번역 방식은 번역 가이드를 따른다. 이 문서에서 [HTTP]는 RFC 9110(번역)을 가리킨다.
HTTP(Hypertext Transfer Protocol)는 분산·협업 하이퍼텍스트 정보 시스템을 위한 무상태(stateless, 요청 사이의 상태 정보를 유지하지 않는) 애플리케이션 수준 프로토콜이다. 이 문서는 HTTP/1.1의 메시지 구문, 메시지 파싱, 연결 관리, 그리고 관련 보안 문제를 명시한다.
이 문서는 RFC 7230의 일부를 대체한다.
이 문서는 인터넷 표준화 트랙(Standards Track) 문서다.
이 문서는 IETF(Internet Engineering Task Force)의 결과물이며 IETF 커뮤니티의 합의를 나타낸다. 이 문서는 공개 검토를 거쳤고 IESG(Internet Engineering Steering Group)의 발행 승인을 받았다. 인터넷 표준에 대한 자세한 정보는 RFC 7841의 2절에 있다.
이 문서의 현재 상태, 정오표, 의견을 보내는 방법은 https://www.rfc-editor.org/info/rfc9112 에서 확인할 수 있다.
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) 애플리케이션 수준의 요청/응답 프로토콜로, 확장 가능한 의미론(semantics)과 자기 서술적(self-descriptive) 메시지(message)를 사용하여 네트워크 기반 하이퍼텍스트 정보 시스템과 유연하게 상호 작용할 수 있게 한다. HTTP/1.1은 다음 문서로 정의된다.
이 문서는 HTTP 의미론이 HTTP/1.1 메시지 구문, 프레이밍(framing), 연결 관리 메커니즘을 통해 어떻게 전달되는지 명시한다. 이 문서의 목표는 HTTP/1.1 메시지 파서와 메시지를 전달하는 중개자(intermediary)에 대한 요구 사항을 빠짐없이 정의하는 것이다.
이 문서는 RFC 7230 가운데 HTTP/1.1 메시지 구문과 연결 관리에 관련된 부분을 대체하며, 바뀐 점은 부록 C.3에 요약되어 있다. RFC 7230의 나머지 부분은 “HTTP Semantics” [HTTP]가 대체한다.
이 문서의 키워드 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, “OPTIONAL”은 여기에 보인 것처럼 모두 대문자로 쓰였을 때, 그리고 그때에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석한다.
적합성 기준과 오류 처리에 관한 고려 사항은 [HTTP]의 2장에 정의되어 있다.
이 명세는 [RFC5234]의 ABNF(Augmented Backus-Naur Form) 표기법을 사용하며, [RFC7405]에 정의된 문자열의 대소문자 구분 표기를 더해 확장한다.
또한 [HTTP]의 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(눈에 보이는 모든 [USASCII] 문자).
아래 규칙은 [HTTP]에 정의되어 있다.
BWS = <BWS, see [HTTP], Section 5.6.3>
OWS = <OWS, see [HTTP], Section 5.6.3>
RWS = <RWS, see [HTTP], Section 5.6.3>
absolute-path = <absolute-path, see [HTTP], Section 4.1>
field-name = <field-name, see [HTTP], Section 5.1>
field-value = <field-value, see [HTTP], Section 5.5>
; 원문: obs-text = <obs-text, see [HTTP], Section 5.6.4>
obs-text = <obs-text, see [HTTP], Section 5.5>
quoted-string = <quoted-string, see [HTTP], Section 5.6.4>
token = <token, see [HTTP], Section 5.6.2>
transfer-coding =
<transfer-coding, see [HTTP], Section 10.1.4>
역주: 정오표 7744(Verified)에 따라
obs-text의 참조를 5.6.4절에서 5.5절로 고친 규칙이다(원문 규칙은 주석으로 남김).
아래 규칙은 [URI]에 정의되어 있다.
absolute-URI = <absolute-URI, see [URI], Section 4.3>
authority = <authority, see [URI], Section 3.2>
uri-host = <host, see [URI], Section 3.2.2>
port = <port, see [URI], Section 3.2.3>
query = <query, see [URI], Section 3.4>
HTTP/1.1 클라이언트(client)와 서버(server)는 메시지를 보내 통신한다. HTTP의 일반 용어와 핵심 개념은 [HTTP]의 3장을 참고한다.
HTTP/1.1 메시지는 시작 줄(start-line)과 그 뒤의 CRLF, 그리고 인터넷 메시지 형식(Internet Message Format, [RFC5322])과 비슷한 형식의 옥텟 시퀀스로 이루어진다. 이 옥텟 시퀀스는 0개 이상의 헤더 필드 줄(통틀어 “헤더(headers)” 또는 “헤더 섹션(header section)”이라고 한다), 헤더 섹션의 끝을 나타내는 빈 줄, 그리고 선택적인 메시지 본문(message body)이다.
HTTP-message = start-line CRLF
*( field-line CRLF )
CRLF
[ message-body ]
메시지는 클라이언트에서 서버로 가는 요청이거나 서버에서 클라이언트로 가는 응답이다. 구문상 두 종류의 메시지는 시작 줄과, 메시지 본문의 길이를 판단하는 알고리즘(6장)에서만 다르다. 시작 줄은 요청이면 요청 줄(request-line), 응답이면 상태 줄(status-line)이다.
start-line = request-line / status-line
이론상으로는 클라이언트가 요청을 받고 서버가 응답을 받으면서, 서로 다른 시작 줄 형식으로 둘을 구별할 수도 있다. 실제로는 서버는 요청만 기대하도록(응답은 알 수 없거나 유효하지 않은 요청 메서드(request method)로 해석된다), 클라이언트는 응답만 기대하도록 구현된다.
HTTP는 MIME(Multipurpose Internet Mail Extensions) [RFC2045]와 비슷한 프로토콜 요소(protocol element)를 일부 사용한다. HTTP 메시지와 MIME 메시지의 차이는 부록 B를 참고한다.
HTTP 메시지를 파싱하는 일반적인 절차는 시작 줄을 구조체로 읽고, 빈 줄이 나올 때까지 각 헤더 필드 줄을 필드 이름(field name)별로 해시 테이블(hash table)에 읽어 들인 다음, 파싱한 데이터로 메시지 본문이 있을지 판단하는 것이다. 메시지 본문이 있다고 표시되었다면, 메시지 본문 길이만큼의 옥텟을 읽거나 연결이 닫힐 때까지 본문을 스트림으로 읽는다.
수신자(recipient)는 HTTP 메시지를 US-ASCII([USASCII])의 초집합(superset)인 인코딩의 옥텟 시퀀스로 반드시 파싱해야 한다(MUST). 특정 인코딩을 고려하지 않고 HTTP 메시지를 유니코드 문자의 스트림으로 파싱하면, 옥텟 LF(%x0A)를 담은 유효하지 않은 멀티바이트(multibyte) 문자 시퀀스를 문자열 처리 라이브러리마다 다르게 다루기 때문에 보안 취약점이 생긴다. 문자열 기반 파서는 프로토콜 요소를 메시지에서 추출한 뒤에 그 요소 안에서만 안전하게 쓸 수 있다. 예를 들어 메시지 파싱으로 개별 필드 줄(field line)의 경계를 정한 뒤, 헤더 필드 줄 값(field line value) 안에서 쓰는 경우다.
시작 줄과 필드의 개행 문자(line terminator)는 CRLF 시퀀스이지만, 수신자는 LF 하나를 개행 문자로 인식하고 앞에 오는 CR을 무시할 수 있다(MAY).
송신자(sender)는 콘텐츠(content)를 제외한 어떤 프로토콜 요소 안에서도 단독 CR(bare CR, 바로 뒤에 LF가 오지 않는 CR 문자)을 생성해서는 안 된다(MUST NOT). 그런 단독 CR을 받은 수신자는 그 요소를 처리하거나 메시지를 전달하기 전에, 반드시 그 요소를 유효하지 않은 것으로 간주하거나 각 단독 CR을 SP로 바꿔야 한다(MUST).
오래된 HTTP/1.0 사용자 에이전트(user agent) 구현은, 줄 끝으로 끝나지 않은 메시지 본문 콘텐츠를 읽지 못하는 일부 초기 서버 애플리케이션에 대한 우회책(workaround)으로 POST 요청 뒤에 CRLF를 하나 더 보내기도 한다. HTTP/1.1 사용자 에이전트는 요청 앞이나 뒤에 CRLF를 더 붙여서는 안 된다(MUST NOT). 요청 메시지 본문을 줄 끝으로 끝내고 싶다면, 사용자 에이전트는 끝의 CRLF 옥텟을 메시지 본문 길이의 일부로 반드시 세어야 한다(MUST).
강건성(robustness)을 위해, 요청 줄을 받아 파싱할 것으로 기대하는 서버는 요청 줄 앞에 받은 빈 줄(CRLF)을 적어도 하나는 무시해야 한다(SHOULD).
송신자는 시작 줄과 첫 헤더 필드 사이에 공백을 보내서는 안 된다(MUST NOT).
시작 줄과 첫 헤더 필드 사이에 공백을 받은 수신자는 그 메시지를 유효하지 않은 것으로 거부하거나, 공백으로 시작하는 각 줄을 더 처리하지 않고 소비하는 것(즉, 올바른 형식의 헤더 필드를 받거나 헤더 섹션이 끝날 때까지, 그 줄 전체와 그 뒤에 이어지는 공백으로 시작하는 줄을 모두 무시하는 것) 중 하나를 반드시 해야 한다(MUST). 공백으로 시작하는 유효하지 않은 줄을 거부하거나 제거해야 하는 것은, 요청 스머글링(request smuggling, 11.2절)이나 응답 분할(response splitting, 11.1절) 공격에 취약할 수 있는 다운스트림(downstream) 수신자가 그 줄을 잘못 해석하지 않게 하기 위해서다.
HTTP 요청 메시지만 기다리는 서버, 또는 시작 줄로 보아 HTTP 요청 메시지로 보이는 것을 처리하는 서버가, 위에 나열한 강건성 예외를 제외하고 HTTP-message 문법에 맞지 않는 옥텟 시퀀스를 받으면, 그 서버는 400 (Bad Request) 응답으로 응답하고 연결을 닫아야 한다(SHOULD).
HTTP는 프로토콜 버전을 나타내기 위해 “
HTTP/1.x 메시지의 버전은 시작 줄의 HTTP-version 필드로 나타낸다. HTTP-version은 대소문자를 구분한다.
HTTP-version = HTTP-name "/" DIGIT "." DIGIT
HTTP-name = %s"HTTP"
HTTP/1.1 메시지를 HTTP/1.0 수신자([HTTP/1.0])나 버전을 알 수 없는 수신자에게 보낼 때는, 새 기능을 모두 무시하면 유효한 HTTP/1.0 메시지로 해석될 수 있도록 HTTP/1.1 메시지를 구성한다. 이 명세는 일부 새 기능에 수신자 버전 요구 사항을 두어, 적합한 송신자가 설정이나 메시지 수신을 통해 수신자가 HTTP/1.1을 지원한다고 판단하기 전까지는 호환되는 기능만 쓰도록 한다.
HTTP 메시지를 처리하는 중개자(즉, 터널(tunnel)로 동작하는 것을 제외한 모든 중개자)는 업스트림(upstream) 문제에 대한 우회책으로 일부러 다운그레이드(downgrade)하는 경우가 아니라면, 전달하는 메시지에 자신의 HTTP-version을 반드시 보내야 한다(MUST). 다시 말해, 중개자는 메시지의 프로토콜 버전이 자신이 메시지를 받고 보내는 양쪽 모두에서 지원하는 버전과 일치하는지 확인하지 않고 시작 줄을 무작정 전달해서는 안 된다. HTTP-version을 다시 쓰지 않고 HTTP 메시지를 전달하면, 다운스트림 수신자가 메시지 송신자의 버전을 보고 그 송신자와의 이후 통신에서 어떤 기능을 안전하게 쓸 수 있는지 판단할 때 통신 오류가 생길 수 있다.
클라이언트가 HTTP 명세를 잘못 구현해 이후 버전의 응답을 올바르게 처리하지 못한다는 것이 알려졌거나 의심되면, 서버는 HTTP/1.1 요청에 HTTP/1.0 응답을 보낼 수 있다(MAY). 예를 들어 클라이언트가 버전 번호를 올바르게 파싱하지 못하거나, 중개자가 프로토콜의 해당 부 버전을 따르지 않으면서도 HTTP-version을 무작정 전달한다고 알려진 경우다. 그런 프로토콜 다운그레이드는 특정 클라이언트 속성 때문에 일어나는 것이 아니라면 수행하지 않아야 한다(SHOULD NOT). 예를 들어 하나 이상의 요청 헤더 필드(예: User-Agent)가 오류가 있다고 알려진 클라이언트가 보내는 값과 고유하게 일치할 때다.
역주: 2.3절의 “is not allowed to”는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
요청 줄은 메서드 토큰(token)으로 시작하고, 그 뒤에 공백 하나(SP), 요청 대상(request-target), 또 하나의 공백(SP)이 오며, 프로토콜 버전으로 끝난다.
request-line = method SP request-target SP HTTP-version
request-line 문법 규칙은 각 구성 요소를 SP 옥텟 하나로 구분하도록 요구하지만, 수신자는 대신 공백으로 구분되는 단어 경계로 파싱하고, CRLF 종결자를 제외한 모든 형태의 공백을 SP 구분자로 다루면서 앞뒤 공백을 무시할 수 있다(MAY). 그런 공백에는 SP, HTAB, VT(%x0B), FF(%x0C), 단독 CR 옥텟 중 하나 이상이 포함된다. 그러나 메시지의 수신자가 여럿이고 각자 강건성을 저마다 다르게 해석한다면, 이런 관대한 파싱(lenient parsing)은 요청 스머글링 보안 취약점으로 이어질 수 있다(11.2절 참고).
[HTTP]의 2.3절에서 설명하듯, HTTP는 요청 줄의 길이에 미리 정한 제한을 두지 않는다. 자신이 구현한 어떤 메서드보다 긴 메서드를 받은 서버는 501 (Not Implemented) 상태 코드(status code)로 응답해야 한다(SHOULD). 파싱하려는 어떤 URI보다 긴 요청 대상을 받은 서버는 414 (URI Too Long) 상태 코드([HTTP]의 15.5.15절 참고)로 반드시 응답해야 한다(MUST).
실제로는 요청 줄 길이에 대한 다양한 임시방편 제한이 있다. 모든 HTTP 송신자와 수신자가 적어도 8000옥텟 길이의 요청 줄을 지원할 것을 권고한다(RECOMMENDED).
메서드 토큰은 대상 리소스(target resource)에 수행할 요청 메서드를 나타낸다. 요청 메서드는 대소문자를 구분한다.
method = token
이 명세가 정의하는 요청 메서드는 HTTP 메서드 레지스트리 정보, 새 메서드 정의에 관한 고려 사항과 함께 [HTTP]의 9장에서 볼 수 있다.
요청 대상은 요청을 적용할 대상 리소스를 식별한다. 클라이언트는 원하는 대상 URI(target URI)로부터 요청 대상을 만든다. 요청 대상에는 요청하는 메서드와 요청이 프록시(proxy)로 가는지에 따라 서로 다른 네 가지 형식이 있다.
request-target = origin-form
/ absolute-form
/ authority-form
/ asterisk-form
요청 대상에는 공백이 허용되지 않는다. 안타깝게도 일부 사용자 에이전트는 하이퍼텍스트 참조에 있는 공백을 제대로 인코딩하거나 제외하지 못해, 허용되지 않는 그 문자들이 형식이 잘못된 요청 줄의 요청 대상으로 보내진다.
유효하지 않은 요청 줄의 수신자는 400 (Bad Request) 오류로 응답하거나, 요청 대상을 올바르게 인코딩한 301 (Moved Permanently) 리다이렉트(redirect)로 응답해야 한다(SHOULD). 유효하지 않은 요청 줄은 요청 사슬을 따라 있는 보안 필터를 우회하도록 일부러 만든 것일 수 있으므로, 수신자는 리다이렉트 없이 자동으로 고쳐서 요청을 처리하려고 하지 않아야 한다(SHOULD NOT).
클라이언트는 모든 HTTP/1.1 요청 메시지에 Host 헤더 필드([HTTP]의 7.2절)를 반드시 보내야 한다(MUST). 대상 URI에 authority 구성 요소가 있으면, 클라이언트는 userinfo 하위 구성 요소와 그 “@” 구분자([HTTP]의 4.2절)를 제외하고 그 authority 구성 요소와 똑같은 Host 필드 값(field value)을 반드시 보내야 한다(MUST). 대상 URI에 authority 구성 요소가 없거나 정의되지 않았다면, 클라이언트는 필드 값이 빈 Host 헤더 필드를 반드시 보내야 한다(MUST).
서버는 Host 헤더 필드가 없는 모든 HTTP/1.1 요청 메시지, 그리고 Host 헤더 필드 줄이 둘 이상이거나 필드 값이 유효하지 않은 Host 헤더 필드를 담은 모든 요청 메시지에 400 (Bad Request) 상태 코드로 반드시 응답해야 한다(MUST).
가장 흔한 요청 대상 형식은 “origin-form”이다.
origin-form = absolute-path [ "?" query ]
원본 서버(origin server)에 직접 요청할 때, (아래에서 자세히 설명하는) CONNECT나 서버 전체 OPTIONS 요청이 아니라면, 클라이언트는 대상 URI의 절대 경로와 쿼리 구성 요소만 요청 대상으로 반드시 보내야 한다(MUST). 대상 URI의 경로 구성 요소가 비어 있으면, 클라이언트는 origin-form 요청 대상 안의 경로로 “/”를 반드시 보내야 한다(MUST). [HTTP]의 7.2절에 정의된 대로 Host 헤더 필드도 보낸다.
예를 들어 다음으로 식별되는 리소스의 표현(representation)을
http://www.example.org/where?q=now
원본 서버에서 직접 가져오려는 클라이언트는 호스트 “www.example.org”의 80번 포트로 TCP 연결을 열고(또는 재사용하고) 다음 줄을
GET /where?q=now HTTP/1.1
Host: www.example.org
보낸 뒤, 요청 메시지의 나머지를 보낸다.
프록시로 요청할 때, (아래에서 자세히 설명하는) CONNECT나 서버 전체 OPTIONS 요청이 아니라면, 클라이언트는 대상 URI를 “absolute-form”으로 요청 대상에 반드시 보내야 한다(MUST).
absolute-form = absolute-URI
프록시는 가능하면 유효한 캐시(cache)에서 그 요청을 처리하거나, 클라이언트를 대신해 다음 인바운드(inbound) 프록시 서버로, 또는 요청 대상이 가리키는 원본 서버로 직접 같은 요청을 하도록 요청받는다. 메시지의 그런 “전달(forwarding)”에 대한 요구 사항은 [HTTP]의 7.6절에 정의되어 있다.
absolute-form 요청 줄의 예는 다음과 같다.
GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1
요청 대상이 absolute-form이더라도 클라이언트는 HTTP/1.1 요청에 Host 헤더 필드를 반드시 보내야 한다(MUST). 그래야 Host를 구현하지 않았을 수 있는 오래된 HTTP/1.0 프록시를 거쳐서도 Host 정보가 전달될 수 있다.
프록시가 absolute-form 요청 대상을 가진 요청을 받으면, 프록시는 받은 Host 헤더 필드(있다면)를 반드시 무시하고 요청 대상의 호스트 정보로 바꿔야 한다(MUST). 그런 요청을 전달하는 프록시는 받은 Host 필드 값을 전달하지 말고, 받은 요청 대상을 바탕으로 새 Host 필드 값을 반드시 생성해야 한다(MUST).
원본 서버가 absolute-form 요청 대상을 가진 요청을 받으면, 원본 서버는 받은 Host 헤더 필드(있다면)를 반드시 무시하고 요청 대상의 호스트 정보를 써야 한다(MUST). 이 경우 요청 대상에 authority 구성 요소가 없으면 빈 Host 헤더 필드가 보내진다는 점에 유의한다.
대부분의 HTTP/1.1 클라이언트는 absolute-form을 프록시에만 보내지만, 서버는 요청에서 absolute-form을 반드시 받아들여야 한다(MUST).
요청 대상의 “authority-form”은 CONNECT 요청([HTTP]의 9.3.6절)에만 쓰인다. 이 형식은 터널 목적지의 uri-host와 포트 번호만으로 이루어지며, 둘은 콜론(“:”)으로 구분된다.
authority-form = uri-host ":" port
하나 이상의 프록시를 거쳐 터널을 맺기 위해 CONNECT 요청을 할 때, 클라이언트는 터널 목적지의 호스트와 포트만 요청 대상으로 반드시 보내야 한다(MUST). 클라이언트는 대상 URI의 authority 구성 요소에서 호스트와 포트를 얻는다. 다만 대상 URI에 포트가 생략되어 있으면 스킴의 기본 포트를 보낸다. 예를 들어 “http://www.example.com”에 대한 CONNECT 요청은 다음과 같다.
CONNECT www.example.com:80 HTTP/1.1
Host: www.example.com
역주: RFC 9931(2026년 3월)은
CONNECT요청에 관한 요구 사항으로 이 문서를 갱신했다. 내용은 역자 부록에 옮겼다.
요청 대상의 “asterisk-form”은 서버 전체 OPTIONS 요청([HTTP]의 9.3.7절)에만 쓰인다.
asterisk-form = "*"
클라이언트가 서버의 특정 이름 있는 리소스가 아니라 서버 전체에 대해 OPTIONS를 요청하려면, 클라이언트는 “*”(%x2A)만 요청 대상으로 반드시 보내야 한다(MUST). 예:
OPTIONS * HTTP/1.1
프록시가 URI의 경로가 비어 있고 쿼리 구성 요소가 없는 absolute-form 요청 대상을 가진 OPTIONS 요청을 받으면, 요청 사슬의 마지막 프록시는 지정된 원본 서버로 요청을 전달할 때 요청 대상으로 “*“를 반드시 보내야 한다(MUST).
예를 들어 다음 요청은
OPTIONS http://www.example.org:8001 HTTP/1.1
마지막 프록시가 호스트 “www.example.org”의 8001번 포트로 연결한 뒤 다음과 같이 전달된다.
OPTIONS * HTTP/1.1
Host: www.example.org:8001
요청 대상이 absolute-form이면 대상 URI는 요청 대상 그 자체다. 이 경우 서버는 이후 평가를 위해 URI를 일반 구성 요소로 파싱한다.
그렇지 않으면 서버는 대상 리소스를 식별하기 위해([HTTP]의 7.1절) 연결 문맥과 요청 메시지의 여러 부분으로부터 대상 URI를 재구성한다.
Host 헤더 필드의 필드 값이다. Host 헤더 필드가 없거나 그 필드 값이 비어 있거나 유효하지 않으면, 대상 URI의 authority 구성 요소는 비어 있다.absolute-URI 형식으로 다시 결합할 수 있다.예 1: 보안 연결로 받은 다음 메시지는
GET /pub/WWW/TheProject.html HTTP/1.1
Host: www.example.org
다음 대상 URI를 가진다.
https://www.example.org/pub/WWW/TheProject.html
예 2: 보안되지 않은 연결로 받은 다음 메시지는
OPTIONS * HTTP/1.1
Host: www.example.org:8080
다음 대상 URI를 가진다.
http://www.example.org:8080
대상 URI의 authority 구성 요소가 비어 있고 그 URI 스킴이 비어 있지 않은 authority를 요구한다면(“http”와 “https”가 그렇다), 서버는 요청을 거부하거나, 들어온 연결의 문맥에 맞는, 설정된 기본값이 적용되는지 판단할 수 있다. 문맥에는 주소와 포트 같은 연결 세부 정보, 어떤 보안이 적용되었는지, 그 서버 설정에 고유한, 로컬에서 정의한 정보가 포함될 수 있다. 빈 authority는 요청을 더 처리하기 전에 설정된 기본값으로 바뀐다.
사용자 에이전트가 의도한 authority가 기본값과 다를 가능성이 조금이라도 있다면, 보안 처리된 연결의 문맥 안에서 authority의 기본 이름을 제공하는 것은 본질적으로 안전하지 않다. 요청 문맥에서 authority를 고유하게 식별할 수 있는 서버는 이런 위험 없이 그 식별 정보를 기본값으로 쓸 수 있다(MAY). 또는 새 클라이언트를 얻는 방법을 설명하는 안전한 리소스로 요청을 리다이렉트하는 편이 나을 수도 있다.
클라이언트의 대상 URI를 재구성하는 것은 대상 리소스를 식별하는 과정의 절반일 뿐이라는 점에 유의한다. 나머지 절반은 [HTTP]의 7.4절에 정의된 대로, 그 대상 URI가 서버가 응답을 보낼 의향과 능력이 있는 리소스를 식별하는지 판단하는 것이다.
응답 메시지의 첫 줄은 상태 줄이며, 프로토콜 버전, 공백(SP), 상태 코드, 또 하나의 공백으로 이루어지고, 상태 코드를 설명하는 텍스트 구문으로 끝난다. 이 구문은 선택 사항이다(OPTIONAL).
status-line = HTTP-version SP status-code SP [ reason-phrase ]
status-line 문법 규칙은 각 구성 요소를 SP 옥텟 하나로 구분하도록 요구하지만, 수신자는 대신 공백으로 구분되는 단어 경계로 파싱하고, 개행 문자를 제외한 모든 형태의 공백을 SP 구분자로 다루면서 앞뒤 공백을 무시할 수 있다(MAY). 그런 공백에는 SP, HTAB, VT(%x0B), FF(%x0C), 단독 CR 옥텟 중 하나 이상이 포함된다. 그러나 메시지의 수신자가 여럿이고 각자 강건성을 저마다 다르게 해석한다면, 이런 관대한 파싱은 응답 분할 보안 취약점으로 이어질 수 있다(11.1절 참고).
status-code 요소는 대응하는 클라이언트 요청을 이해하고 충족하려는 서버의 시도 결과를 설명하는 세 자리 정수 코드다. 수신자는 그 상태 코드를 알면 그 상태 코드에 정의된 의미에 비추어, 특정 코드를 알지 못하면 그 상태 코드의 클래스(class)에 따라 응답 메시지의 나머지를 파싱하고 해석한다.
status-code = 3DIGIT
HTTP의 핵심 상태 코드는 상태 코드 클래스, 새 상태 코드 정의에 관한 고려 사항, 그런 정의를 모으는 IANA 레지스트리와 함께 [HTTP]의 15장에 정의되어 있다.
reason-phrase 요소는 숫자 상태 코드에 연관된 텍스트 설명을 제공하려는 목적 하나로만 존재한다. 이는 주로 대화형 텍스트 클라이언트에서 더 자주 쓰이던 이전 인터넷 애플리케이션 프로토콜을 존중하기 위한 것이다.
reason-phrase = 1*( HTAB / SP / VCHAR / obs-text )
클라이언트는 reason-phrase 내용을 무시해야 한다(SHOULD). 정보를 전달하는 믿을 만한 통로가 아니기 때문이다(특정 로케일(locale)에 맞게 번역되거나, 중개자가 덮어쓰거나, 다른 HTTP 버전을 거쳐 메시지가 전달될 때 버려질 수 있다). 서버는 reason-phrase가 없을 때에도(즉, 상태 줄이 공백으로 끝나게 되더라도) status-code와 reason-phrase를 구분하는 공백을 반드시 보내야 한다(MUST).
각 필드 줄은 대소문자를 구분하지 않는 필드 이름, 그 뒤의 콜론(“:”), 선택적인 앞 공백, 필드 줄 값, 선택적인 뒤 공백으로 이루어진다.
field-line = field-name ":" OWS field-value OWS
필드 값 안의 파싱 규칙은 [HTTP]의 5.5절에 정의되어 있다. 이 절은 HTTP/1.1 메시지에 헤더 필드를 넣고 메시지에서 헤더 필드를 꺼내는 일반 구문을 다룬다.
메시지는 개별 필드 이름과 무관한 일반 알고리즘으로 파싱한다. 주어진 필드 줄 값의 내용은 메시지 해석의 나중 단계(보통 메시지의 필드 섹션(field section) 전체를 처리한 뒤)까지 파싱하지 않는다.
필드 이름과 콜론 사이에는 공백이 허용되지 않는다. 과거에는 그런 공백을 처리하는 방식의 차이 때문에 요청 라우팅과 응답 처리에서 보안 취약점이 생겼다. 서버는 헤더 필드 이름과 콜론 사이에 공백이 있는 요청 메시지를 받으면 응답 상태 코드 400 (Bad Request)로 반드시 거부해야 한다(MUST). 프록시는 응답 메시지를 다운스트림으로 전달하기 전에 그런 공백을 반드시 제거해야 한다(MUST).
필드 줄 값의 앞이나 뒤, 또는 양쪽에 선택적 공백(OWS)이 올 수 있다. 사람이 일관되게 읽을 수 있도록 필드 줄 값 앞에는 SP 하나를 두는 것이 선호된다. 필드 줄 값에는 그 앞뒤 공백이 포함되지 않는다. 필드 줄 값의 공백이 아닌 첫 옥텟 앞이나 공백이 아닌 마지막 옥텟 뒤에 오는 OWS는, 파서가 필드 줄에서 필드 줄 값을 추출할 때 제외한다.
역사적으로 HTTP/1.x 필드 값은 추가되는 각 줄 앞에 공백이나 수평 탭을 적어도 하나 두어 여러 줄에 걸쳐 확장할 수 있었다(obs-fold). 이 명세는 “message/http” 미디어 유형(media type, 10.1절) 안에서를 제외하고 그런 자동 줄바꿈(line folding)을 사용 중단한다.
obs-fold = OWS CRLF RWS
; obsolete line folding
송신자는 “message/http” 미디어 유형 안에 담기 위한 메시지가 아니라면, 자동 줄바꿈을 포함한(즉, 필드 줄 값 가운데 obs-fold 규칙과 일치하는 부분이 있는) 메시지를 생성해서는 안 된다(MUST NOT).
“message/http” 컨테이너(container) 안에 있지 않은 요청 메시지에서 obs-fold를 받은 서버는, 반드시 400 (Bad Request)를 보내 그 메시지를 거부하거나(폐기된 자동 줄바꿈을 받아들일 수 없다고 설명하는 표현을 함께 보내는 것이 좋다), 필드 값을 해석하거나 메시지를 다운스트림으로 전달하기 전에 받은 각 obs-fold를 하나 이상의 SP 옥텟으로 바꿔야 한다(MUST).
“message/http” 컨테이너 안에 있지 않은 응답 메시지에서 obs-fold를 받은 프록시나 게이트웨이는, 반드시 그 메시지를 버리고 502 (Bad Gateway) 응답으로 대체하거나(받아들일 수 없는 자동 줄바꿈을 받았다고 설명하는 표현을 함께 보내는 것이 좋다), 필드 값을 해석하거나 메시지를 다운스트림으로 전달하기 전에 받은 각 obs-fold를 하나 이상의 SP 옥텟으로 바꿔야 한다(MUST).
“message/http” 컨테이너 안에 있지 않은 응답 메시지에서 obs-fold를 받은 사용자 에이전트는 필드 값을 해석하기 전에 받은 각 obs-fold를 하나 이상의 SP 옥텟으로 반드시 바꿔야 한다(MUST).
HTTP/1.1 메시지의 메시지 본문(있다면)은 요청이나 응답의 콘텐츠([HTTP]의 6.4절)를 실어 나르는 데 쓰인다. 메시지 본문은 6.1절에서 설명하듯 전송 코딩(transfer coding)이 적용되지 않았다면 콘텐츠와 같다.
message-body = *OCTET
HTTP/1.1 메시지에 메시지 본문이 있는지 판단하는 규칙은 요청과 응답에서 다르다.
요청에 메시지 본문이 있다는 것은 Content-Length나 Transfer-Encoding 헤더 필드로 알린다. 요청 메시지 프레이밍(message framing)은 메서드 의미와 무관하다.
6.3절에서 자세히 설명하듯, 응답에 메시지 본문이 있는지는 응답하는 요청의 메서드와 응답 상태 코드 둘 다에 달려 있다. 이는 HTTP 의미론이 응답 콘텐츠를 허용하는 경우([HTTP]의 6.4.1절)와 대응한다.
Transfer-Encoding 헤더 필드는 메시지 본문을 만들기 위해 콘텐츠에 적용되었거나 적용될 전송 코딩의 순서에 해당하는 전송 코딩 이름을 나열한다. 전송 코딩은 7장에 정의되어 있다.
Transfer-Encoding = #transfer-coding
; defined in [HTTP], Section 10.1.4
Transfer-Encoding은 7비트 전송 서비스 위에서 이진 데이터를 안전하게 전송할 수 있도록 설계된 MIME의 Content-Transfer-Encoding 필드([RFC2045]의 6장)와 비슷하다. 그러나 8비트를 그대로 전달하는(8bit-clean) 전송 프로토콜에서는 안전한 전송의 초점이 다르다. HTTP의 경우 Transfer-Encoding은 주로 동적으로 생성된 콘텐츠의 경계를 정확하게 정하기 위한 것이다. 또한 전송 중에만 적용되는 인코딩을 선택된 표현(selected representation)의 특성인 인코딩과 구별하는 역할도 한다.
수신자는 청크 전송 코딩(chunked transfer coding, 7.1절)을 반드시 파싱할 수 있어야 한다(MUST). 콘텐츠 크기를 미리 알 수 없을 때 메시지를 프레이밍하는 데 결정적인 역할을 하기 때문이다. 송신자는 메시지 본문에 청크 전송 코딩을 두 번 이상 적용해서는 안 된다(MUST NOT). 즉, 이미 청크로 나눈 메시지를 다시 청크로 나누는 것은 허용되지 않는다. 요청의 콘텐츠에 chunked가 아닌 전송 코딩을 적용하면, 송신자는 메시지가 올바르게 프레이밍되도록 마지막 전송 코딩으로 chunked를 반드시 적용해야 한다(MUST). 응답의 콘텐츠에 chunked가 아닌 전송 코딩을 적용하면, 송신자는 반드시 마지막 전송 코딩으로 chunked를 적용하거나 연결을 닫아 메시지를 끝내야 한다(MUST).
예를 들어
Transfer-Encoding: gzip, chunked
는 메시지 본문을 만들면서 콘텐츠를 gzip 코딩으로 압축한 다음 chunked 코딩으로 청크로 나누었음을 나타낸다.
Content-Encoding([HTTP]의 8.4.1절)과 달리 Transfer-Encoding은 표현이 아니라 메시지의 속성이다. 요청/응답 사슬을 따라 있는 수신자는 누구든, Transfer-Encoding 필드 값을 그에 맞게 바꾼다는 전제로, 받은 전송 코딩(들)을 디코딩하거나 메시지 본문에 전송 코딩(들)을 추가로 적용할 수 있다(MAY). 인코딩 매개 변수(parameter)에 관한 추가 정보는 이 명세에 정의되지 않은 다른 헤더 필드로 제공할 수 있다.
Transfer-Encoding은 HEAD 요청에 대한 응답이나 GET 요청에 대한 304 (Not Modified) 응답([HTTP]의 15.4.5절)에 보낼 수 있다(MAY). 둘 다 메시지 본문이 없지만, 요청이 조건 없는 GET이었다면 원본 서버가 메시지 본문에 전송 코딩을 적용했을 것임을 나타내기 위해서다. 그러나 응답 사슬의 어느 수신자(원본 서버 포함)든 필요 없을 때 전송 코딩을 제거할 수 있으므로, 이런 표시가 꼭 필요하지는 않다.
서버는 상태 코드가 1xx (Informational)나 204 (No Content)인 응답에 Transfer-Encoding 헤더 필드를 보내서는 안 된다(MUST NOT). 서버는 CONNECT 요청([HTTP]의 9.3.6절)에 대한 2xx (Successful) 응답에 Transfer-Encoding 헤더 필드를 보내서는 안 된다(MUST NOT).
자신이 이해하지 못하는 전송 코딩이 적용된 요청 메시지를 받은 서버는 501 (Not Implemented)로 응답해야 한다(SHOULD).
Transfer-Encoding은 HTTP/1.1에서 추가되었다. 일반적으로 HTTP/1.0만 지원한다고 알리는 구현은 전송 인코딩된 콘텐츠를 처리하는 방법을 이해하지 못할 것이고, Transfer-Encoding과 함께 받은 HTTP/1.0 메시지는 전송 중에 청크 전송 코딩을 제대로 처리하지 않은 채 전달되었을 가능성이 크다고 가정한다.
클라이언트는 서버가 HTTP/1.1 요청(또는 그 이후 부 버전)을 처리한다는 것을 알지 못하면 Transfer-Encoding을 담은 요청을 보내서는 안 된다(MUST NOT). 서버가 그런 요청을 처리한다는 것은 특정 사용자 설정으로, 또는 이전에 받은 응답의 버전을 기억해서 알 수 있다. 서버는 대응하는 요청이 HTTP/1.1(또는 그 이후 부 버전)을 나타내지 않으면 Transfer-Encoding을 담은 응답을 보내서는 안 된다(MUST NOT).
초기 Transfer-Encoding 구현은 가끔 메시지 프레이밍을 위한 청크 전송 코딩과 진행 표시줄(progress bar)에 쓸 추정 Content-Length 헤더 필드를 함께 보냈다. 그래서 Transfer-Encoding은 Content-Length와 서로 양립할 수 없는 것이 아니라 Content-Length보다 우선하는 것으로 정의되었다. 안타깝게도 다운스트림 수신자가, 특히 HTTP/1.0만 구현한 다운스트림 수신자가 이 명세에 따라 메시지를 파싱하지 못하면, 그런 메시지를 전달하는 것은 요청 스머글링(11.2절)이나 응답 분할(11.1절) 공격에 관한 취약점으로 이어질 수 있다.
서버는 Content-Length와 Transfer-Encoding을 모두 담은 요청을 거부하거나, 그런 요청을 Transfer-Encoding만 따라 처리할 수 있다(MAY). 어느 쪽이든 서버는 잠재적 공격을 피하기 위해 그런 요청에 응답한 뒤 연결을 반드시 닫아야 한다(MUST).
Transfer-Encoding 헤더 필드를 담은 HTTP/1.0 메시지를 받은 서버나 클라이언트는, Content-Length가 있더라도 반드시 그 메시지를 프레이밍에 결함이 있는 것처럼 다루고 메시지를 처리한 뒤 연결을 닫아야 한다(MUST). 메시지 송신자가 메시지의 일부를 버퍼에 남겨 두었을 수 있고, 연결을 계속 쓰면 그것이 잘못 해석될 수 있기 때문이다.
메시지에 Transfer-Encoding 헤더 필드가 없으면, Content-Length 헤더 필드([HTTP]의 8.6절)는 있을 수 있는 콘텐츠의 예상 크기를 옥텟 수의 십진수로 제공할 수 있다. 콘텐츠가 있는 메시지에서 Content-Length 필드 값은 데이터(와 메시지)가 어디서 끝나는지 판단하는 데 필요한 프레이밍 정보를 제공한다. 콘텐츠가 없는 메시지에서 Content-Length는 선택된 표현의 크기를 나타낸다([HTTP]의 8.6절).
송신자는 Transfer-Encoding 헤더 필드를 담은 메시지에 Content-Length 헤더 필드를 보내서는 안 된다(MUST NOT).
참고: HTTP가 메시지 프레이밍에
Content-Length를 쓰는 방식은 MIME에서 같은 필드를 쓰는 방식과 크게 다르다. MIME에서 이 필드는 “message/external-body” 미디어 유형 안에서만 쓰이는 선택적 필드다.
메시지 본문의 길이는 다음 중 하나로 정한다(우선순위 순).
HEAD 요청에 대한 모든 응답과 상태 코드가 1xx (Informational), 204 (No Content), 304 (Not Modified)인 모든 응답은, 메시지에 어떤 헤더 필드가 있든 항상 헤더 필드 뒤의 첫 빈 줄에서 끝나므로 메시지 본문이나 트레일러 섹션(trailer section)을 담을 수 없다.CONNECT 요청에 대한 모든 2xx (Successful) 응답은 헤더 필드를 끝내는 빈 줄 바로 뒤에 연결이 터널이 된다는 뜻이다. 클라이언트는 그런 메시지에서 받은 Content-Length나 Transfer-Encoding 헤더 필드를 모두 반드시 무시해야 한다(MUST).Transfer-Encoding과 Content-Length 헤더 필드를 모두 담은 채 받으면, Transfer-Encoding이 Content-Length보다 우선한다. 그런 메시지는 요청 스머글링(11.2절)이나 응답 분할(11.1절)을 시도하는 것일 수 있으며, 오류로 처리해야 마땅하다. 그 메시지를 전달하기로 한 중개자는 메시지를 다운스트림으로 전달하기 전에 반드시 먼저 받은 Content-Length 필드를 제거하고 (아래에서 설명하듯) Transfer-Encoding을 처리해야 한다(MUST).Transfer-Encoding 헤더 필드가 있고 청크 전송 코딩(7.1절)이 마지막 인코딩이면, 메시지 본문 길이는 전송 코딩이 데이터가 끝났다고 나타낼 때까지 청크 데이터를 읽고 디코딩해서 정한다.
응답에 Transfer-Encoding 헤더 필드가 있고 청크 전송 코딩이 마지막 인코딩이 아니면, 메시지 본문 길이는 서버가 연결을 닫을 때까지 연결을 읽어서 정한다.
요청에 Transfer-Encoding 헤더 필드가 있고 청크 전송 코딩이 마지막 인코딩이 아니면, 메시지 본문 길이를 믿을 만하게 정할 수 없다. 서버는 400 (Bad Request) 상태 코드로 응답한 다음 연결을 반드시 닫아야 한다(MUST).
Transfer-Encoding 없이, 유효하지 않은 Content-Length 헤더 필드와 함께 받으면 메시지 프레이밍이 유효하지 않으므로, 수신자는 그것을 복구할 수 없는 오류로 반드시 다뤄야 한다(MUST). 다만 필드 값을 쉼표로 구분된 목록([HTTP]의 5.6.1절)으로 성공적으로 파싱할 수 있고, 목록의 값이 모두 유효하며, 목록의 값이 모두 같다면 예외다(이 경우 그 단일 값을 Content-Length 필드 값으로 써서 메시지를 처리한다). 복구할 수 없는 오류가 요청 메시지에 있으면, 서버는 400 (Bad Request) 상태 코드로 응답한 다음 연결을 반드시 닫아야 한다(MUST). 프록시가 받은 응답 메시지에 있으면, 프록시는 반드시 서버와의 연결을 닫고, 받은 응답을 버리고, 클라이언트에 502 (Bad Gateway) 응답을 보내야 한다(MUST). 사용자 에이전트가 받은 응답 메시지에 있으면, 사용자 에이전트는 반드시 서버와의 연결을 닫고 받은 응답을 버려야 한다(MUST).Transfer-Encoding 없이 유효한 Content-Length 헤더 필드가 있으면, 그 십진 값이 예상 메시지 본문 길이를 옥텟 단위로 정한다. 표시된 옥텟 수를 다 받기 전에 송신자가 연결을 닫거나 수신자의 시간이 초과되면, 수신자는 반드시 그 메시지를 불완전한 것으로 간주하고 연결을 닫아야 한다(MUST).성공적으로 완료되었고 연결 종료로 끝이 구분되는(close-delimited) 응답 메시지를, 네트워크 장애로 중단되어 일부만 받은 메시지와 구별할 방법이 없으므로, 서버는 가능하면 언제나 인코딩이나 길이로 끝이 구분되는(length-delimited) 메시지를 생성해야 한다(SHOULD). 연결 종료로 끝을 구분하는 기능은 주로 HTTP/1.0과의 하위 호환을 위해 존재한다.
참고: 요청 메시지는 연결 종료로 끝이 구분되는 일이 없다. 요청 메시지는 언제나 길이나 전송 코딩으로 명시적으로 프레이밍되며, 둘 다 없으면 요청이 헤더 섹션 바로 뒤에서 끝난다는 뜻이기 때문이다.
서버는 메시지 본문은 있지만 Content-Length가 없는 요청을 411 (Length Required)로 응답해 거부할 수 있다(MAY).
chunked가 아닌 전송 코딩을 적용한 경우가 아니라면, 메시지 본문을 담은 요청을 보내는 클라이언트는 메시지 본문 길이를 미리 알 때 청크 전송 코딩 대신 유효한 Content-Length 헤더 필드를 써야 한다(SHOULD). 일부 기존 서비스는 청크 전송 코딩을 이해하면서도 chunked에 411 (Length Required) 상태 코드로 응답하기 때문이다. 이는 보통 그런 서비스가 호출되기 전에 콘텐츠 길이를 요구하는 게이트웨이를 통해 구현되어 있고, 서버가 처리하기 전에 요청 전체를 버퍼링할 수 없거나 그럴 의향이 없기 때문이다.
메시지 본문을 담은 요청을 보내는 사용자 에이전트는 반드시 유효한 Content-Length 헤더 필드를 보내거나 청크 전송 코딩을 써야 한다(MUST). 클라이언트는 서버가 HTTP/1.1(또는 그 이후) 요청을 처리한다는 것을 알지 못하면 청크 전송 코딩을 써서는 안 된다(MUST NOT). 서버가 그런 요청을 처리한다는 것은 특정 사용자 설정으로, 또는 이전에 받은 응답의 버전을 기억해서 알 수 있다.
연결의 마지막 요청에 대한 최종 응답을 모두 받았는데 읽을 데이터가 더 남아 있으면, 사용자 에이전트는 남은 데이터를 버리거나, 그 데이터가 이전 메시지 본문의 일부인지 판단하려고 시도할 수 있다(MAY). 이전 메시지의 Content-Length 값이 틀렸다면 그럴 수 있다. 클라이언트는 그런 추가 데이터를 별도의 응답으로 처리하거나, 캐시하거나, 전달해서는 안 된다(MUST NOT). 그렇게 하면 캐시 중독(cache poisoning)에 취약해지기 때문이다.
역주: 6장의 “ought to”(6.3절 3항)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
전송 코딩 이름은 네트워크를 통한 “안전한 전송”을 보장하기 위해 메시지의 콘텐츠에 적용되었거나, 적용될 수 있거나, 적용되어야 할 수도 있는 인코딩 변환을 나타내는 데 쓰인다. 전송 코딩은 전송되는 표현의 속성이 아니라 메시지의 속성이라는 점에서 콘텐츠 코딩(content coding)과 다르다.
모든 전송 코딩 이름은 대소문자를 구분하지 않으며, 7.3절에 정의된 HTTP 전송 코딩 레지스트리(HTTP Transfer Coding Registry)에 등록되어야 마땅하다. 전송 코딩 이름은 Transfer-Encoding(6.1절) 헤더 필드와 TE([HTTP]의 10.1.4절) 헤더 필드에서 쓰인다(TE 쪽에서 transfer-coding 문법도 정의한다).
청크 전송 코딩은 콘텐츠를 각자 크기 표시를 가진 일련의 청크(chunk)로 감싸서 전송하며, 그 뒤에 트레일러 필드(trailer field)를 담은 트레일러 섹션이 올 수 있다. 이 트레일러 섹션은 선택 사항이다(OPTIONAL). 청크 방식을 쓰면 크기를 알 수 없는 콘텐츠 스트림을 길이로 끝이 구분되는 버퍼의 연속으로 전송할 수 있으므로, 송신자는 연결 지속성을 유지할 수 있고 수신자는 메시지 전체를 언제 다 받았는지 알 수 있다.
chunked-body = *chunk
last-chunk
trailer-section
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
chunk-size = 1*HEXDIG
last-chunk = 1*("0") [ chunk-ext ] CRLF
chunk-data = 1*OCTET ; a sequence of chunk-size octets
chunk-size 필드는 chunk-data의 크기를 옥텟 단위로 나타내는 16진수 문자열이다. 청크 전송 코딩은 chunk-size가 0인 청크를 받으면 완료되며, 그 뒤에 트레일러 섹션이 올 수 있고, 마지막은 빈 줄로 끝난다.
수신자는 청크 전송 코딩을 반드시 파싱하고 디코딩할 수 있어야 한다(MUST).
HTTP/1.1은 중개자가 응답 전체를 버퍼에 담을 수 있다고 확신할 수 있도록 청크 응답의 크기를 제한하는 수단을 정의하지 않는다. 또한 매우 큰 청크 크기는 받는 구현에서 그 값을 정확히 표현하지 못하면 오버플로나 정밀도 손실을 일으킬 수 있다. 따라서 수신자는 매우 큰 16진수 숫자가 올 수 있음을 반드시 예상하고, 정수 변환 오버플로로 인한 파싱 오류와 정수 표현으로 인한 정밀도 손실을 막아야 한다(MUST).
청크 코딩은 어떤 매개 변수도 정의하지 않는다. 매개 변수가 있으면 오류로 처리해야 한다(SHOULD).
청크 코딩에서는 청크별 메타데이터(서명이나 해시 등), 메시지 중간의 제어 정보, 메시지 본문 크기의 무작위화를 제공하기 위해, 각 청크의 chunk-size 바로 뒤에 0개 이상의 청크 확장(chunk extension)을 넣을 수 있다.
chunk-ext = *( BWS ";" BWS chunk-ext-name
[ BWS "=" BWS chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token / quoted-string
청크 코딩은 연결마다 따로 적용되므로, 상위 애플리케이션이 확장을 살펴볼 기회를 얻기 전에 각 수신자(중개자 포함)가 이를 제거하거나 다시 코딩할 가능성이 높다. 그래서 청크 확장은 보통 클라이언트와 서버가 청크 확장의 사용에 관한 기대를 공유할 수 있는 “롱 폴링(long polling)” 같은 특수한 HTTP 서비스나, 종단 간(end-to-end) 보안 처리된 연결 안의 패딩(padding)에만 쓰인다.
수신자는 인식하지 못하는 청크 확장을 반드시 무시해야 한다(MUST). 서버는 메시지의 다른 부분에 길이 제한과 타임아웃을 적용하는 것과 같은 방식으로, 요청에서 받은 청크 확장의 전체 길이를 제공하는 서비스에 맞는 합리적인 양으로 제한하고, 그 양을 넘으면 적절한 4xx (Client Error) 응답을 생성해야 마땅하다.
트레일러 섹션을 쓰면 송신자는 콘텐츠를 보내는 동안 동적으로 생성될 수 있는 메타데이터(메시지 무결성 검사, 전자 서명, 후처리 상태 등)를 제공하기 위해 청크 메시지 끝에 필드를 더 넣을 수 있다. 트레일러 필드의 올바른 사용법과 제한은 [HTTP]의 6.5절에 정의되어 있다.
trailer-section = *( field-line CRLF )
메시지에서 청크 코딩을 제거하는 수신자는 받은 트레일러 필드를 선택적으로 유지하거나 버릴 수 있다(MAY). 받은 트레일러 필드를 유지하는 수신자는 반드시 그 트레일러 필드를 받은 헤더 필드와 따로 저장·전달하거나, 받은 트레일러 필드를 헤더 섹션에 병합해야 한다(MUST). 수신자는 대응하는 헤더 필드 정의가 병합을 명시적으로 허용하고 트레일러 필드 값을 안전하게 병합하는 방법을 지시하지 않는 한, 받은 트레일러 필드를 헤더 섹션에 병합해서는 안 된다(MUST NOT).
청크 전송 코딩을 디코딩하는 과정은 의사 코드(pseudo-code)로 다음과 같이 나타낼 수 있다.
length := 0
read chunk-size, chunk-ext (if any), and CRLF
while (chunk-size > 0) {
read chunk-data and CRLF
append chunk-data to content
length := length + chunk-size
read chunk-size, chunk-ext (if any), and CRLF
}
read trailer field
while (trailer field is not empty) {
if (trailer fields are stored/forwarded separately) {
append trailer field to existing trailer fields
}
else if (trailer field is understood and defined as mergeable) {
merge trailer field with existing header fields
}
else {
discard trailer field
}
read trailer field
}
Content-Length := length
Remove "chunked" from Transfer-Encoding
다음 압축용 전송 코딩 이름은 대응하는 콘텐츠 코딩과 같은 알고리즘으로 정의된다.
압축 코딩은 어떤 매개 변수도 정의하지 않는다. 이 압축 코딩 중 어느 것에든 매개 변수가 있으면 오류로 처리해야 한다(SHOULD).
“HTTP 전송 코딩 레지스트리”는 전송 코딩 이름의 네임스페이스를 정의한다. 이 레지스트리는 https://www.iana.org/assignments/http-parameters에서 관리된다.
등록에는 다음 항목이 반드시 포함되어야 한다(MUST).
전송 코딩의 이름은, 7.2절에 정의된 압축 코딩처럼 인코딩 변환이 동일한 경우가 아니라면, 콘텐츠 코딩([HTTP]의 8.4.1절)의 이름과 겹쳐서는 안 된다(MUST NOT).
TE 헤더 필드([HTTP]의 10.1.4절)는 여러 전송 코딩을 받아들일 수 있을 때 순위 값으로 “q”라는 의사 매개 변수(pseudo-parameter)를 쓴다. 앞으로 등록하는 전송 코딩은 모호함을 피하기 위해 (대소문자를 구분하지 않고) “q”라는 이름의 매개 변수를 정의하지 않아야 한다(SHOULD NOT).
이 네임스페이스에 값을 추가하려면 IETF Review가 필요하며([RFC8126]의 4.8절 참고), 이 명세에 정의된 전송 코딩의 목적에 반드시 부합해야 한다(MUST).
인코딩 형식을 식별하는 데 프로그램 이름을 쓰는 것은 바람직하지 않으며, 앞으로의 인코딩에서는 권장하지 않는다.
HTTP/1.1에서 TE 필드([HTTP]의 10.1.4절)는 클라이언트가 응답에서 청크 외에 어떤 전송 코딩을 받아들일 의향이 있는지, 그리고 청크 전송 코딩에서 트레일러 필드를 보존할 의향이 있는지를 나타내는 데 쓰인다.
클라이언트는 TE에 청크 전송 코딩 이름을 보내서는 안 된다(MUST NOT). HTTP/1.1 수신자에게 청크는 언제나 받아들일 수 있는 것이다.
다음은 TE를 쓰는 세 가지 예다.
TE: deflate
TE:
TE: trailers, deflate;q=0.5
여러 전송 코딩을 받아들일 수 있을 때, 클라이언트는 대소문자를 구분하지 않는 “q” 매개 변수로 선호에 따라 코딩의 순위를 매길 수 있다(MAY). 이 매개 변수는 콘텐츠 협상(content negotiation) 필드에서 쓰는 qvalue와 비슷하다([HTTP]의 12.4.2절 참고). 순위 값은 0부터 1까지 범위의 실수이며, 0.001이 가장 덜 선호되고 1이 가장 선호된다. 0이라는 값은 “받아들일 수 없음”을 뜻한다.
TE 필드 값이 비어 있거나 TE 필드가 없으면, 받아들일 수 있는 전송 코딩은 청크뿐이다. 전송 코딩이 없는 메시지는 언제나 받아들일 수 있다.
키워드 “trailers”는 [HTTP]의 6.5절에 설명된 대로 송신자가 트레일러 필드를 버리지 않을 것임을 나타낸다.
TE 헤더 필드는 바로 이웃한 연결에만 적용되므로, TE를 보내는 송신자는 그 의미를 지원하지 않는 중개자가 TE 헤더 필드를 전달하지 않도록 Connection 헤더 필드([HTTP]의 7.6.1절) 안에 “TE” 연결 옵션도 반드시 보내야 한다(MUST).
역주: 7장의 “ought to”(7장 첫머리, 7.1.1절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
보통 요청이 취소되었거나 타임아웃 예외가 발생해서 불완전한 요청 메시지를 받은 서버는 연결을 닫기 전에 오류 응답을 보낼 수 있다(MAY).
연결이 일찍 닫히거나 청크 전송 코딩으로 보이는 것의 디코딩에 실패할 때 생길 수 있는 불완전한 응답 메시지를 받은 클라이언트는 그 메시지를 반드시 불완전한 것으로 기록해야 한다(MUST). 불완전한 응답에 대한 캐시 요구 사항은 [CACHING]의 3.3절에 정의되어 있다.
응답이 헤더 섹션 중간에서(빈 줄을 받기 전에) 끝났고 상태 코드가 응답의 온전한 의미를 전달하는 데 헤더 필드에 의존할 수 있다면, 클라이언트는 그 의미가 전달되었다고 가정할 수 없다. 클라이언트는 다음에 어떤 동작을 할지 정하기 위해 요청을 반복해야 할 수도 있다.
청크 전송 코딩을 쓰는 메시지 본문은 인코딩을 끝내는 크기 0의 청크를 받지 못했으면 불완전하다. 유효한 Content-Length를 쓰는 메시지는 받은 메시지 본문의 크기(옥텟 단위)가 Content-Length로 주어진 값보다 작으면 불완전하다. 청크 전송 코딩도 Content-Length도 없는 응답은 연결 종료로 끝나며, 헤더 섹션을 온전히 받았다면 기반 연결이 오류를 알리지 않는 한 완전한 것으로 간주한다(예를 들어 TLS의 “불완전한 종료(incomplete close)”는 9.8절에서 설명하듯 응답을 불완전한 상태로 남긴다).
HTTP 메시지 구문은 기반 전송 계층 또는 세션 계층 연결 프로토콜과 무관하다. HTTP는 요청이 순서대로 전달되고 그에 대응하는 응답도 순서대로 전달되는 신뢰할 수 있는 전송만을 전제한다. HTTP 요청과 응답 구조를 기반 전송 프로토콜의 데이터 단위에 매핑하는 것은 이 명세의 범위를 벗어난다.
[HTTP]의 7.3절에서 설명하듯, HTTP 상호 작용에 쓰일 구체적인 연결 프로토콜은 클라이언트 설정과 대상 URI로 정해진다. 예를 들어 “http” URI 스킴([HTTP]의 4.2.1절)은 기본 TCP 포트 80을 쓰는 IP 위의 TCP를 기본 연결로 나타내지만, 클라이언트는 다른 연결, 포트, 프로토콜을 거쳐 프록시를 쓰도록 설정되어 있을 수도 있다.
HTTP 구현은 연결 관리를 수행할 것으로 기대된다. 연결 관리에는 현재 연결의 상태 유지, 새 연결 수립이나 기존 연결 재사용, 연결에서 받은 메시지 처리, 연결 장애 감지, 각 연결 닫기가 포함된다. 대부분의 클라이언트는 서버 엔드포인트 하나에 둘 이상의 연결을 두는 것을 포함해 여러 연결을 병렬로 유지한다. 대부분의 서버는 공정한 사용을 가능하게 하고 서비스 거부 공격을 감지하기 위해 요청 큐를 제어하면서 수천 개의 동시 연결을 유지하도록 설계된다.
여러 전송 계층 또는 세션 계층 프로토콜로 연결을 수립하는 방법을 설명하는 것은 이 명세의 범위를 벗어난다. 각 HTTP 연결은 기반 전송 연결 하나에 대응한다.
HTTP/1.1에는 주어진 요청 메시지를 그에 대응하는 하나 이상의 응답 메시지와 연관시키는 요청 식별자가 없다. 그래서 응답이 도착하는 순서가 같은 연결에서 요청을 보낸 순서와 정확히 일치한다는 데 의존한다. 요청 하나에 응답 메시지가 둘 이상인 경우는 같은 요청에 대한 최종 응답 앞에 하나 이상의 정보 응답(1xx, [HTTP]의 15.2절 참고)이 올 때뿐이다.
한 연결에 미처리 요청이 둘 이상 있는 클라이언트는 미처리 요청 목록을 보낸 순서대로 반드시 유지해야 하며(MUST), 그 연결에서 받은 각 응답 메시지를 아직 최종(1xx가 아닌) 응답을 받지 못한 첫 번째 미처리 요청에 반드시 연관시켜야 한다(MUST).
클라이언트가 미처리 요청이 없는 연결에서 데이터를 받으면, 클라이언트는 그 데이터를 유효한 응답으로 간주해서는 안 된다(MUST NOT). 데이터가 하나 이상의 CRLF로만 이루어진 경우(이는 2.2절에 따라 버릴 수 있다)가 아니라면, 이제 메시지 구분이 모호해졌으므로 클라이언트는 연결을 닫아야 한다(SHOULD).
HTTP/1.1은 기본적으로 “지속 연결(persistent connection)”을 사용하므로, 연결 하나로 여러 요청과 응답을 주고받을 수 있다. HTTP 구현은 지속 연결을 지원해야 한다(SHOULD).
수신자는 연결이 지속되는지를 가장 최근에 받은 메시지의 프로토콜 버전과 (있다면) Connection 헤더 필드([HTTP]의 7.6.1절)에 근거해 판단한다.
지속 연결을 지원하지 않는 클라이언트는 모든 요청 메시지에 “close” 연결 옵션을 반드시 보내야 한다(MUST).
지속 연결을 지원하지 않는 서버는 상태 코드가 1xx (Informational)이 아닌 모든 응답 메시지에 “close” 연결 옵션을 반드시 보내야 한다(MUST).
클라이언트는 “close” 연결 옵션을 보내거나 받을 때까지, 또는 “keep-alive” 연결 옵션이 없는 HTTP/1.0 응답을 받을 때까지 지속 연결에서 요청을 더 보낼 수 있다(MAY).
연결이 지속되려면 그 연결의 모든 메시지가 6장에서 설명하듯 스스로 정의된 메시지 길이(즉, 연결 종료로 정해지지 않는 길이)를 가져야 한다. 서버는 반드시 요청 메시지 본문 전체를 읽거나, 응답을 보낸 뒤 연결을 닫아야 한다(MUST). 그러지 않으면 지속 연결에 남은 데이터가 다음 요청으로 잘못 해석된다. 마찬가지로 클라이언트는 같은 연결을 다음 요청에 재사용하려면 응답 메시지 본문 전체를 반드시 읽어야 한다(MUST).
프록시 서버는 HTTP/1.0 클라이언트와 지속 연결을 유지해서는 안 된다(MUST NOT). 많은 HTTP/1.0 클라이언트가 구현한 Keep-Alive 헤더 필드의 문제에 관한 정보와 논의는 부록 C.2.2를 참고하라.
HTTP/1.0 클라이언트와의 하위 호환성에 관한 더 많은 정보는 부록 C.2.2를 참고하라.
연결은 의도와 관계없이 언제든 닫힐 수 있다. 구현은 비동기적인 종료 이벤트로부터 복구해야 할 필요를 예상해야 마땅하다. 클라이언트가 미처리 요청의 연속을 자동으로 재시도할 수 있는 조건은 [HTTP]의 9.2.2절에 정의되어 있다.
지속 연결을 지원하는 클라이언트는 각 응답을 기다리지 않고 여러 요청을 보내는 방식으로 요청을 “파이프라이닝(pipeline)”할 수 있다(MAY). 서버는 파이프라이닝된 요청들이 모두 안전한 메서드([HTTP]의 9.2.1절)를 쓴다면 그 요청들을 병렬로 처리할 수 있지만(MAY), 대응하는 응답은 요청을 받은 순서와 같은 순서로 반드시 보내야 한다(MUST).
요청을 파이프라이닝하는 클라이언트는 대응하는 응답을 모두 받기 전에 연결이 닫히면 응답받지 못한 요청을 재시도해야 한다(SHOULD). 연결이 실패한 뒤(서버가 마지막 완전한 응답에서 명시적으로 닫지 않은 연결) 파이프라이닝된 요청을 재시도할 때, 클라이언트는 연결을 수립한 직후에 파이프라이닝해서는 안 된다(MUST NOT). 이전 파이프라인에서 남은 첫 요청이 오류 응답을 일으켰을 수 있는데, 일찍 닫힌 연결에 여러 요청을 보내면 그 오류 응답을 다시 잃을 수 있기 때문이다(9.6절에서 설명하는 TCP 리셋 문제 참고).
멱등(idempotent) 메서드([HTTP]의 9.2.2절)는 연결 실패 뒤에 자동으로 재시도할 수 있으므로 파이프라이닝에서 중요하다. 사용자 에이전트는 파이프라이닝된 연속과 관련된 부분 실패 상황을 감지하고 복구할 수단이 없다면, 멱등이 아닌 메서드 뒤에는 그 메서드의 최종 응답 상태 코드를 받을 때까지 요청을 파이프라이닝하지 않아야 한다(SHOULD NOT).
파이프라이닝된 요청을 받은 중개자는 그 요청을 인바운드로 전달할 때 파이프라이닝할 수 있다(MAY). 어떤 요청을 안전하게 파이프라이닝할 수 있는지는 아웃바운드 쪽 사용자 에이전트가 정한다고 믿을 수 있기 때문이다. 응답을 받기 전에 인바운드 연결이 실패하면, 파이프라이닝하는 중개자는 아직 응답을 받지 못한 요청이 모두 멱등 메서드를 쓴다면 그 요청의 연속을 재시도해 볼 수 있다(MAY). 그렇지 않으면 파이프라이닝하는 중개자는 받은 응답을 모두 전달한 뒤, 아웃바운드 쪽 사용자 에이전트가 그에 맞게 복구할 수 있도록 대응하는 아웃바운드 연결을 닫아야 한다(SHOULD).
클라이언트는 주어진 서버에 대해 동시에 열어 두는 연결 수를 제한해야 마땅하다.
이전 HTTP 개정판은 연결 수의 상한으로 특정 숫자를 제시했지만, 이는 많은 애플리케이션에서 비현실적인 것으로 드러났다. 그래서 이 명세는 특정 최대 연결 수를 의무화하지 않고, 대신 클라이언트가 여러 연결을 열 때 보수적으로 행동하도록 권장한다.
여러 연결은 보통 “HOL 블로킹(head-of-line blocking)” 문제를 피하기 위해 쓰인다. 이는 서버 측 처리가 많이 필요하거나 매우 큰 콘텐츠를 전송하는(또는 둘 다인) 요청이 같은 연결의 후속 요청을 막는 문제다. 그러나 각 연결은 서버 자원을 소비한다.
게다가 여러 연결을 쓰면 혼잡한 네트워크에서 바람직하지 않은 부작용이 생길 수 있다. 연결 수가 많아지면 혼잡하지 않은 네트워크에서도 부작용이 생길 수 있다. 연결들의 송신 동작이 합쳐지고 처음부터 동기화되어 있어서, 더 적은 병렬 연결을 썼다면 없었을 혼잡을 일으킬 수 있기 때문이다.
서버는 남용으로 판단하거나 서비스 거부 공격의 특징으로 보이는 트래픽(예: 한 클라이언트가 지나치게 많은 연결을 여는 것)을 거부할 수도 있다는 점에 유의하라.
서버에는 보통 그 시간이 지나면 비활성 연결을 더 이상 유지하지 않는 어떤 타임아웃 값이 있다. 프록시 서버는 클라이언트가 같은 프록시 서버를 거쳐 더 많은 연결을 맺을 가능성이 높으므로 이 값을 더 크게 잡을 수도 있다. 지속 연결을 쓴다고 해서 클라이언트나 서버의 이 타임아웃 길이(또는 존재)에 대한 요구 사항이 생기지는 않는다.
타임아웃하려는 클라이언트나 서버는 연결에 우아한 종료(graceful close)를 수행해야 한다(SHOULD). 연결 양쪽을 신속하게 닫으면 할당된 시스템 자원을 회수할 수 있으므로, 구현은 열린 연결에서 종료 신호를 받는지 계속 감시하고 적절히 대응해야 한다(SHOULD).
클라이언트, 서버, 프록시는 언제든 전송 연결을 닫을 수 있다(MAY). 예를 들어 서버가 “유휴” 연결을 닫기로 결정한 바로 그때 클라이언트가 새 요청을 보내기 시작했을 수 있다. 서버 입장에서는 유휴 상태인 연결을 닫는 것이지만, 클라이언트 입장에서는 요청이 진행 중이다.
서버는 가능하면 지속 연결을 유지하고, 클라이언트가 재시도하리라 기대하고 연결을 끊기보다 기반 전송의 흐름 제어 메커니즘이 일시적 과부하를 해소하게 두어야 한다(SHOULD). 연결을 끊는 방식은 네트워크 혼잡이나 서버 부하를 악화할 수 있다.
메시지 본문을 보내는 클라이언트는 요청을 전송하는 동안 네트워크 연결에서 오류 응답이 오는지 감시해야 한다(SHOULD). 서버가 메시지 본문을 받을 생각이 없고 연결을 닫고 있음을 나타내는 응답을 보면, 클라이언트는 본문 전송을 즉시 멈추고 자기 쪽 연결을 닫아야 한다(SHOULD).
“close” 연결 옵션은 송신자가 응답을 완료한 뒤 이 연결을 닫겠다는 신호로 정의된다. 송신자는 연결을 닫으려 할 때 “close” 연결 옵션을 담은 Connection 헤더 필드([HTTP]의 7.6.1절)를 보내야 한다(SHOULD). 예를 들어 다음 필드는
Connection: close
요청 헤더 필드로 쓰이면 클라이언트가 이 연결에서 보내는 마지막 요청임을 나타내고, 응답에서는 응답 메시지가 완료된 뒤 서버가 이 연결을 닫을 것임을 나타낸다.
필드 이름 “Close”는 예약되어 있다는 점에 유의하라. 그 이름을 헤더 필드로 쓰면 “close” 연결 옵션과 충돌할 수 있기 때문이다.
“close” 연결 옵션을 보내는 클라이언트는 그 연결에서 (“close”를 담은 요청 뒤에) 요청을 더 보내서는 안 되며(MUST NOT), 이 요청에 대응하는 최종 응답 메시지를 읽은 뒤 연결을 반드시 닫아야 한다(MUST).
“close” 연결 옵션을 받은 서버는 “close” 연결 옵션을 담은 요청에 대한 최종 응답을 보낸 뒤 연결 종료(아래 참고)를 반드시 시작해야 한다(MUST). 서버는 그 연결의 최종 응답에 “close” 연결 옵션을 보내야 한다(SHOULD). 서버는 그 연결에서 받은 이후 요청을 처리해서는 안 된다(MUST NOT).
“close” 연결 옵션을 보내는 서버는 “close” 연결 옵션을 담은 응답을 보낸 뒤 연결 종료(아래 참고)를 반드시 시작해야 한다(MUST). 서버는 그 연결에서 받은 이후 요청을 처리해서는 안 된다(MUST NOT).
“close” 연결 옵션을 받은 클라이언트는 반드시 그 연결에서 요청 보내기를 멈추고, “close” 연결 옵션을 담은 응답 메시지를 읽은 뒤 연결을 닫아야 한다(MUST). 그 연결에 파이프라이닝된 요청을 더 보냈다면, 클라이언트는 서버가 그 요청을 처리하리라고 가정하지 않아야 한다(SHOULD NOT).
서버가 TCP 연결을 즉시 닫으면 클라이언트가 마지막 HTTP 응답을 읽지 못할 위험이 크다. 서버가 완전히 닫힌 연결에서 클라이언트로부터 데이터를 더 받으면(예: 클라이언트가 서버의 응답을 받기 전에 보낸 다른 요청), 서버의 TCP 스택은 클라이언트에 리셋 패킷을 보낸다. 불행히도 리셋 패킷은 클라이언트의 HTTP 파서가 읽고 해석하기 전에 클라이언트에서 아직 확인 응답하지 않은 입력 버퍼를 지울 수 있다.
TCP 리셋 문제를 피하기 위해 서버는 보통 연결을 단계적으로 닫는다. 먼저 서버는 읽기/쓰기 연결의 쓰기 쪽만 닫는 하프 클로즈(half-close)를 수행한다. 그런 다음 서버는 클라이언트가 대응하는 종료를 할 때까지, 또는 서버 자신의 TCP 스택이 서버의 마지막 응답을 담은 패킷에 대한 클라이언트의 확인 응답을 받았다고 합리적으로 확신할 때까지 연결에서 계속 읽는다. 마지막으로 서버는 연결을 완전히 닫는다.
리셋 문제가 TCP에만 해당하는지, 다른 전송 연결 프로토콜에서도 나타날 수 있는지는 알려져 있지 않다.
클라이언트가 하프 클로즈한 TCP 연결은 요청 메시지의 끝을 구분하지 않으며, 클라이언트가 더 이상 응답에 관심이 없다는 뜻도 아니라는 점에 유의하라. HTTP/1.1은 전송과 무관하므로, 일반적으로 경계 상황을 알리는 데 전송 신호에 의존할 수 없다.
개념적으로 HTTP/TLS는 단순히 TLS([TLS13])로 보안 처리된 연결 위에서 HTTP 메시지를 보내는 것이다.
HTTP 클라이언트는 TLS 클라이언트 역할도 한다. 클라이언트는 적절한 포트로 서버에 연결을 시작하고, TLS 핸드셰이크(handshake)를 시작하기 위해 TLS ClientHello를 보낸다. TLS 핸드셰이크가 끝나면 클라이언트는 첫 HTTP 요청을 시작할 수 있다. 모든 HTTP 데이터는 반드시 TLS “애플리케이션 데이터(application data)”로 보내야 하지만(MUST), 그 밖에는 HTTP의 일반 연결처럼 다룬다(지속 연결로 재사용될 수 있는 것도 포함해서).
TLS는 안전한 연결 종료를 위해 (오류가 아닌) 연결 종료에 앞서 종료 경고(closure alert)를 주고받는다. [TLS13]의 6.1절을 참고하라. 유효한 종료 경고를 받으면 구현은 그 연결에서 더 이상 데이터를 받지 않으리라고 확신할 수 있다.
역주: RFC 8446의 6.1절은 RFC 9846에서도 같은 번호, 같은 제목의 절이다(6.1절 종료 경고). RFC 9846에서는 close_notify를 warning 수준으로 보내야 한다는 문장, user_canceled 뒤에 반드시 close_notify를 보내야 한다는 규정(RFC 8446은 SHOULD) 등이 더해지거나 강화되었다.
구현은 보통 HTTP 메시지 경계를 감지해 자신이 관심 있는 메시지 데이터를 모두 보냈거나 받았음을 알게 되면, 종료 경고를 보낸 뒤 피어로부터 대응하는 종료 경고를 받기를 기다리지 않고 연결을 닫아 “불완전한 종료(incomplete close)”를 일으킬 수도 있다.
불완전한 종료는 이미 받은 데이터의 보안을 의심하게 하지는 않지만, 이후 데이터가 잘렸을 수 있음을 나타낼 수는 있다. TLS는 HTTP 메시지 프레이밍을 직접 알지 못하므로, 메시지가 완전한지 판단하려면 HTTP 데이터 자체를 살펴봐야 한다. 불완전한 메시지의 처리는 8장에 정의되어 있다.
불완전한 종료를 만나면, 클라이언트는 다음 중 하나를 받은 모든 요청을 완료된 것으로 처리해야 한다(SHOULD).
Content-Length 헤더 필드에 지정된 만큼의 데이터, 또는Transfer-Encoding을 쓸 때) 끝을 나타내는 길이 0의 청크청크 전송 코딩도 Content-Length도 없는 응답은 유효한 종료 경고를 받았을 때만 완전하다. 불완전한 메시지를 완전한 것으로 처리하면 구현이 공격에 노출될 수 있다.
불완전한 종료를 감지한 클라이언트는 우아하게 복구해야 한다(SHOULD).
클라이언트는 연결을 닫기 전에 종료 경고를 반드시 보내야 한다(MUST). 더 이상 데이터를 받을 것으로 예상하지 않는 클라이언트는 서버의 종료 경고를 기다리지 않고 그냥 연결을 닫기로 선택할 수 있으며(MAY), 그러면 서버 쪽에 불완전한 종료가 생긴다.
서버는 클라이언트가 서버 데이터의 끝을 찾아낼 수 있는 경우가 많으므로, 클라이언트로부터 불완전한 종료를 받을 준비가 되어 있어야 한다(SHOULD).
서버는 연결을 닫기 전에 클라이언트와 종료 경고 교환을 반드시 시작하려고 시도해야 한다(MUST). 서버는 종료 경고를 보낸 뒤 연결을 닫을 수 있으며(MAY), 그러면 클라이언트 쪽에 불완전한 종료가 생긴다.
역주: 9장의 “ought to”(9.3.1·9.4절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
“message/http” 미디어 유형은 줄 길이와 인코딩에 관해 모든 “message” 유형에 적용되는 MIME 제약을 지킨다면, 하나의 HTTP 요청 또는 응답 메시지를 감싸는 데 쓸 수 있다. 줄 길이 제한 때문에 “message/http” 안의 필드 값은 5.2절에서 설명한 자동 줄바꿈(obs-fold)을 써서 여러 줄에 걸쳐 전달할 수 있다. “message/http” 데이터의 수신자는 메시지를 소비할 때 폐기된 자동 줄바꿈을 모두 하나 이상의 SP 문자로 반드시 바꿔야 한다(MUST).
“application/http” 미디어 유형은 하나 이상의 HTTP 요청 또는 응답 메시지(서로 섞이지 않은)의 파이프라인을 감싸는 데 쓸 수 있다.
Content-Transfer-Encoding을 써야 한다.역주: 등록 양식의 항목 이름은 IANA 양식 이름이라 10.1절에서 원어를 병기했고, 10.2절에서는 반복하지 않았다. 값 가운데 N/A, COMMON, IESG 같은 양식 값과 매개 변수 이름·값은 원어로 두었다.
이 절은 개발자, 정보 제공자, 사용자에게 HTTP 메시지 구문과 파싱에 관련해 알려진 보안 고려 사항을 알리기 위한 것이다. HTTP 의미론, 콘텐츠, 라우팅에 관한 보안 고려 사항은 [HTTP]에서 다룬다.
응답 분할(CRLF 인젝션이라고도 한다)은 웹 사용에 대한 여러 공격에 쓰이는 흔한 기법으로, HTTP 메시지 프레이밍이 줄 단위라는 성질과 지속 연결에서 요청과 응답이 순서대로 연관된다는 점을 악용한다 [Klein]. 이 기법은 요청이 공유 캐시를 거칠 때 특히 큰 피해를 줄 수 있다.
응답 분할은 서버(보통 애플리케이션 서버 안)의 취약점을 악용한다. 공격자는 요청의 어떤 매개 변수 안에 인코딩된 데이터를 보낼 수 있는데, 그 데이터는 나중에 디코딩되어 응답의 어느 응답 헤더 필드 안에 그대로 되돌아온다. 디코딩된 데이터가 응답이 끝나고 다음 응답이 시작된 것처럼 보이도록 만들어져 있으면 응답이 분할된 것이고, 겉보기에 두 번째인 응답 안의 콘텐츠는 공격자가 제어하게 된다. 그러면 공격자는 같은 지속 연결에서 다른 요청을 아무것이나 보내, 분할된 응답의 뒷부분이 두 번째 요청에 대한 권한 있는(authoritative) 응답이라고 수신자(중개자 포함)가 믿도록 속일 수 있다.
예를 들어 요청 대상 안의 매개 변수를 애플리케이션 서버가 읽어 리다이렉트 안에서 재사용하면, 같은 매개 변수가 응답의 Location 헤더 필드에 그대로 되돌아오게 된다. 애플리케이션이 그 매개 변수를 디코딩하고, 응답 필드에 넣을 때 적절히 인코딩하지 않으면, 공격자는 인코딩된 CRLF 옥텟과 그 밖의 콘텐츠를 보내 애플리케이션의 단일 응답을 둘 이상의 응답처럼 보이게 만들 수 있다.
응답 분할에 대한 흔한 방어책은 인코딩된 CR과 LF처럼 보이는 데이터(예: “%0D”, “%0A”)가 있는 요청을 걸러 내는 것이다. 그러나 이는 애플리케이션 서버가 URI 디코딩만 하고, charset 트랜스코딩, XML 엔터티 변환, base64 디코딩, sprintf 재서식 등 더 드러나지 않는 데이터 변환은 하지 않는다고 가정한다. 더 효과적인 완화책은 서버의 핵심 프로토콜 라이브러리가 아닌 것이 헤더 섹션 안에 CR이나 LF를 보내지 못하게 막는 것이다. 이는 헤더 필드 출력을 잘못된 옥텟을 걸러 내는 API로 제한하고, 애플리케이션 서버가 프로토콜 스트림에 직접 쓰지 못하게 한다는 뜻이다.
요청 스머글링([Linhart])은 여러 수신자 사이의 프로토콜 파싱 차이를 악용해, 겉보기에 무해한 요청 안에 (그렇지 않았다면 정책에 따라 차단되거나 비활성화되었을 수 있는) 추가 요청을 숨기는 기법이다. 응답 분할과 마찬가지로 요청 스머글링은 HTTP 사용에 대한 여러 공격으로 이어질 수 있다.
이 명세는 요청 스머글링의 효과를 줄이기 위해 요청 파싱에 관한 새 요구 사항을, 특히 6.3절의 메시지 프레이밍과 관련해 도입했다.
역주: RFC 9931(2026년 3월)은
CONNECT요청에 관한 요구 사항으로 이 문서를 갱신했다. 내용은 역자 부록에 옮겼다.
HTTP는 메시지 무결성을 보장하는 구체적인 메커니즘을 정의하지 않으며, 대신 기반 전송 프로토콜의 오류 감지 능력과, 완전성을 감지하기 위한 길이나 청크로 끝이 구분되는 프레이밍의 사용에 의존한다. 역사적으로 단일한 무결성 메커니즘이 없는 것은 대부분의 HTTP 통신이 격식 없는 성격을 띤다는 점으로 정당화되었다. 그러나 HTTP가 정보 접근 메커니즘으로 널리 쓰이면서, 메시지 무결성 검증이 매우 중요한 환경에서도 점점 더 많이 쓰이게 되었다.
“https” 스킴과 함께 제공되는 인증된 암호화(authenticated encryption) 같은 메커니즘은 메시지 변조에 대한 보호를 제공한다. 그러나 연결 종료를 이용해 메시지를 잘라 낼 수 없도록 주의할 필요가 있다(9.8절 참고). 사용자 에이전트는 불완전한 메시지를 받아들이기를 거부하거나 특별하게 다룰 수도 있다. 예를 들어 병력이나 약물 상호 작용 정보를 보는 데 쓰이는 브라우저는 그런 정보가 전송 중에 불완전하거나, 만료되었거나, 손상되었음을 프로토콜이 감지하면 이를 사용자에게 알려야 한다. 그런 메커니즘은 사용자 에이전트 확장이나 응답 안의 메시지 무결성 메타데이터의 존재에 따라 선택적으로 활성화될 수도 있다.
“http” 스킴은 메시지의 우발적 또는 악의적 변조에 대한 보호를 전혀 제공하지 않는다.
“https” 스킴을 쓰는 경우에도 프로토콜 확장을 써서 중개자에 의한 원치 않는 메시지 변조의 위험을 완화할 수도 있다. 확장 가능한 메타데이터 필드를 통해 메시지에 선택적으로 추가하는 메시지 인증 코드(message authentication code)나 전자 서명을 써서 무결성을 보장할 수도 있다.
HTTP는 메시지 기밀성이 필요할 때 기반 전송 프로토콜이 이를 제공하는 데 의존한다. HTTP는 전송 프로토콜과 무관하도록 특별히 설계되었으므로 여러 형태의 암호화된 연결 위에서 쓰일 수 있으며, 그런 전송의 선택은 URI 스킴의 선택이나 사용자 에이전트 설정으로 식별된다.
“https” 스킴은 [HTTP]의 4.2.2절에서 설명하듯, 기밀 연결이 필요한 리소스를 식별하는 데 쓸 수 있다.
다음 등록들의 변경 관리자는 “IETF (iesg@ietf.org) - Internet Engineering Task Force”다.
IANA는 [HTTP]의 18.4절에 설명된 대로, https://www.iana.org/assignments/http-fields의 “HTTP 필드 이름 레지스트리(Hypertext Transfer Protocol (HTTP) Field Name Registry)”에 다음 필드 이름을 추가했다.
| 필드 이름(Field Name) | 상태(Status) | 절(Section) | 비고(Comments) |
|---|---|---|---|
| Close | permanent | 9.6 | (reserved) |
| MIME-Version | permanent | B.1 | |
| Transfer-Encoding | permanent | 6.1 |
표 1
IANA는 https://www.iana.org/assignments/media-types의 “미디어 유형(Media Types)” 레지스트리를 미디어 유형 “message/http”와 “application/http”에 대한 10.1절과 10.2절의 등록 정보로 각각 갱신했다.
IANA는 https://www.iana.org/assignments/http-parameters/의 “HTTP 전송 코딩 레지스트리”를 7.3절의 등록 절차와 아래 표에 요약한 콘텐츠 코딩 이름(content coding names)으로 갱신했다.
| 이름(Name) | 설명(Description) | 절(Section) |
|---|---|---|
| chunked | Transfer in a series of chunks | 7.1 |
| compress | UNIX “compress” data format [Welch] | 7.2 |
| deflate | “deflate” compressed data ([RFC1951]) inside the “zlib” data format ([RFC1950]) | 7.2 |
| gzip | GZIP file format [RFC1952] | 7.2 |
| trailers | (reserved) | 12.3 |
| x-compress | Deprecated (alias for compress) | 7.2 |
| x-gzip | Deprecated (alias for gzip) | 7.2 |
표 2
참고: 코딩 이름 “trailers”는 예약되어 있다. 그 이름을 쓰면
TE헤더 필드([HTTP]의 10.1.4절)의 키워드 “trailers”와 충돌하기 때문이다.
IANA는 https://www.iana.org/assignments/tls-extensiontype-values/의 “TLS ALPN 프로토콜 ID(TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs)” 레지스트리를 아래 등록으로 갱신했다.
| 프로토콜(Protocol) | 식별 시퀀스(Identification Sequence) | 참조(Reference) |
|---|---|---|
| HTTP/1.1 | 0x68 0x74 0x74 0x70 0x2f 0x31 0x2e 0x31 (“http/1.1”) | RFC 9112 |
표 3
역주: RFC 8446은 2026년 7월 RFC 9846으로 대체되었다(번역). RFC 9846도 같은 TLS 버전 1.3을 규정하며, RFC 8446의 절 구성을 대체로 그대로 유지한다.
역주: 참고 문헌 항목은 원문 그대로 두고 Markdown 목록으로 바꾸었다. 여러 줄에 걸친 항목은 한 줄로 이었고, 줄바꿈으로 끊긴 URL은 공백 없이 이었다.
아래 수집한 ABNF에서 목록 규칙은 [HTTP]의 5.6.1절에 따라 펼쳐 놓았다.
BWS = <BWS, see [HTTP], Section 5.6.3>
HTTP-message = start-line CRLF *( field-line CRLF ) CRLF [
message-body ]
HTTP-name = %x48.54.54.50 ; HTTP
HTTP-version = HTTP-name "/" DIGIT "." DIGIT
OWS = <OWS, see [HTTP], Section 5.6.3>
RWS = <RWS, see [HTTP], Section 5.6.3>
Transfer-Encoding = [ transfer-coding *( OWS "," OWS transfer-coding
) ]
absolute-URI = <absolute-URI, see [URI], Section 4.3>
absolute-form = absolute-URI
absolute-path = <absolute-path, see [HTTP], Section 4.1>
asterisk-form = "*"
authority = <authority, see [URI], Section 3.2>
authority-form = uri-host ":" port
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
chunk-data = 1*OCTET
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val
] )
chunk-ext-name = token
chunk-ext-val = token / quoted-string
chunk-size = 1*HEXDIG
chunked-body = *chunk last-chunk trailer-section CRLF
field-line = field-name ":" OWS field-value OWS
field-name = <field-name, see [HTTP], Section 5.1>
field-value = <field-value, see [HTTP], Section 5.5>
last-chunk = 1*"0" [ chunk-ext ] CRLF
message-body = *OCTET
method = token
obs-fold = OWS CRLF RWS
; 원문: obs-text = <obs-text, see [HTTP], Section 5.6.4>
obs-text = <obs-text, see [HTTP], Section 5.5>
origin-form = absolute-path [ "?" query ]
port = <port, see [URI], Section 3.2.3>
query = <query, see [URI], Section 3.4>
quoted-string = <quoted-string, see [HTTP], Section 5.6.4>
reason-phrase = 1*( HTAB / SP / VCHAR / obs-text )
request-line = method SP request-target SP HTTP-version
request-target = origin-form / absolute-form / authority-form /
asterisk-form
start-line = request-line / status-line
status-code = 3DIGIT
status-line = HTTP-version SP status-code SP [ reason-phrase ]
token = <token, see [HTTP], Section 5.6.2>
trailer-section = *( field-line CRLF )
transfer-coding = <transfer-coding, see [HTTP], Section 10.1.4>
uri-host = <host, see [URI], Section 3.2.2>
역주: 정오표 7744(Verified)에 따라
obs-text의 참조를 5.6.4절에서 5.5절로 고친 규칙이다(원문 규칙은 주석으로 남김).
HTTP/1.1은 메시지 본문을 제한 없이 다양한 표현과 확장 가능한 필드로 전송할 수 있도록, 인터넷 메시지 형식 [RFC5322]과 MIME [RFC2045]에 정의된 구성 요소를 많이 사용한다. 그러나 이 구성 요소 가운데 일부는 대화형 통신의 요구에 더 잘 맞도록 재해석되었고, 그 결과 HTTP 안에서 MIME 구성 요소를 쓰는 방식에 몇 가지 차이가 생겼다. 이 차이는 이진 연결에서의 성능을 최적화하고, 새 미디어 유형을 더 자유롭게 쓸 수 있게 하고, 날짜 비교를 쉽게 하고, 흔한 구현을 수용하도록 신중하게 선택되었다.
이 부록은 HTTP가 MIME과 다른 구체적인 영역을 설명한다. 엄격한 MIME 환경과 오가는 프록시와 게이트웨이는 이 차이를 알고, 필요한 곳에서 적절한 변환을 제공해야 한다.
HTTP는 MIME을 준수하는 프로토콜이 아니다. 그러나 메시지는 메시지를 구성하는 데 MIME 프로토콜의 어느 버전을 썼는지 나타내기 위해 MIME-Version 헤더 필드를 하나 포함할 수 있다. MIME-Version 헤더 필드를 쓰면 메시지가 ([RFC2045]에 정의된) MIME 프로토콜에 완전히 적합함을 나타낸다. 송신자는 HTTP 메시지를 엄격한 MIME 환경으로 내보낼 때 (가능하면) 완전한 적합성을 보장할 책임이 있다.
MIME은 [RFC2049]의 4장에 설명된 대로 인터넷 메일 본문 부분을 전송하기 전에 정규형으로 변환할 것을 요구하며, “text” 유형의 콘텐츠는 줄바꿈을 CRLF로 나타내고 줄바꿈 시퀀스 밖에서 CR이나 LF를 쓰는 것을 금지할 것을 요구한다 [RFC2046]. 이와 달리 HTTP는 콘텐츠 안의 줄바꿈을 나타내는 데 CRLF, 단독 CR, 단독 LF 중 무엇을 쓰든 상관하지 않는다.
HTTP에서 엄격한 MIME 환경으로 가는 프록시나 게이트웨이는 텍스트 미디어 유형 안의 모든 줄바꿈을 RFC 2049의 정규형인 CRLF로 바꿔야 마땅하다. 그러나 Content-Encoding이 있거나, HTTP가 CR과 LF를 나타내는 데 각각 옥텟 13과 10을 쓰지 않는 일부 charset의 사용을 허용한다는 점 때문에 이 작업이 복잡해질 수 있다는 점에 유의하라.
원래 콘텐츠가 이미 정규형이 아니라면, 변환은 원래 콘텐츠에 적용된 암호학적 체크섬을 모두 깨뜨린다. 따라서 HTTP에서 그런 체크섬을 쓰는 콘텐츠에는 정규형을 권장한다.
HTTP/1.1은 날짜 비교 과정을 단순하게 하기 위해 제한된 날짜 형식 집합([HTTP]의 5.6.7절)을 쓴다. 다른 프로토콜에서 오는 프록시와 게이트웨이는 메시지에 있는 Date 헤더 필드가 HTTP/1.1 형식 중 하나를 따르도록 보장하고, 필요하면 날짜를 다시 써야 마땅하다.
MIME에는 HTTP의 Content-Encoding 헤더 필드에 해당하는 개념이 없다. 이 필드는 미디어 유형에 대한 수정자로 작용하므로, HTTP에서 MIME 준수 프로토콜로 가는 프록시와 게이트웨이는 메시지를 전달하기 전에 Content-Type 헤더 필드의 값을 바꾸거나 표현을 디코딩해야 마땅하다. (인터넷 메일에서 Content-Type을 실험적으로 적용한 일부 사례는 Content-Encoding과 같은 기능을 하기 위해 “;conversions=
HTTP는 MIME의 Content-Transfer-Encoding 필드를 쓰지 않는다. MIME 준수 프로토콜에서 HTTP로 가는 프록시와 게이트웨이는 응답 메시지를 HTTP 클라이언트에 전달하기 전에 Content-Transfer-Encoding을 모두 제거해야 한다.
HTTP에서 MIME 준수 프로토콜로 가는 프록시와 게이트웨이는 메시지가 그 프로토콜에서 안전하게 전송될 수 있는 올바른 형식과 인코딩을 갖추도록 보장할 책임이 있다. 여기서 “안전한 전송(safe transport)”은 사용하는 프로토콜의 제한으로 정의된다. 그런 프록시나 게이트웨이는 적절한 Content-Transfer-Encoding으로 데이터를 변환하고 표시하는 것이 목적지 프로토콜에서 안전하게 전송될 가능성을 높인다면 그렇게 해야 마땅하다.
MHTML [RFC2557] 구현과 코드를 공유하는 HTTP 구현은 MIME의 줄 길이 제한을 알고 있어야 한다. HTTP에는 이 제한이 없으므로 HTTP는 긴 줄에 자동 줄바꿈을 하지 않는다. HTTP로 전송되는 MHTML 메시지는 줄 길이 제한과 자동 줄바꿈, 정규화 등을 포함해 MHTML의 모든 규칙을 따른다. HTTP는 메시지 본문을 수정 없이 전송하며, “multipart/byteranges” 유형([HTTP]의 14.6절)을 제외하고는 콘텐츠나 그 안에 들어 있을 수 있는 MIME 헤더 줄을 해석하지 않기 때문이다.
역주: 부록 B의 “ought to”(B.2·B.3·B.4·B.5절)는 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
HTTP/0.9는 요청에서 헤더 필드를 지원하지 않았으므로, 이름 기반 가상 호스트(name-based virtual host, Host 헤더 필드를 살펴 리소스를 선택하는 것)를 지원할 메커니즘이 없다. 이름 기반 가상 호스트를 구현하는 서버는 HTTP/0.9 지원을 꺼야 마땅하다. HTTP/0.9처럼 보이는 요청은 대부분 실제로는 클라이언트가 요청 대상을 제대로 인코딩하지 못해 잘못 구성된 HTTP/1.x 요청이다.
클라이언트와 서버가 Host 헤더 필드([HTTP]의 7.2절)를 지원하고, HTTP/1.1 요청에 이 필드가 없으면 오류를 보고하고, 절대 URI(3.2절)를 받아들여야 한다는 요구 사항은 HTTP/1.1이 정의한 가장 중요한 변경 사항에 속한다.
예전 HTTP/1.0 클라이언트는 IP 주소와 서버가 일대일로 대응한다고 가정했다. 요청이 향한 IP 주소 외에는 요청의 의도된 서버를 구별하는 확립된 메커니즘이 없었다. Host 헤더 필드는 HTTP/1.1을 개발하는 동안 도입되었고, 대부분의 HTTP/1.0 브라우저가 빠르게 구현했지만, 완전한 채택을 보장하기 위해 모든 HTTP/1.1 요청에 추가 요구 사항을 두었다. 이 글을 쓰는 시점에 대부분의 HTTP 기반 서비스는 요청 대상을 정하는 데 Host 헤더 필드에 의존한다.
HTTP/1.0에서는 각 연결을 요청 전에 클라이언트가 수립하고, 응답을 보낸 뒤 서버가 닫는다. 그러나 일부 구현은 [RFC2068]의 19.7.1절에 설명된, 명시적으로 협상하는(“Keep-Alive”) 방식의 지속 연결을 구현한다.
일부 클라이언트와 서버는 “Connection: keep-alive” 요청 헤더 필드로 명시적으로 협상해, 지속 연결에 대한 이 이전 방식과 호환되기를 원할 수 있다. 그러나 HTTP/1.0 지속 연결의 일부 실험적 구현에는 결함이 있다. 예를 들어 HTTP/1.0 프록시 서버가 Connection을 이해하지 못하면 그 헤더 필드를 다음 인바운드 서버로 잘못 전달하고, 그 결과 연결이 멈춘다.
시도된 해결책 하나는 프록시만을 겨냥한 Proxy-Connection 헤더 필드의 도입이었다. 실제로는 이것도 통하지 않았다. 프록시는 여러 계층으로 배치되는 경우가 많아 위에서 논의한 것과 같은 문제를 일으키기 때문이다.
그래서 클라이언트가 어떤 요청에도 Proxy-Connection 헤더 필드를 보내지 않기를 권장한다.
클라이언트가 요청에 “Connection: keep-alive”를 쓰는 것도 신중히 고려하기를 권장한다. 이를 쓰면 HTTP/1.0 서버와 지속 연결을 할 수 있지만, 이를 쓰는 클라이언트는 연결에서 “멈춘” 요청(클라이언트가 그 헤더 필드를 그만 보내야 마땅함을 나타낸다)이 있는지 감시해야 하며, 프록시를 쓰는 동안에는 클라이언트가 이 메커니즘을 아예 쓰지 않아야 마땅하다.
HTTP/1.1은 Transfer-Encoding 헤더 필드(6.1절)를 도입한다. 전송 코딩은 MIME 준수 프로토콜로 HTTP 메시지를 전달하기 전에 디코딩해야 한다.
HTTP의 설계 목표, 역사, 아키텍처, 적합성 기준, 프로토콜 버전 관리, URI, 메시지 라우팅, 헤더 필드를 소개하는 절의 대부분은 [HTTP]로 옮겼다. 이 문서는 HTTP/1.1에 고유한 메시지 구문과 연결 관리 요구 사항만 다루도록 줄였다.
콘텐츠 밖에서 단독 CR을 금지했다. (2.2절)
프록시 처리와 맞추기 위해, 원본 서버가 요청 URI의 절대 형식을 Host 헤더 필드보다 우선한다는 점을 명시했다. (3.2.2절)
역주: 바로 위 문단은 정오표 8284(Verified)에 따라 추가한 내용이다.
authority-form의 ABNF 정의를, (포트가 선택 사항인) URI의 더 일반적인 authority 구성 요소에서 CONNECT가 요구하는 구체적인 host:port 형식으로 바꿨다. (3.2.3절)
수신자는 모호한 메시지 프레이밍을 처리할 때 스머글링/분할 공격을 피해야 한다. (6.1절)
청크 확장의 ABNF에서 “;”와 “=” 주변의 (나쁜) 공백을 다시 도입했다. [RFC7230]에서 공백을 없앴지만, 그 변경이 기존 구현을 깨뜨리는 것으로 드러났다. (7.1.1절)
트레일러 필드의 의미론은 이제 청크 전송 코딩의 세부 사항을 넘어선다. 청크 디코딩 알고리즘(7.1.3절)을 갱신해, 트레일러 필드를 헤더 섹션과 따로 저장·전달하도록 장려하고, 수신자가 대응하는 필드 정의가 병합을 허용하고 병합 방법을 정의한다는 것을 알 때만 헤더 섹션에 병합하도록 허용하며, 그 밖의 경우에는 병합하지 않고 트레일러 필드를 버리도록 했다. 트레일러 부분은 이제 헤더 섹션과 더 일관되고 본문 부분과 더 뚜렷이 구별되도록 트레일러 섹션이라고 부른다. (7.1.2절)
TE 헤더 필드의 순위 사용과 충돌하지 않도록 “q”라는 전송 코딩 매개 변수를 허용하지 않는다. (7.3절)
역주: 부록 C의 “ought to”와 “ought not”(C.1·C.2.2절)은 대문자 키워드가 아니므로 괄호를 붙이지 않았다.
역주: 이 부록은 원문에 없으며 역자가 추가했다. RFC 9931(2026년 3월)의 8장 “Requirements for HTTP CONNECT” 본문을 옮긴 것이다. RFC 9931은 이 내용으로 RFC 9112를 갱신하지만 넣을 위치를 정하지 않아, 3.2.3절과 11.2절에 역주로 안내하고 여기에 모았다.
RFC 9931은 RFC 9112(원문 표기 [HTTP/1.1], 곧 이 문서)를 갱신하여 이 절의 나머지 본문을 포함하게 한다. 이 절의 요구 사항은 HTTP/1.1에만 적용된다.
신뢰할 수 없는 TCP 클라이언트를 대신해 CONNECT 요청을 보내는 프록시 클라이언트는 다음 중 하나 또는 둘 다를 반드시 해야 한다(MUST).
2xx (Successful) 응답을 기다린다.이 두 동작 중 적어도 하나를 구현하지 않는 프록시 클라이언트는 간단한 요청 스머글링 공격(11.2절)에 취약하다.
이 글을 쓰는 시점에 일부 프록시 클라이언트는 설명한 대로 취약한 것으로 여겨진다. 완화책으로, 프록시 서버는 CONNECT 요청을 거부할 때 그 연결에서 더 이상 요청을 처리하지 않고 기반 연결을 반드시 닫아야 한다(MUST). 이 요구 사항은 요청에 “close” 연결 옵션이 있든 없든 적용된다.
이 완화책 때문에 올바르게 구현된 클라이언트의 연결 수립이 자주 느려진다는 점에 유의하라. 특히 407 (Proxy Authentication Required) 응답을 반환할 때 그렇다. 이 성능 손실은 이 공격에 취약하지 않은 HTTP/2나 HTTP/3을 써서 피할 수 있다.
성능 최적화로, 클라이언트가 신뢰할 수 없는 TCP 페이로드 데이터를 전달하기 전에 2xx (Successful) 응답을 기다린다고(즉, 위 1번 항목을 지킨다고) 알려져 있다면 프록시 서버는 이 완화책을 끌 수 있다(MAY). 프록시 서버는 요청의 User-Agent 헤더 필드와, 준수 여부에 관한 그 사용자 에이전트 제조사의 문서를 이용해 준수하는 클라이언트를 식별할 수 있다.
[HTTP]의 부록 “Acknowledgements”를 참고하라. 그 내용이 이 문서에도 적용된다.
역주: 색인 항목은 원문 그대로 두고, 절 번호만 이 문서의 해당 절 링크로 바꾸었다. 원문에서 강조(
*_…_*)된 참조는 굵게 표시했다.
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