HTTP 헤더 기본 구조
header-field = field-name ":" OWS field-value OWS
- field-name: 헤더 이름 (대소문자 구분이 없다)
- ex. Host, Content-Type
- field-value: 헤더 값 (문자열, 숫자, 기타 정보)
- OWS (Optional White Space): 공백 허용 (콜론 뒤에 스페이스 있어도 되고 없어도 된다.)
[요청]
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com 👈 HTTP 헤더
[응답]
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8 👈 HTTP 헤더
Content-Length: 3423 👈 HTTP 헤더
<html>
<body>...</body>
</html>
HTTP 헤더 용도
HTTP 헤더는 HTTP 전송에 필요한 모든 부가 정보를 담고 있는 영역이다. 예를 들어 메시지 바디의 내용이 어떤 형식인지, 메시지 바디의 크기가 얼마인지, 압축 여부, 인증 방식, 요청을 보낸 클라이언트의 정보, 서버의 정보, 캐시 관리 정책 등 다양한 데이터가 헤더에 포함된다. 이처럼 헤더는 단순히 본문을 전달하는 것에서 끝나는 것이 아니라, 그 본문을 어떻게 해석하고 처리해야 하는지 알려주는 중요한 메타데이터 역할을 한다.
HTTP에는 수많은 표준 헤더가 정의되어 있으며, 개발자가 필요에 따라 임의의 헤더를 추가할 수도 있다. (ex. helloworld: hihi )
HTTP 헤더
과거 - RFC2616 (1999)
Header

