ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

HTTP 헤더 설계

도입

이름을 예쁘게 짓는게 아니라, 그 필드가 정말 헤더여야 하는지부터 값 문법·캐시·중간자·보안·등록까지 함께 정의해 상호운용성을 확보하는 일이다

API를 만들다 보면 새로운 요구사항이 생길 때마다 헤더 하나를 추가하고 싶어집니다. 인증 정보, 디버그 플래그, 버전, 기능 토글, 실험용 옵션, 내부 추적 ID 같은 정보들이 대표적입니다.

하지만 HTTP 헤더는 단순한 문자열 슬롯이 아닙니다. 이름은 중간자와 캐시를 지나며 해석되고, 값 문법은 라이브러리와 프록시를 통과하며 조합되고, 잘못 설계하면 기존 표준 의미를 깨뜨리거나 보안 문제를 만들 수도 있습니다. 그래서 헤더 설계는 “일단 하나 만들어 쓰자”가 아니라, HTTP 위에서 오래 살아남을 수 있는 계약을 설계하는 일로 봐야 합니다.

필요성

헤더 설계를 제대로 이해하면 커스텀 확장을 더 안전하게 만들 수 있고, 프록시·캐시·브라우저·라이브러리를 통과하는 동안 생기는 상호운용성 문제를 훨씬 일찍 줄일 수 있다

HTTP는 헤더를 확장 지점으로 폭넓게 허용합니다. 그래서 헤더 하나를 추가하는 일 자체는 쉽습니다. 문제는 그 헤더가 다양한 클라이언트, 프록시, 캐시, 게이트웨이, 로깅 시스템을 모두 지나야 한다는 데 있습니다.

설계가 부실하면 이름 충돌, 파싱 실패, 캐시 오동작, 리다이렉트 시 민감 정보 유출, 중간자에 의한 변형 같은 문제가 생길 수 있습니다. 반대로 처음부터 기준을 잡고 설계하면, 새 헤더는 애플리케이션 확장을 돕는 좋은 도구가 됩니다.

헤더 설계를 꼭 신중히 봐야 하는 상황
  • 사내 API나 외부 공개 API에 새 요청/응답 헤더를 추가할 때
  • 기존 표준 헤더로는 부족해 보이는 기능을 확장할 때
  • 프록시, CDN, API 게이트웨이, 브라우저 캐시를 함께 통과할 때
  • 리다이렉트, 인증, A/B 테스트, 기능 협상 같은 제어 정보를 실어야 할 때
  • 오래 유지될 공용 프로토콜이나 SDK를 설계할 때

정의

현대 HTTP 사양에서 헤더는 더 넓게는 field라고 보는 편이 정확하며, 헤더 섹션과 트레일러 섹션에서 쓰일 수 있는 이름-값 확장 지점이다

최근 HTTP 사양은 흔히 “헤더”라고 부르던 것을 더 넓게 field라고 부릅니다. 이유는 이름-값 쌍이 메시지의 header section뿐 아니라 trailer section에도 존재할 수 있기 때문입니다.

다만 실무와 문서에서는 여전히 “HTTP 헤더”라는 표현이 널리 쓰입니다. 그래서 이 글도 이해하기 쉽게 헤더라는 말을 계속 쓰지만, 엄밀히 말하면 설계 대상은 HTTP field라고 보는 편이 더 정확합니다.

핵심 문장
HTTP 헤더 설계는 “문자열 하나 추가”가 아니라, 이름-값 쌍 하나의 의미를 HTTP 전체 생태계 안에서 정의하는 작업입니다.

핵심 원리

새 헤더를 설계할 때 가장 중요한 원칙은 기존 HTTP 의미를 비틀지 않고, 새로운 필드가 모르는 수신자에게도 최대한 안전하게 무시되거나 전달될 수 있도록 만드는 데 있다

좋은 HTTP 확장은 기존 생태계를 존중해야 합니다. 기존 메서드, 상태 코드, 표준 헤더의 의미를 새 애플리케이션이 마음대로 덮어쓰면, 프록시와 캐시, 공용 클라이언트가 기대하는 동작이 깨집니다.

