HTTP 요청은 그냥 글자 몇 줄처럼 보이죠? 사실은 줄 하나, 빈 줄 하나가 메시지의 경계를 가르는 문법이에요.HTTP와 HTTPS는 뭐가 다를까요?에서는 HTTP가 브라우저와 서버의 대화 규칙이고, HTTPS는 그 대화를 보호된 통로에 싣는 방식이라고 봤어요. 그리고 DoH와 DoT는 DNS 경로를 어디까지 숨겨줄까요?까지 오면서, 이름을 주소로 바꾸는 구간과 그다음 웹 요청 구간도 분리해서 볼 수 있게 됐죠. 근데요, 이제 진짜 웹 서버 앞에 서면 이런 질문이 생겨요.
GET / HTTP/1.1이 한 줄은 정확히 뭘 나눈 걸까요?Host: example.com같은 줄은 어디까지가 이름이고 어디부터가 값일까요?- 헤더가 끝났다는 건 어떻게 알까요?
- 본문이 있는 요청은 어디서부터 어디까지가 본문일까요?
Content-Length와Transfer-Encoding: chunked는 왜 자주 같이 언급될까요?
여기서는 HTTP/1.1의 메시지 모양과 경계 읽기에 집중해요. HTTP/2 프레임, HTTP/3, 캐시 정책, 쿠키 보안, 모든 메서드 의미까지는 깊게 열지 않을게요. 오늘은 “시작 줄, 헤더, 빈 줄, 본문을 정확히 나눠 읽는 감각” 이 목표예요.
왜 HTTP/1.1 문법을 알아야 할까요?
브라우저 개발자 도구나curl -v를 보면 HTTP는 꽤 읽기 쉬워 보여요.
- 첫 줄은 요청의 방향을 정해요.
- 그다음 줄들은 메타데이터를 붙여요.
- 빈 줄은 헤더가 끝났다는 신호예요.
- 빈 줄 뒤에 본문이 올 수 있어요.
주문서의 제목, 메모, 빈 칸, 내용물
카페 주문서를 떠올려볼게요. 주문서 맨 위에는 이렇게 적혀 있어요.“아이스 아메리카노를 주문합니다.”그 아래에는 추가 메모가 붙어요.
“테이크아웃이에요."그리고 줄 하나를 비운 뒤, 쿠폰이나 긴 요청사항이 따로 붙을 수 있어요. HTTP/1.1 메시지도 비슷하게 읽을 수 있어요.
"얼음은 적게 주세요.”
여기서 빈 줄이 중요해요. 사람은 대충 문맥으로 읽을 수 있지만, 서버는 문맥으로 눈치 보지 않아요. 빈 줄이 나오기 전까지는 헤더, 그 뒤부터는 조건에 따라 본문으로 읽어요.
HTTP/1.1 메시지의 큰 모양
HTTP/1.1 메시지는 크게 이렇게 생겼어요.GET 요청에는 보통 본문이 없고, POST 요청이나 200 OK 응답에는 본문이 있을 수 있어요. 중요한 건 본문이 있느냐보다, 본문이 어디서 시작하고 얼마나 이어지는지를 어떻게 판단하느냐예요.
요청 줄은 세 조각으로 읽어요
가장 익숙한 요청 줄부터 볼게요.
즉 이 줄은 “
/articles/42라는 대상을 HTTP/1.1 방식으로 GET하고 싶어요” 라는 말이에요.
여기서 request target은 항상 전체 URL처럼 보이지는 않아요. 일반적인 웹 서버 요청에서는 /path?query 같은 origin-form이 흔해요. 프록시를 향한 요청에서는 전체 URL처럼 보이는 absolute-form이 쓰일 수 있고, CONNECT처럼 터널을 만들 때는 host:port 모양의 authority-form도 나와요.
처음엔 전부 외울 필요는 없어요. 다만 “요청 줄의 두 번째 칸은 그냥 URL 전체가 아니라, 요청 대상 표현 방식이다” 라고 잡아두면 좋아요.
응답의 첫 줄은 결과표예요
응답은 이렇게 시작해요.200 OK는 성공, 404 Not Found는 대상을 찾지 못함, 502 Bad Gateway는 앞단이 뒤쪽 서버와 대화하다가 실패한 장면처럼 읽을 수 있어요. 상태 코드의 의미 자체는 HTTP semantics 쪽 이야기이고, 여기서는 응답도 첫 줄에서 결과를 먼저 말한다는 구조를 붙잡으면 충분해요.
404 Not Found는 결과표이고, Content-Type과 Content-Length는 뒤에 오는 본문을 어떻게 읽을지 알려주는 메모예요.
헤더 필드는 이름과 값의 목록이에요
시작 줄 다음에는 헤더 필드가 이어져요.field-name: field-value 형태예요.
여기서
Host는 HTTP/1.1에서 특히 중요해요. 하나의 서버나 IP 뒤에 여러 도메인이 함께 있을 수 있기 때문에, 서버는 Host를 보고 어느 사이트를 향한 요청인지를 고를 수 있어요.
HTTP 필드 이름은 대소문자를 구분하지 않는 이름으로 다뤄요. 하지만 실제 출력에서는
Content-Type, content-type, CONTENT-TYPE처럼 여러 모양을 만날 수 있어요. 사람이 읽을 때는 같은 필드 이름인지부터 보고, 값의 의미를 따로 읽으면 돼요.빈 줄은 왜 그렇게 중요할까요?
HTTP/1.1 메시지를 직접 보면 헤더와 본문 사이에 아무것도 없어 보이는 줄이 하나 있어요.본문 길이는 어떻게 알까요?
TCP는 바이트 흐름이에요. 택배 상자처럼 “여기까지가 메시지 하나” 라고 자동으로 잘라주지 않아요. 그래서 HTTP/1.1은 본문 길이를 따로 판단해야 해요. 가장 단순한 신호는Content-Length예요.
Transfer-Encoding: chunked가 나올 수 있어요.
0인 조각이 나오면 끝이라고 읽어요.
여기서 디버깅 감각이 중요해요. 본문 길이를 판단하는 신호가 애매하거나 서로 충돌하면, 프록시와 서버가 메시지 경계를 다르게 읽을 수 있어요. 그래서
Content-Length와 Transfer-Encoding은 단순한 부가 정보가 아니라, HTTP/1.1 메시지를 안전하게 자르는 핵심 신호예요.
실제로 한 요청과 응답을 같이 읽어볼까요?
아주 작은 HTTP/1.1 대화를 펼쳐보면 이런 모양이에요.- 요청 줄에서 메서드와 대상을 봐요.
Host로 어느 사이트를 향하는지 봐요.- 빈 줄에서 요청 헤더가 끝났다고 봐요.
- 응답 상태 줄에서 결과를 봐요.
- 응답 헤더에서 본문 형식과 길이를 봐요.
- 빈 줄 뒤의 본문을 그 길이만큼 읽어요.
HTTPS면 이 모양이 안 보일까요?
여기서 기본편과 다시 연결해볼게요. HTTPS는 HTTP 메시지 모양을 없애는 게 아니에요. 보통은 TLS 통로 안에 HTTP 메시지가 들어가요. 그래서 네트워크 중간에서 패킷을 보면 HTTP/1.1 시작 줄과 헤더가 그대로 보이지 않을 수 있지만, 브라우저와 서버가 TLS를 풀어 처리하는 안쪽에서는 여전히 HTTP 의미와 메시지 구조가 중요해요. 정확히는 실제 구현에서는 TLS 레코드, HTTP 버전, 프록시 구성, ALPN 협상 등에 따라 보이는 층이 달라질 수 있어요. 하지만 초반 감각으로는 HTTPS는 HTTP 문법을 보호된 통로 안에 넣는다고 잡아도 좋아요.잘못 읽기 쉬운 함정
HTTP/1.1 메시지는 읽기 쉬워 보여서 오히려 대충 읽기 쉬워요.
특히 마지막 줄이 다음 글로 이어져요. HTTP/1.1은 사람이 읽기 쉬운 텍스트 줄처럼 보이지만, HTTP/2부터는 프레임이라는 단위가 훨씬 중요해져요.
자, 정리해볼까요?
- HTTP/1.1 메시지는 시작 줄, 헤더 필드, 빈 줄, 본문으로 나눠 읽어요.
- 요청의 첫 줄은
method,request target,HTTP version으로 나뉘어요. - 응답의 첫 줄은
HTTP version,status code,reason phrase로 나뉘어요. - 빈 줄은 단순한 장식이 아니라 헤더가 끝났다는 경계예요.
Content-Length와Transfer-Encoding은 본문을 어디까지 읽을지 정하는 중요한 신호예요.
GET / HTTP/1.1 같은 줄을 보면 그냥 예시 문자열이 아니라, 서버와 프록시가 메시지 경계를 맞추기 위해 읽는 문법으로 보일 거예요.
이어서 보면 좋은 글
- HTTP와 HTTPS는 뭐가 다를까요? — HTTP가 대화 규칙이고 HTTPS가 보호 통로라는 기본 감각을 다시 잡고 싶을 때 좋아요.
- TLS 핸드셰이크는 실제로 어떻게 한 단계씩 진행될까요? — HTTPS에서 HTTP 메시지가 오가기 전, 보호 통로가 어떻게 준비되는지 이어서 보기 좋아요.
- QUIC은 왜 UDP 위에서 돌아갈까요? — HTTP/3에서는 HTTP가 어떤 바닥 위로 옮겨 가는지 큰 그림을 보고 싶을 때 좋아요.
이어서 볼 질문
“HTTP/1.1은 줄 단위 메시지였어요. 그럼 HTTP/2는 왜 프레임과 스트림이라는 말을 쓸까요?”다음에는 HTTP/2 프레임과 멀티플렉싱을 열어서, 한 연결 안에서 여러 요청이 어떻게 섞여 흐르는지 볼게요.