HTTP/1.1을 정의했던 RFC2616에서는 헤더를 크게 네 가지로 구분하였다.
- General 헤더: 메시지 전체에 적용되는 정보 (ex. `Connection: close`)
- Request 헤더: 요청 정보 (ex. `User-Agent: Mozilla/5.0 (Machintosh; .. )`)
- Response 헤더: 응답 정보 (ex. `Server: Apache`)
- Entity 헤더: 엔티티 바디 정보 (ex. `Content-Type: tex/html, Content-Length: 3423`)
Message Body
HTTP/1.1 200 OK
Content-Type: text-/html;charset=UTF-8 👈 엔티티 헤더
Content-Length: 3423 👈 엔티티 헤더
<html> 👈 메시지 본문, 엔티티 본문
<body>...</body>
</html>
- 메시지 본문(message body)은 엔티티 본문(entity body)을 전달하는 데 사용되었다.
- 엔티티 본문은 요청이나 응답에서 실제 데이터를 의미한다.
- 엔티티 헤더는 이 데이터를 해석하기 위한 정보를 제공하였다.
- 예: 데이터 유형(html, json), 데이터 길이, 압축 정보 등
‼️ 1999년에 정의된 RFC2616은 이후 폐기되었고, 2014년에 새로운 표준인 RFC7230~7235가 등장하였다.
최신 - RFC723x (2014)
- 엔티티(Entity) → 표현(Representation) 으로 변경되었다.
- Representation = Representation Metadata + Representation Data
- 표현은 메타데이터(해석 정보)와 데이터(실제 내용)로 이루어진다.
Message Body - RFC7230
HTTP/1.1 200 OK
Content-Type: text-/html;charset=UTF-8 👈 표현 헤더
Content-Length: 3423 👈 표현 헤더
<html> 👈 메시지 본문, 표현 데이터
<body>...</body>
</html>
- 메시지 본문(message body)은 표현 데이터(representation data)를 전달한다.
- 메시지 본문은 페이로드(payload)라고도 부른다.
- 표현은 요청이나 응답에서 전달되는 실제 데이터이다.
- 표현 헤더는 이 데이터를 해석할 수 있는 정보를 제공한다.
- 예: 데이터 유형(html, json), 데이터 길이, 압축 정보 등
- 참고로, 표현 헤더는 표현 메타데이터와 페이로드 메시지를 구분해야 하지만, 여기서는 단순화를 위해 생략한다.
표현
표현 헤더(Representation Header)는 요청(Request)과 응답(Response) 모두에서 사용된다. 서버 응답뿐 아니라, 클라이언트가 요청 시에도 특정 표현을 보내거나 지정할 때 표현 헤더를 포함할 수 있다.
HTTP/1.1 200 OK
Content-Type: text-/html;charset=UTF-8
Content-Length: 3423
<html>
<body>...</body>
</html>
- Content-Type: 표현 데이터의 형식을 설명한다.
- 미디어타입, 문자 인코딩
- ex. `text/html;` , `charset=utf-8` , `application/json` , `image/png`
- Content-Encoding: 표현 데이터의 압축 방식을 나타낸다.
- 표현 데이터를 압축하기 위해 사용한다.
- 데이터를 전달하는 곳에서 압축 후 인코딩 헤더를 추가한다.
- 데이터를 읽는 쪽에서 인코딩 헤더의 정보로 압축을 해제한다.
- ex. `gzip`, `deflate`, `identity`
HTTP/1.1 200 OK Content-Type: text/html;charset=UTF-8 Content-Encoding: gzip Content-Length: 521 lkj123kljolasudlkjawelolywfnfdo9123u34ljko09udkl - Content-Language: 표현 데이터의 자연 언어를 표현한다.
- ex. `ko`, `en`, `en-US`
HTTP/1.1 200 OK Content-Type: text/html;charset=UTF-8 Content-Language:ko Content-Length: 521 <html> 안녕하세요. </html> - Content-Length: 표현 데이터의 길이(바이트 단위)를 나타낸다.
- Transfer-Encoding(전송 코딩)을 사용하면 Content-Length를 사용하면 안 된다.
HTTP/1.1 200 OK Content-Type: text/html;charset=UTF-8 Content-Length: 5 hello
협상 (Contents Negotiation)
클라이언트가 선호하는 표현을 요청하는 것이다. 협상 헤더는 요청 시에만 사용한다.
- Accept: 클라이언트가 선호하는 미디어 타입 전달
- Accept-Charset: 클라이언트가 선호하는 문자 인코딩
- Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
- Accept-Language: 클라이언트가 선호하는 자연 언어
Accept-Language 적용
예를 들어 한국어 브라우저를 사용하고 외국에 있는 이벤트 사이트에 접속한다고 가정해보자. 해당 서버가 다국어를 지원하지만 기본적으로 영어(en)를 응답하고 한국어(ko)도 지원한다고 하면, 클라이언트에서 단순히 요청만 보낼 경우 서버는 브라우저가 한국어를 사용하는지 알 수 없기 때문에 영어로 응답하게 된다. 이런 경우에 콘텐츠 협상(Content Negotiation) 방식 중 하나인 `Accept-Language` 헤더를 사용하면 된다. 클라이언트가 요청 시 `Accept-Language: ko`를 함께 보내면, 서버는 이를 참고하여 한국어로 응답할 수 있고, 응답에는 `Content-Language: ko`와 같이 언어가 명시된다.
복잡한 예로 서버가 기본 언어로 독일어(de)를 제공하고, 영어(en)도 함께 지원한다고 가정해볼 수 있다. 클라이언트는 한국어로 된 서비스를 받고 싶지만 서버가 한국어를 지원하지 않는다면, 독일어보다는 영어로 된 응답을 받기를 원할 수 있다. 이런 경우에는 단순히 언어를 지정하는 것만으로는 부족하며, 언어 선택에 대한 우선순위(priority)를 함께 전달해야 한다.
협상과 우선 순위
Quality Values(q)
1.Quality Values(q) 값을 사용한다.
- 0~1, 클수록 높은 우선순위
- 생략하면 1
- 구체적인 것을 우선한다.
GET /event
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
- ko-KR;q=1 (q생략)
- ko;q=0.9
- en-US;q=0.8
- en:q=0.7
2. 구체적인 것을 우선한다
GET /event
Accept: text/*, text/plain, text/plain;format=flowered, */*
- text/plain;format=flowed
- text/plain
- text/*
- /
3. 구체적인 것을 기준으로 미디어 타입을 맞춘다.
Accept: text/*;q=0.3,text/html;q=0.7,text/html;level=1,text/html;level=2;q=0.4,*/*;q=0.5