반대로 잘 설계된 헤더는 모르는 구현이 보더라도 전체 메시지를 망가뜨리지 않고, 아는 구현은 그 의미를 정확히 해석할 수 있습니다. 결국 헤더 설계의 핵심은 확장 가능성과 상호운용성입니다.

기본 구조

헤더를 설계할 때는 이름 하나를 정하는 것보다, 왜 헤더가 필요한지부터 값 문법·반복 가능성·캐시 영향·보안·등록까지 체크리스트처럼 점검하는 편이 훨씬 안전하다
설계 항목 무엇을 정해야 하나 왜 중요한가
적합성 이 정보가 정말 헤더에 있어야 하는가 바디나 쿼리가 더 자연스러운 정보를 굳이 헤더로 만들지 않기 위해
이름 짧고 설명적인 이름인지, 기존 이름과 충돌하지 않는지 상호운용성과 가독성, 이름 공간 관리에 직결됨
값 문법 싱글턴인지 리스트인지, 반복 가능성은 어떤지 파싱과 결합 규칙이 애매하면 구현이 쉽게 갈라짐
전달 범위 request/response/trailer 어디에서 유효한지 잘못된 위치에 등장했을 때 처리 규칙까지 정해야 함
중간자 프록시가 삽입/삭제/변경해도 되는지 실전 HTTP는 항상 중간자를 거칠 수 있기 때문
캐시 응답 선택에 영향을 주는지, Vary가 필요한지 잘못 설계하면 캐시가 틀린 응답을 재사용함
보안/개인정보 민감한 값인지, 리다이렉트 시 제거해야 하는지 유출, 오용, 로그 노출, 중간자 노출을 줄이기 위해
등록/문서화 IANA 등록이 필요한지, 공개 문서가 있는지 공용 확장은 결국 문서와 레지스트리 위에서 오래 살아남음

패턴 1. 먼저 헤더가 정말 필요한가

좋은 헤더 설계는 이름부터 짓지 않고, 이 정보가 헤더에 있어야 하는 이유부터 따지는 데서 시작된다

모든 새 정보가 헤더에 적합한 것은 아닙니다. HTTP 기반 프로토콜 설계 지침은 새 헤더가 보통 세 경우에 특히 적합하다고 봅니다. 첫째, 프록시나 캐시 같은 중간자에게 유용한 정보일 때. 둘째, 클라이언트/서버 라이브러리 같은 일반적인 HTTP 소프트웨어가 알면 좋은 정보일 때. 셋째, 표현 형식 제약 때문에 그 값을 메시지 바디에 넣기 어려울 때입니다.

반대로 이 조건이 아니라면, 애플리케이션 전용 정보는 바디나 URL 쿼리 같은 다른 위치가 더 자연스러울 수 있습니다. 그리고 무엇보다 기존 HTTP 메서드, 상태 코드, 표준 헤더의 의미를 자기 애플리케이션 맥락으로 다시 정의해서는 안 됩니다.

헤더가 비교적 잘 맞는 정보
- 캐시나 프록시가 알아야 하는 메타데이터
- 인증, 콘텐츠 협상, 표현 선택 같은 전송 제어 정보
- 바디 형식이 담기 어려운 공통 제어값

헤더보다 바디/쿼리가 더 자연스러운 정보
- 순수 비즈니스 데이터
- 도메인 모델의 내부 속성
- 표현 본문과 강하게 결합된 내용
실전 감각
헤더는 “메시지를 어떻게 다뤄야 하는가”에 가깝고, 바디는 “실제 내용이 무엇인가”에 가깝습니다. 이 구분이 흐려질수록 커스텀 헤더가 난립하기 쉽습니다.

패턴 2. 이름은 어떻게 지을까

새 헤더 이름은 짧지만 설명적이어야 하고, 너무 일반적인 단어를 독점하지 말아야 하며, 더 이상 X- 접두사를 기본 습관처럼 쓰지 않는 편이 맞다

HTTP 필드 이름은 대소문자를 구분하지 않습니다. 하지만 상호운용성을 위해 새 이름은 가능하면 문자, 숫자, 하이픈, 점 정도로 제한하고, 첫 글자는 문자로 시작하는 편이 좋습니다. 특히 밑줄(_)은 일부 비HTTP 게이트웨이나 인프라에서 문제를 일으킬 수 있어 피하는 쪽이 안전합니다.

이름은 짧되 설명적이어야 합니다. 너무 일반적인 이름은 나중에 더 넓은 의미로 쓰일 가능성을 막아 버리고, 너무 긴 이름은 전송 오버헤드와 가독성을 해칩니다. 애플리케이션 전용 헤더라면 그 애플리케이션 식별자를 접두사처럼 붙이는 방식이 더 낫습니다. 예를 들어 Acme-TraceTrace보다 충돌 가능성이 낮고, X-Acme-Trace보다 현대적인 방향입니다.

좋지 않은 예
- X-User-Mode
- Description
- my_header_flag

더 나은 방향
- Acme-User-Mode
- Acme-Trace
- Example-Retry-After
X-를 쓰지 않는 이유
X-는 예전에는 비표준 이름 공간을 표시하려는 관습이었지만, 실제 배포가 시작되면 표준 이름과 이중 호환 비용을 만드는 경우가 많아 현재는 새 이름에 권장되지 않습니다.

패턴 3. 값 문법은 어떻게 정의할까

헤더 설계에서 이름보다 더 자주 문제를 만드는 것은 값 문법이며, 특히 반복 필드 결합, 콤마 포함 데이터, 싱글턴 여부를 명확히 정의하지 않으면 구현이 쉽게 갈라진다

HTTP는 같은 필드 이름이 여러 번 등장할 수 있고, 일반적으로 반복된 필드 라인은 콤마로 결합된 하나의 값처럼 취급될 수 있습니다. 그래서 새 헤더를 만들 때는 이 필드가 리스트인지, 싱글턴인지, 반복이 가능한지, 그리고 중복이 들어오면 어떻게 처리할지를 반드시 문서화해야 합니다.

여기서 가장 흔한 함정이 콤마입니다. 값 안에 콤마가 자연스럽게 들어갈 수 있으면, 반복 필드 결합과 구분이 섞여 파싱이 곤란해집니다. 그래서 새 헤더는 값 문법을 ABNF로 엄격하게 정의하거나, 가능하면 Structured Fields를 사용하는 편이 더 안전합니다.

반복 필드 결합 예시
Example-Field: Foo, Bar
Example-Field: Baz

→ 결합된 값은 대체로 "Foo, Bar, Baz"처럼 해석될 수 있음
Structured Fields 스타일 예시
Acme-Retry: 30
Acme-Debug: ?1
Acme-Features: login, billing
Acme-Policy: mode="strict", retries=3

Structured Fields를 쓰면 값이 Item, List, Dictionary 같은 공통 타입 위에 올라가므로, 새 헤더마다 파서를 새로 설계하는 부담이 줄어듭니다. 특히 엄격한 파싱과 반복/콤마 처리 규칙을 통일하기 쉬워서 신규 헤더에 잘 맞습니다.

패턴 4. 중간자·캐시·보안까지 같이 설계하기

새 헤더를 만든다면 값 문법만 정의해서는 부족하고, 프록시가 만져도 되는지·트레일러에서 허용되는지·캐시에 어떤 영향을 주는지·리다이렉트 시 제거해야 하는지까지 같이 정해야 한다

HTTP는 항상 종단 간 1:1 통신이 아닙니다. 프록시, CDN, 게이트웨이, 브라우저 캐시 같은 중간자가 필드를 보고 행동할 수 있습니다. 따라서 새 필드가 request에만 쓰이는지, response에만 쓰이는지, trailer에서도 허용되는지부터 문서에 명확히 써야 합니다.

또한 이 필드가 end-to-end인지 hop-by-hop인지 구분해야 합니다. hop-by-hop 필드라면 Connection 헤더와의 관계를 고려해야 하고, 그렇지 않다면 프록시가 모르는 필드라도 전달할 수 있게 설계되어야 합니다. 기본적으로 HTTP의 새 필드는 모르는 수신자가 안전하게 무시하거나 프록시가 전달할 수 있는 방향이 유리합니다.