전송 방식
네 개로 분류할 수 있다.
단순 전송 (Content-Length)
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
<html>
<body>...</body>
</html>
메시지의 길이를 명확히 알고 있을 때 사용하는 방식이다. 이때는 `Content-Length` 헤더에 본문의 크기를 지정하여 클라이언트와 서버가 정확히 얼마만큼의 데이터를 주고받아야 하는지를 알 수 있다. 요청을 보낼 때 본문 전체를 한 번에 전송하고, 응답을 받을 때도 지정된 길이만큼 쭉 받아 처리하는 구조다. 따라서 메시지의 크기가 확정되어 있을 때 가장 단순하고 직관적으로 사용할 수 있는 전송 방식이라고 할 수 있다.
압축 전송 (Content-Encoding)
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Encoding: gzip
Content-Length: 521
lkj123kljolasudlkjawelolywfnfdo9123u34ljko09udkl
서버에서 데이터를 압축한 뒤 클라이언트로 전달하는 방식이다. 이 경우 서버는 본문을 압축했음을 알리기 위해 반드시 `Content-Encoding` 헤더를 함께 보내야 한다. 클라이언트는 이 정보를 확인하고, 전달받은 데이터를 해당 압축 방식에 맞게 해제하여 원래의 콘텐츠로 복원한다. 이를 통해 네트워크 트래픽을 줄이고 응답 속도를 개선할 수 있다는 장점이 있다.
분할 전송 (Transfer-Encoding)
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
5
Hello
5
World
0
\r\n
데이터를 일정한 덩어리(chunk) 단위로 쪼개어 전송하는 방식이다. 이 경우 `Transfer-Encoding: chunked` 헤더를 사용하여 서버가 데이터를 청크 단위로 보냄을 알린다. 예를 들어 서버가 5바이트 크기의 문자열 “Hello”와 또 다른 5바이트 문자열 “World”를 전송한다고 가정하면, 서버는 각각의 데이터 앞에 청크의 크기를 16진수로 명시한 뒤 본문을 전달한다. 클라이언트는 이를 순차적으로 받아 저장해두며, 모든 전송이 끝났음을 알릴 때는 `0\r\n`을 보내 전송 종료를 표시한다. 이 방식은 전송할 전체 데이터의 크기를 미리 알 수 없을 때 유용하게 사용된다.
범위 전송 (Range, Content-Range)
[요청]
GET /event
Range: bytes=1001-2000
[응답]
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Range: bytes 1001-2000 / 2000
qweqwe1;2oi3019u20ehj1987askjh34q09y
큰 데이터를 받을 때 유용한 방식으로, 전체 파일을 다시 받지 않고 필요한 범위만 지정해서 요청할 수 있다. 예를 들어 이미지를 다운로드하다가 중간에 끊긴 경우, 일반 전송이라면 처음부터 다시 받아야 해서 불필요한 트래픽이 발생한다. 하지만 범위 전송을 사용하면 이어받기를 할 수 있다. 클라이언트는 `Range` 헤더를 사용하여 특정 바이트 구간을 지정해 요청하고, 서버는 이에 맞춰 `206 Partial Content` 상태 코드와 함께 해당 범위의 데이터만 응답한다. 이렇게 하면 대용량 파일 전송 시 네트워크 자원을 효율적으로 사용할 수 있다.
일반 정보
단순 정보성 HTTP 헤더는 다음과 같다.
From
유저 에이전트의 이메일 정보를 담는 헤더이다. 일반적인 웹 요청에서는 거의 사용되지 않으며, 주로 검색 엔진과 같은 특수한 경우에 활용된다. 이 헤더는 요청 시 포함될 수 있다.
Referer
현재 요청된 페이지의 이전 웹 페이지 주소를 의미한다. 예를 들어 A 페이지에서 B 페이지로 이동하는 경우, B를 요청할 때 `Referer: A`가 포함되어 전송된다. 이를 통해 유입 경로 분석이 가능하다. 참고로, 이 헤더의 이름은 원래 단어 referrer의 오타에서 비롯되었다.
User-Agent
클라이언트 애플리케이션의 정보를 담는다. 예를 들어 웹 브라우저의 이름, 버전, 운영체제 정보 등이 여기에 포함된다. 서버는 이를 통해 어떤 종류의 브라우저에서 접속했는지 파악할 수 있으며, 특정 환경에서 장애가 발생하는지 확인하거나 통계 자료로 활용할 수 있다. 이 헤더 역시 요청에서 사용된다.
Server
요청을 처리하는 오리진(Origin) 서버의 소프트웨어 정보를 나타낸다. 오리진 서버는 실제로 클라이언트 요청을 최종적으로 처리하는 서버를 의미하며, 예를 들어 `Server: Apache/2.2.22 (Debian)`이나 `Server: nginx`와 같이 나타난다.
Date
메시지가 생성된 날짜와 시간을 의미한다. 예시는 `Date: Tue, 15 Nov 1994 08:12:31 GMT`와 같은 형식이며, 주로 응답 메시지에서 사용된다.
특별한 정보
Host
요청한 호스트 정보(도메인)을 나타낸다.
GET /search?=hello&hl=ko HTTP/1.1
Host: www.google.com
요청에서 반드시 포함되어야 하며, 하나의 서버가 여러 도메인을 처리할 때 구분하는 역할을 한다. 가상호스트를 통해 하나의 IP 주소에 여러 도메인이 연결되어 있는 경우, 서버는 클라이언트 요청이 어떤 도메인에 대한 것인지 Host 헤더를 통해 식별한다. 만약 Host 헤더가 없다면 서버는 어느 애플리케이션으로 요청이 들어왔는지 알 수 없기 때문에, 반드시 `Host: aaa.com`과 같이 지정해야 한다.
Location
페이지 리다이렉션과 관련된 정보를 담는다. 웹 브라우저는 3xx 응답을 받을 때 Location 헤더가 포함되어 있으면 해당 위치로 자동 이동한다. 또한, 201 Created 응답에서는 Location 헤더 값이 요청에 의해 생성된 리소스의 URI를 나타내며, 3xx Redirection 응답에서는 요청을 자동으로 리디렉션하기 위한 대상 리소스를 지정한다.
Allow
허용 가능한 HTTP 메서드를 알려주는 역할을 한다. 예를 들어 특정 URL 경로가 존재하지만 GET, HEAD, PUT 메서드만 제공하고 POST는 제공하지 않는다면, 클라이언트가 POST 요청을 보낼 경우 서버는 405 Method Not Allowed 응답과 함께 `Allow: GET, HEAD, PUT` 헤더를 보내야 한다. 이를 통해 클라이언트는 해당 리소스에서 어떤 메서드가 허용되는지 알 수 있다.
Retry-After
클라이언트가 다음 요청을 하기까지 기다려야 하는 시간을 나타낸다. 주로 503 Service Unavailable 응답과 함께 사용되며, 서비스가 언제까지 불능인지 알려준다. 값은 날짜 형식(`Retry-After: Fri, 31 Dec 1999 23:59:59 GMT`)이나 초 단위(`Retry-After: 120`)로 지정할 수 있다.
인증
인증과 관련된 HTTP 헤더에는 주로 Authorization과 WWW-Authenticate가 있다.
Authorization
클라이언트가 서버에 자신의 인증 정보를 전달할 때 사용된다. 예를 들어 `Authorization: Basic xxxxxxxxx`와 같이 헤더를 포함시켜 요청을 보내면, 서버는 이를 확인하여 요청한 클라이언트의 접근 권한을 판단한다. 값은 사용되는 인증 방식에 따라 달라진다.
WWW-Authenticate
서버가 클라이언트에게 리소스 접근을 위해 필요한 인증 방법을 알려줄 때 사용된다. 주로 401 Unauthorized 응답과 함께 제공된다.
WWW-Authenticate: Newauth realm="apps", type=1, title="Login to \"apps\"", Basic realm="simple"
클라이언트는 위 정보를 참고하여 올바른 인증 정보를 만들어 다시 요청을 시도해야 한다. WWW-Authenticate는 서버가 요구하는 인증 방식과 필요한 파라미터를 명시하는 역할을 하며, 클라이언트가 인증 절차를 올바르게 수행하도록 안내한다.
쿠키
쿠키는 클라이언트와 서버 간 상태를 유지하기 위해 사용하는 HTTP 헤더이다. 서버는 응답 시 `Set-Cookie` 헤더를 통해 클라이언트에 쿠키를 전달하고, 클라이언트는 이를 저장한 뒤 이후 요청 시 `Cookie` 헤더에 포함하여 서버로 전송한다.
쿠키 미사용의 경우
<처음 welcome 페이지에 접근>
[웹브라우저 요청]
GET /welcome HTTP/1.1
[서버 응답]
HTTP/1.1 200 OK
안녕하세요. 손님
-------------------------
<로그인>
[웹브라우저 요청]
GET /login HTTP/1.1
user=홍길동
[서버 응답]
HTTP/1.1 200 OK
홍길동님이 로그인했습니다.
--------------------------
<로그인 이후 welcome 페이지 접근>
[웹브라우저 요청]
GET /welcome HTTP/1.1
[서버 응답]
HTTP/1.1 200 OK
안녕하세요. 손님 👈 로그인했는지 구분하지 못한다.
HTTP는 무상태(Stateless) 프로토콜이다.
- 클라이언트와 서버가 요청과 응답을 주고 받으면 연결이 끊어진다.
- 클라이언트가 다시 요청하면 서버는 이전 요청을 기억하지 못한다.
- 클라이언트와 서버는 서로 상태를 유지하지 않는다.
모든 요청에 사용자 정보를 포함하는 경우
[웹브라우저 요청]
GET /welcome?user=홍길동 HTTP/1.1
[서버 응답]
HTTP/1.1 200 OK
안녕하세요. 홍길동 님
이 방식은 모든 요청과 링크에 사용자 정보를 포함해야 하고, 브라우저를 완전히 종료했다가 다시 열면 정보가 사라지는 문제가 있다.
쿠키 도입
<로그인>
[웹브라우저 요청]
GET /login HTTP/1.1
user=홍길동
[서버 응답]
HTTP/1.1 200 OK
Set-Cookie: user=홍길동
홍길동님이 로그인했습니다.
[웹브라우저]
쿠키 저장소에 user=홍길동 저장
--------------------------
<로그인 이후 welcome 페이지 접근>
[웹브라우저 요청]
GET /welcome HTTP/1.1
Cookie: user=홍길동 👈 쿠키 저장소에서 조회🍪
웹 브라우저는 서버에 요청을 보낼 때마다 자신의 쿠키 저장소를 확인하고,
해당 도메인과 경로에 맞는 쿠키를 찾아 요청 헤더에 자동으로 포함시킨다.
✨ 브라우저는 쿠키를 수동으로 추가하지 않아도 항상 필요한 쿠키를 찾아 서버로 전송한다.
[서버 응답]
HTTP/1.1 200 OK
안녕하세요. 홍길동 님
---------------------------
<모든 요청에 자동 포함되는 쿠키>
[웹브라우저 요청]
GET /order HTTP/1.1
Cookie: user=홍길동
지정한 서버에 대해 쿠키는 모든 요청에 자동으로 전송되지만, 모든 요청에 쿠키 정보를 무작정 보내면 보안 위험이 발생할 수 있으므로 쿠키 전송을 제한하거나 보안 옵션을 적용하는 방법이 존재한다.
🍪 쿠키
set-cookie: sessionId=abcde1234; expires=Sat, 26-Dec-2020 00:00:00 GMT; path=/; domain=.google.com; Secure
쿠키는 주로 사용자 로그인 세션 관리와 광고 정보 트래킹에 사용된다. 그러나 쿠키는 항상 서버에 전송되므로 네트워크 트래픽을 유발할 수 있다. 만약 서버에 전송하지 않고 브라우저 내부에 데이터를 저장하고 싶다면 Web Storage(localStorage, sessionStorage)를 활용할 수 있다. 또한 주민번호나 신용카드 번호 등 보안에 민감한 데이터는 쿠키에 저장하면 안 된다.
쿠키 생명주기
`expires` 또는 `max-age` 속성을 통해 생명주기를 설정할 수 있다. `Set-Cookie: expires=Sat,26-Dec-2020 04:39:21 GMT`처럼 만료일을 지정하면 해당 날짜에 쿠키가 삭제된다. `Set-Cookie: max-age=3600`처럼 초 단위로 유효 기간을 지정할 수도 있으며, 0이나 음수를 지정하면 쿠키가 즉시 삭제된다. 만료 날짜를 생략하면 세션 쿠키가 되어 브라우저 종료 시 삭제되고, 만료 날짜를 지정하면 영속 쿠키로 해당 날짜까지 유지된다.
쿠키 도메인
domain 속성을 통해 접근 가능한 도메인을 지정할 수 있다. 예를 들어 `domain=example.com`으로 설정하면, 지정한 도메인과 모든 서브도메인(`dev.example.com` 등)에서 쿠키에 접근할 수 있다. 도메인을 생략하면 현재 문서 기준 도메인에서만 접근 가능하다.
쿠키 경로
`path` 속성은 쿠키 접근을 허용할 경로를 지정한다. 예를 들어 `path=/home`으로 설정하면 `/home`과 그 하위 경로(`/home/level1`)에서만 쿠키에 접근 가능하며, `/hello` 같은 다른 경로에서는 접근할 수 없다. 일반적으로 루트 경로(`/`)를 지정하여 모든 경로에서 접근 가능하게 한다.
쿠키 보안
쿠키 보안을 위해서는 Secure, HttpOnly, SameSite 옵션을 사용할 수 있다.
Secure 옵션은 쿠키를 전송할 때 HTTPS 연결에서만 전송되도록 제한한다. 원래 쿠키는 HTTP와 HTTPS를 구분하지 않고 전송되지만, Secure를 적용하면 안전한 HTTPS 연결일 때만 서버로 전송된다.
HttpOnly 옵션은 자바스크립트에서 쿠키에 접근하지 못하도록 하여 XSS 공격을 방지한다. XSS(Cross-Site Scripting) 공격은 악의적인 사용자가 공격 대상 사이트에 스크립트를 삽입하여 의도하지 않은 동작을 수행하게 하는 공격 기법이다. HttpOnly가 설정된 쿠키는 `document.cookie`를 통해 접근할 수 없으며, 오직 HTTP 전송에만 사용된다.
SameSite 옵션은 XSRF(Cross-Site Request Forgery) 공격을 방지하는 데 사용된다. XSRF는 사용자가 의도하지 않은 상태에서 공격자가 의도한 요청을 특정 웹사이트에 보내도록 하는 공격이다. SameSite가 설정된 쿠키는 요청 도메인과 쿠키가 설정된 도메인이 일치할 때만 서버로 전송되므로, 사이트 간 요청 위조를 차단할 수 있다.