캐시도 반드시 고려해야 합니다. 만약 어떤 request 헤더가 응답 내용 선택에 영향을 준다면, 그 자원은 Vary를 보내거나 아예 캐시 불가로 설계해야 할 수 있습니다. 그렇지 않으면 캐시가 다른 요청에 대해 잘못된 응답을 재사용할 수 있습니다.

보안도 중요합니다. 민감한 필드라면 프록시 로그나 중간자에 노출될 수 있고, 자동 리다이렉트 시 다른 origin으로 넘어가며 유출될 수 있습니다. 따라서 프라이버시 데이터, 인증 관련 상태, 보안 토큰류는 특히 주의해서 문서화해야 합니다.

새 헤더에서 꼭 문서화할 질문
  • request 전용인가, response 전용인가, 둘 다 가능한가
  • header section에서만 쓰는가, trailer도 허용하는가
  • 프록시가 삽입·삭제·수정해도 되는가
  • hop-by-hop인가, end-to-end인가
  • 응답 선택에 영향을 준다면 Vary가 필요한가
  • 리다이렉트 시 제거하거나 수정해야 하는가
  • 개인정보나 인증 정보가 들어가는가
핵심 포인트
헤더는 메시지의 전송·선택·처리 방식에 영향을 주기 쉽기 때문에, 바디보다 훨씬 더 넓은 시스템 영향 범위를 가질 수 있습니다. 설계가 가벼워 보여도 운영 영향은 결코 가볍지 않습니다.

등록과 문서화

공개적으로 쓸 새 헤더라면 결국 문서와 레지스트리로 귀결되며, 이름과 상태를 남기지 않으면 시간이 지날수록 사실상의 사설 규칙이 공용 프로토콜을 오염시킬 수 있다

HTTP 필드 이름은 IANA의 HTTP Field Name Registry가 이름 공간을 관리합니다. 새 이름은 그 등록 절차를 통해 permanent, provisional, deprecated, obsoleted 같은 상태를 가질 수 있습니다.

실무 기준으로 보면, 사내 실험용 헤더를 당장 전부 등록해야 하는 것은 아닐 수 있습니다. 하지만 외부 공개 API, 공개 SDK, 여러 조직이 함께 쓰는 프로토콜이라면 최소한 문서화와 등록을 고려하는 편이 맞습니다. 결국 오래 살아남는 헤더는 코드보다 문서가 먼저 정리되어야 합니다.

한계와 주의점

헤더는 확장 지점으로 매우 편리하지만, 지나치게 많이 쓰면 오히려 API 의미가 분산되고 바디·쿼리·표준 메서드가 해야 할 역할까지 빼앗아 설계를 흐리게 만들 수 있다

헤더는 공용 메타데이터에 강하지만, 모든 정보를 헤더로 옮기는 순간 메시지 의미가 분산됩니다. 요청 본문에 있어야 할 도메인 데이터가 헤더로 쪼개지고, 캐시와 프록시 영향도 더 커집니다.

또한 헤더는 라이브러리와 인프라에서 자동 처리되는 경우가 많기 때문에, 애플리케이션 개발자가 직접 통제한다고 생각한 값이 중간에서 잘리거나 합쳐지거나 변형될 수도 있습니다. 그래서 새 헤더는 “쉽게 추가할 수 있다”보다 “추가한 뒤 오랫동안 책임질 수 있는가”를 먼저 따져야 합니다.

자주 하는 실수

HTTP 헤더 설계가 어그러지는 가장 흔한 이유는 이름을 먼저 만들고 나중에 의미를 맞추려 하거나, 기존 HTTP 의미를 자기 서비스 사정에 맞게 비틀려는 데 있다
  • 기존 표준 헤더를 다른 의미로 재정의
  • 바디에 있어야 할 데이터를 습관적으로 헤더로 올림
  • X- 접두사를 아직도 새 헤더 기본값처럼 씀
  • 이름은 정했지만 값 문법과 반복 규칙을 문서화하지 않음
  • 싱글턴 헤더인데 중복 발생 시 처리 규칙을 정하지 않음
  • 캐시 영향이 있는 요청 헤더를 만들고도 Vary를 빠뜨림
  • 민감한 필드를 리다이렉트나 로그 노출 경로까지 고려하지 않음
  • 외부 공개 헤더인데 등록과 명세를 끝까지 미루고 사설 이름으로 버팀

실무 루틴

새 헤더를 만들 때는 이름을 정하는 것보다 먼저 기존 표준으로 해결 가능한지 확인하고, 정말 필요할 때만 이름·문법·캐시·보안·등록을 한 번에 설계하는 순서가 맞다
  1. 이 요구사항이 정말 새 헤더를 필요로 하는지 먼저 본다.
  2. 기존 메서드, 상태 코드, 표준 헤더, 바디, 쿼리로 풀 수 없는지 확인한다.
  3. 새 이름이 필요하다면 짧고 설명적으로 짓고 X-는 쓰지 않는다.
  4. 값 문법을 명확히 정의하고, 가능하면 Structured Fields를 검토한다.
  5. 반복 가능성, 싱글턴, 중복 처리, 콤마 포함 값 규칙을 정한다.
  6. request/response/trailer, 중간자 변경 가능성, hop-by-hop 여부를 문서화한다.
  7. 캐시와 Vary, 리다이렉트 시 필드 처리, 보안/개인정보 영향을 검토한다.
  8. 공개 확장이라면 IANA 등록과 장기 문서화를 고려한다.

디버깅

헤더 설계 문제를 디버깅할 때는 “서버가 못 읽는다”로 뭉뚱그리지 말고, 이름 문제인지·문법 문제인지·중복 결합 문제인지·중간자 문제인지·캐시 문제인지 먼저 나눠 보는 편이 빠르다
1
문제가 이름 충돌/비표준 이름인지부터 본다.
2
값 문법이 애매해서 파서마다 다르게 읽는 것은 아닌지 확인한다.
3
같은 필드가 여러 번 들어올 때 결합 규칙이 깨지는지 점검한다.
4
프록시/CDN/API 게이트웨이가 필드를 삭제·수정·정규화하는 것은 아닌지 본다.
5
응답이 캐시된다면 Vary 누락이나 캐시 정책 문제를 함께 본다.
6
리다이렉트나 재시도, 다른 origin 전환 시 민감한 헤더가 계속 따라가는지도 점검한다.
헤더 설계 문제를 볼 때 먼저 나눌 질문
- 이 정보가 정말 헤더에 있어야 하는가?
- 이름은 기존 표준과 충돌하지 않는가?
- 값 문법이 반복/콤마/싱글턴을 견디는가?
- 프록시와 캐시는 이 필드를 어떻게 다룰까?
- 보안상 리다이렉트나 로그에 노출되면 위험한가?
- 등록과 문서가 충분한가?

요약

HTTP 헤더를 잘 설계한다는 것은 새 이름을 하나 만드는 일이 아니라, 그 정보가 정말 헤더로 존재해야 하는지 판단하고, 이름과 값 문법을 엄격히 정의하며, 중간자·캐시·보안·등록까지 포함해 오래 살아남을 수 있는 필드 계약을 만드는 데 있다
  • ✅ 새 헤더가 필요하기 전에 기존 표준, 바디, 쿼리로 해결 가능한지 먼저 본다.
  • ✅ 기존 HTTP 메서드, 상태 코드, 표준 헤더의 의미를 재정의하지 않는다.
  • ✅ 이름은 짧고 설명적으로 짓고, 너무 일반적인 단어와 X- 접두사는 피한다.
  • ✅ 값 문법은 싱글턴/리스트/반복/콤마 처리까지 문서화한다.
  • ✅ 가능하면 Structured Fields를 검토해 파싱 문제를 줄인다.
  • ✅ request/response/trailer, 중간자, hop-by-hop 여부를 명확히 한다.
  • ✅ 캐시에 영향이 있으면 Vary와 캐시 정책을 같이 설계한다.
  • ✅ 공개 확장이라면 IANA 등록과 장기 문서화를 고려한다.
728x90