HTTP 프로토콜: HTTP/1.1·HTTP/2·HTTP/3 차이, 연결 관리, 캐싱, 보안 실전

이 글의 핵심

HTTP/1.1·2·3의 프레이밍·혼잡 제어 차이, Keep-Alive와 연결 수명, 캐시 검증·키·무효화, Accept·Vary 기반 협상, 게이트웨이·CDN·재시도까지 프로덕션 HTTP 내부를 한 번에 정리합니다.

들어가며

HTTP(HyperText Transfer Protocol)는 월드 와이드 웹과 대부분의 공개 API가 공유하는 애플리케이션 계층 프로토콜입니다. 브라우저 탭 하나, 모바일 앱의 백엔드 호출, 마이크로서비스 간 동기 호출까지 “요청을 보내고 응답을 받는다”는 경험의 대부분이 HTTP(보통 HTTPS) 위에 있습니다. 버전은 HTTP/1.1의 단순함과 호환성, HTTP/2의 멀티플렉싱, HTTP/3의 QUIC 기반으로 진화했으며, 보안 쪽에서는 TLS 1.3, HSTS, 콘텐츠 보안 정책(CSP)이 사실상 기본 설정이 되었습니다. 이 글은 프로토콜 스펙과 실무(curl, REST, 캐시, CDN)를 한 흐름으로 잇습니다.

왜 HTTP가 기본이 되었나요?

브라우저·모바일 SDK·API 게이트웨이·CDN이 같은 의미론(메서드·상태 코드·헤더)을 공유하기 때문입니다. 캐시·압축·TLS 종단까지 생태계가 HTTP에 최적화되어 있어, 새로운 전송을 매번 도입하기보다 HTTP 위에서 리소스 모델·보안 헤더를 다듬는 편이 비용 대비 효과가 큽니다.

프로덕션에서 주의할 점

  • 캐시 헤더는 “빠름”과 “최신성”의 계약입니다. Cache-Control 실수는 옛 데이터 노출이나 과도한 원본 부하로 이어집니다.
  • CORS는 브라우저 정책일 뿐, 서버 간 호출 보안을 대신하지 않습니다. 인증·네트워크 경계를 함께 설계합니다.
  • HTTP/3는 UDP 443이 막히면 자동으로 HTTP/2로 폴백되는 경우가 많지만, 의도한 경로인지 로그와 ALB/CDN 설정으로 확인하는 것이 좋습니다.

프로토콜 개요

역사 및 개발 배경

HTTP는 1990년대 초 CERN에서 문서 링크를 전 세계에 퍼뜨리기 위해 시작되었으며, RFC 1945(HTTP/1.0), RFC 2068과 RFC 2616(HTTP/1.1)으로 대중화되었습니다. HTTP/1.1은 이후 RFC 7230~7235로 다시 정리되었고, 2022년에는 버전과 무관한 의미론(RFC 9110), 캐싱(RFC 9111), HTTP/1.1 메시지 형식(RFC 9112)으로 재편되었습니다. HTTP/2는 한 TCP 연결에서 다중 요청을 다루기 위해 2015년 RFC 7540으로 표준화되었고, 2022년 RFC 9113으로 개정되었습니다. HTTP/3(RFC 9114)는 QUIC(UDP 기반) 위에서 TLS 1.3과 혼잡 제어·연결 마이그레이션을 일체화해, 무선·패킷 손실 환경에서 체감 성능을 개선하는 방향으로 정착했습니다(2026년 기준 주요 CDN·브라우저가 기본 지원).

OSI 7계층에서의 위치

HTTP는 OSI 7계층(응용 계층)에 해당합니다. 전송 계층에서는 전통적으로 TCP(HTTP/1.1, HTTP/2) 또는 QUIC(HTTP/3)이 사용되며, TLS는 그 사이에서 HTTP 바이트를 암호화합니다(HTTP/3에서는 TLS 1.3이 QUIC 안에 들어갑니다). DNS로 호스트를 해석한 뒤, 443/tcp 또는 443/udp(QUIC)로 리소스 표현(representation)을 주고받는 모델입니다.

핵심 특징

특징설명
무상태(Stateless)서버는 기본적으로 이전 요청을 기억하지 않음(세션은 쿠키·토큰·서버 저장소로 보완).
요청·응답클라이언트가 요청을 시작하며, 서버가 상태 코드·헤더·본문으로 응답합니다.
리소스 지향URL로 대상을 식별하며, 메서드로 의도(GET 조회, POST 생성 등)를 표현합니다.
협상Accept, Accept-Encoding, Accept-Language 등으로 표현 형식을 맞춥니다.
캐시 친화검증 헤더(ETag)와 Cache-Control으로 대역폭·지연을 줄입니다.

동작 원리

Request / Response

HTTP 메시지는 시작 줄, 헤더 필드, 빈 줄, (선택) 본문으로 구성됩니다.

요청 시작 줄(HTTP/1.1 예)

GET /api/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json

응답 시작 줄

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: private, max-age=60

메서드(안전·멱등)

메서드일반적 용도안전멱등(이상적)
GET조회예예
HEAD메타만예예
POST생성·처리아니오보장 안 함
PUT전체 치환아니오예
PATCH부분 수정아니오보장 안 함(설계에 따라 멱등하게 만들 수 있음)
DELETE삭제아니오예

멱등: 같은 요청을 여러 번 해도 서버 상태가 동일하게 유지되는 성질(네트워크 재시도 설계에 중요).

상태 코드 개요

  • 2xx: 성공(200 OK, 201 Created, 204 No Content)
  • 3xx: 리다이렉트(301, 302, 307, 308—메서드 보존 여부 주의)
  • 4xx: 클라이언트 오류(400, 401, 403, 404, 429)
  • 5xx: 서버 오류(500, 502, 503, 504)

헤더 하이라이트

헤더역할
Host가상 호스팅·SNI와 함께 동일 IP의 여러 사이트 구분(HTTP/1.1에서 필수 헤더).
AuthorizationBearer, Basic 등 인증 자격 증명.
Content-Type본문 MIME(application/json 등).
Cache-Control캐시 가능 여부·수명(max-age, no-store 등).
ETag / Last-Modified조건부 요청·검증.

HTTP/2: 멀티플렉싱

HTTP/2는 하나의 TCP 연결에서 여러 스트림을 동시에 다룹니다. HTTP/1.1에서 요청마다 반복되던 헤더는 HPACK 압축으로 줄입니다. 핵심은 HOL(Head-of-Line) 블로킹을 애플리케이션 레벨에서 완화한다는 점이며, TCP 레벨 HOL은 여전히 남습니다.

HTTP/3: QUIC 위의 HTTP

HTTP/3는 QUIC 위에서 동작합니다. TLS 1.3이 QUIC에 통합되고, 패킷 손실 시 영향 범위가 스트림 단위로 줄어드는 경우가 많아 Wi‑Fi 이동·손실 큰 링크에서 유리할 수 있습니다.

%% 실행 예제
flowchart TB
  subgraph app [응용 계층]
    H3[HTTP/3]
  end
  subgraph quic [전송·암호화]
    Q[QUIC + TLS 1.3]
  end
  subgraph net [네트워크]
    U[UDP]
    IP[IP]
  end
  H3 --> Q --> U --> IP
sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: HTTP Request (method, path, headers)
  S->>C: HTTP Response (status, headers, body)

HTTP/1.1·HTTP/2·HTTP/3 심화 비교

세 버전은 모두 요청 메시지의 의미(메서드·상태·헤더 필드)를 유지하려 하지만, 바이트를 소켓에 얹는 방식(프레이밍)과 전송 계층과의 관계가 다릅니다. 운영·관측에서는 “같은 URL인데 왜 h2만 느리지?”처럼 버전별 특성이 증상으로 드러납니다.

HTTP/1.1: 텍스트, 순차성, TCP의 HOL

HTTP/1.1 메시지는 기본적으로 바이트 스트림 위의 텍스트 시작 줄 + 헤더입니다. 청크 전송(Transfer-Encoding: chunked)은 본문을 조각으로 보내도록 하며, 클라이언트가 파이프라인을 구현해도 응답 순서 고정·중간 응답 블로킹 때문에 널리 쓰이지 않았습니다. 그 결과 동시에 많은 리소스를 가져오려면 브라우저가 여러 TCP 연결을 병렬로 열거나(Connection 제한), 도메인 샤딩 같은 우회가 흔했습니다.

TCP 수준의 Head-of-Line 블로킹은 한 연결에서 패킷 손실이 나면 이후 세그먼트가 순서 복구를 기다리며 지연된다는 뜻입니다. HTTP/1.1은 요청 단위로는 순차적이고, 여러 연결을 쓰면 오버헤드·서버 부하가 늘어납니다.

HTTP/2: 바이너리 프레이밍, HPACK, 스트림·우선순위

HTTP/2는 동일한 TCP 연결 위에 HTTP/2 프레임(DATA, HEADERS, SETTINGS, WINDOW_UPDATE 등)을 얹습니다. 요청·응답은 스트림 ID로 구분되고, HEADERS + DATA 프레임으로 나뉩니다. HPACK은 헤더를 정적·동적 테이블로 압축해 반복 필드(쿠키, User-Agent, 긴 경로) 비용을 줄입니다.

HTTP/2의 멀티플렉싱은 애플리케이션 관점에서 한 연결로 병렬 요청을 처리합니다. 다만 손실된 TCP 세그먼트가 복구될 때까지 그 연결 전체가 영향을 받을 수 있어, TCP HOL은 남습니다. RFC 7540의 우선순위 체계(가중치와 의존성 트리, PRIORITY 프레임)는 구현마다 지원이 들쑥날쑥해 RFC 9113에서 폐기(deprecated)되었고, 지금은 HTTP/2와 HTTP/3에 공통으로 쓰이는 RFC 9218의 Priority 헤더(urgency, incremental)가 대안입니다.

서버 푸시(PUSH_PROMISE)는 스펙상 가능하지만, 이미 캐시에 있는 리소스까지 밀어 넣는 낭비 문제로 Chrome이 2022년에 기본 비활성화했습니다. 대신 <link rel="preload">와 103 Early Hints가 쓰입니다.

HTTP/3: QUIC 위의 HTTP, 스트림별 손실, 0-RTT 주의

HTTP/3는 QUIC(UDP 기반) 위에서 동작합니다. TLS 1.3이 QUIC에 통합되어 1-RTT 핸드셰이크가 일반적이며, 재방문 시 0-RTT는 지연 감소에 유리하지만 0-RTT 데이터는 재전송 공격(replay)에 노출되므로, 서버는 멱등하지 않은 요청을 0-RTT로 받지 않도록 제한해야 합니다(필요하면 425 Too Early로 응답).

QUIC은 스트림마다 순서 보장을 따로 하므로, 한 스트림에 속한 패킷이 손실되어도 다른 스트림의 데이터는 기다리지 않고 애플리케이션에 전달됩니다. 무선·모바일처럼 손실이 잦은 링크에서 체감 지연이 개선될 수 있습니다. 연결 마이그레이션(connection ID)은 Wi‑Fi ↔ 셀룰러 전환 시 세션 유지에 도움이 될 수 있으나, 로드밸런서·방화벽이 UDP 443과 QUIC 인식을 해야 합니다.

한눈에 보는 차이

관점HTTP/1.1HTTP/2HTTP/3
프레이밍텍스트 메시지바이너리 프레임(H2)QUIC 스트림 + H3 프레이밍
전송TCPTCPQUIC(UDP)
병렬성다중 연결·순차 위주단일 TCP에서 스트림 멀티플렉싱멀티플렉싱 + 스트림 단위 손실 처리
헤더 압축없음(본문만 gzip 등으로 압축)HPACKQPACK
전형적 운영 이슈연결 수·순서TCP HOL, 프록시 호환UDP 차단, 0-RTT 정책

연결 관리와 Keep-Alive

HTTP/1.1에서의 Connection과 Keep-Alive

HTTP/1.0은 요청마다 연결을 닫는 것이 기본이었고, 연결을 유지하려면 비표준 확장인 Connection: keep-alive를 주고받아야 했습니다. HTTP/1.1은 지속 연결(persistent connection)이 기본이라 헤더 없이도 같은 TCP를 여러 요청에 재사용하며, 끊을 때만 Connection: close를 보냅니다. 효과는 3-way handshake와 TLS 핸드셰이크 횟수 감소이고, 한계는 한 연결 안에서 요청·응답이 여전히 순차적이라는 점입니다.

Connection 헤더는 close일 때 응답 후 연결 종료를 의미할 수 있으며, HTTP/1.1 프록시는 hop-by-hop 헤더를 어떻게 전달·제거하는지에 따라 예상과 다른 연결 종료가 생길 수 있습니다. 프록시·LB 앞에 두면 idle timeout(유휴 연결 끊김)이 클라이언트보다 짧은 경우가 많아, 재사용하려다 RST를 받거나 재시도가 발생합니다.

연결 풀과 동시성 제한

클라이언트(브라우저·http.Client)는 호스트별 최대 연결 수를 두고 풀에서 재사용합니다. 서버는 유휴 연결을 얼마나 유지할지 타임아웃으로 정합니다(Nginx의 keepalive_timeout 등). HTTP/2는 보통 한 호스트당 하나(또는 소수)의 TCP로 다수 스트림을 처리하므로, HTTP/1.1 때의 “6연결 병렬”과 자원 프로파일이 달라집니다.

GOAWAY, SETTINGS, 흐름 제어(HTTP/2·3)

HTTP/2에서는 SETTINGS 프레임으로 최대 동시 스트림·초기 윈도우 등을 조율하며, GOAWAY로 더 이상 새 스트림을 받지 않겠다는 의사를 전달합니다. Graceful shutdown 시 기존 스트림은 끝까지 처리하며, 클라이언트는 새 연결로 재시도하는 패턴이 됩니다.

HTTP/3는 QUIC의 연결·스트림 종료와 H3의 GOAWAY가 함께 동작합니다. 배포에서 버전 협상 실패 시 h2로 폴백하는지, Alt-Svc 캐시가 얼마나 길게 유지되는지가 실제 사용자 경로를 바꿉니다.

실무 체크포인트

  • LB idle timeout < 애플리케이션 Keep-Alive 기대면 끊긴 연결 재사용으로 502/connection reset이 날 수 있습니다. 양쪽 타임아웃을 맞추거나 클라이언트 재시도를 허용합니다.
  • HTTP/2는 “한 연결”이지만 서버·스토리지는 스트림 수·메모리에 따라 한계에 도달할 수 있습니다. SETTINGS와 서버 리소스 한도를 함께 봅니다.

HTTP 캐싱 메커니즘

HTTP 캐시는 저장 위치(브라우저·공유·CDN)·허가 여부·검증 방식의 세 축으로 이해하는 것이 안전합니다.

캐시 저장 위치와 Cache-Control

private은 공유 캐시(프록시·CDN)에 저장되지 않아야 함을, public은 공유 캐시 저장을 허용할 수 있음을 의미합니다. no-store는 저장 자체를 피하라는 강한 신호이며, no-cache는 저장은 해도 되지만 사용할 때마다 원본에 재검증하라는 뜻입니다. 이름과 달리 “캐시 금지”가 아니라는 점이 자주 헷갈립니다.

max-age / s-maxage는 각각 일반 캐시와 공유 캐시의 수명을 제한합니다. CDN은 원본의 Cache-Control을 그대로 따를지, 엣지 TTL 설정으로 덮어쓸지를 제품마다 다르게 처리하므로 사용하는 CDN의 문서를 확인해야 합니다.

검증: ETag, Last-Modified, 조건부 요청

강한 검증(strong ETag)은 바이트 단위 동일성에 가깝으며, 약한 검증(weak ETag)은 의미상 동일할 때 사용합니다. If-None-Match로 304 Not Modified를 받으면 본문 전송 없이 캐시된 표현을 갱신할 수 있습니다.

Last-Modified + If-Modified-Since는 초 단위·파일 시스템 타임스탬프에 의존해 동시성·서브초 변경에 취약할 수 있습니다. API는 ETag가 더 예측 가능한 경우가 많습니다.

무효화와 퍼지: PURGE, surrogate key, 버전 URL

URL이 동일한데 내용만 바뀌는 자산은 캐시 무효화가 어렵습니다. 따라서 정적 자산은 파일명에 해시를 넣고 immutable로 길게 캐시하는 패턴이 널리 사용됩니다. CDN은 API 퍼지, 태그·키 기반 무효화, 버전 프리픽스로 원본 부하를 줄입니다.

Vary와 캐시 키

Vary는 응답이 어떤 요청 헤더에 의존하는지를 알립니다. 예: Accept-Encoding이 다르면 gzip vs br 본문이 달라질 수 있으며, Accept-Language면 표현이 달라집니다. 누락하면 잘못된 언어·압축이 사용자에게 갈 수 있으며, 과도한 Vary는 캐시 적중을 망칩니다.

휴리스틱 캐시와 헤더 없는 응답

Cache-Control이 없고 Last-Modified 등만 있을 때, 일부 구현은 휴리스틱 캐시를 적용합니다. API가 헤더 없이 200만 반복하면 예상치 못한 캐시가 생기지 않도록 Cache-Control을 명시하는 것이 안전합니다.


콘텐츠 협상

콘텐츠 협상은 같은 URI에 대해 클라이언트가 선호하는 표현(언어·인코딩·형식)을 선택하는 과정입니다.

선제적(proactive) 협상: Accept, Accept-Language, Accept-Encoding

  • Accept: MIME 우선순위(;q=)로 JSON vs HTML 등을 선택합니다.
  • Accept-Language: 지역화된 리소스에 사용합니다. CDN이 엣지에서 언어별 캐시를 나눌 때 Vary: Accept-Language와 함께 설계합니다.
  • Accept-Encoding: gzip, br, zstd 등. 서버가 클라이언트가 원하는 압축을 지원하지 않으면 보통 압축하지 않은 본문(identity)을 보냅니다. 406까지 가는 경우는 클라이언트가 identity;q=0으로 무압축을 명시적으로 거부했을 때 정도입니다.

사후(reactive) 협상: 406 Not Acceptable, 300 Multiple Choices

드물게 406은 요청한 표현을 제공할 수 없음을, 300은 여러 대표 URI를 줄 수 있습니다. API는 보통 명시적 버전(/v1, Accept: application/vnd...)으로 협상을 단순화합니다.

Content-Type, charset, Accept-Charset(레거시)

오늘날 문자 집합은 Content-Type의 charset으로 명시하는 것이 일반적입니다. Accept-Charset은 UTF-8이 사실상 표준이 되면서 주요 브라우저가 더 이상 보내지 않습니다.

협상 실패를 줄이는 설계

  • API 버전은 URL 경로 또는 미디어 타입으로 고정하며, Vary를 최소화합니다.
  • 다국어는 Accept-Language + Vary 또는 경로 분리(/en/, /ko/) 중 하나로 일관되게 선택합니다.
  • 압축은 엣지·원본 모두에서 동일한 정책을 쓰는지 확인합니다(이중 압축·누락 방지).

프로덕션 HTTP 패턴

역방향 프록시·게이트웨이

Nginx, Envoy, Kong 등은 TLS 종단, HTTP/2·gRPC 브리지, 경로 기반 라우팅, 레이트 리밋을 담당합니다. 중요한 점은 클라이언트↔엣지와 엣지↔원본이 서로 다른 HTTP 버전일 수 있다는 것입니다. 원본은 HTTP/1.1만 보더라도 엣지는 h2/h3를 제공할 수 있습니다.

CDN과 캐시 계층

정적 자산은 긴 TTL + 해시 파일명, HTML은 짧은 TTL 또는 캐시 없음. API는 Authorization이 있으면 캐시하지 않음 같은 규칙을 CDN 규칙 엔진에 명시합니다. Stale-while-revalidate류의 확장은 제품 UX와 원본 보호의 균형에 사용됩니다(지원 여부는 CDN·클라이언트에 따름).

재시도·멱등·타임아웃

네트워크 오류에 대해 POST 재시도는 중복 생성 위험이 있습니다. Idempotency-Key, 서버 측 중복 키 저장소, 멱등한 메서드(PUT/DELETE) 설계로 완화합니다. 타임아웃은 클라이언트·LB·서버·DB 중 가장 짧은 곳이 먼저 발화합니다—분산 추적으로 어느 hop인지 구분합니다.

관측: Via, X-Request-ID, 프로토콜 버전

HTTP 버전(h2/h3), TLS 핸드셰이크 시간, TTFB, 재시도 횟수를 메트릭·로그에 남깁니다. 프록시 체인에서는 Via, 상관 ID(X-Request-ID 등)로 장애 구간을 좁힙니다.

보안과 연관된 헤더 운영

HSTS, CSP는 앞선 절과 같으며, 캐시와 결합하면 Cache-Control에 개인 데이터가 섞이지 않게 엣지 규칙을 이중 확인합니다. CORS는 브라우저만 보호하므로 BFF·서비스 메시에서 인증·권한을 반복 검증합니다.


실전 사용

curl로 검증하기

# 응답 헤더만 보기
curl -sI https://example.com
# JSON POST
curl -sS -X POST 'https://api.example.com/items' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"name":"demo"}'
# 리다이렉트 추적(-L), 상세 로그(-v)
curl -L -v 'https://example.com/path'
# HTTP/2 강제(지원 시)
curl --http2 -sI https://example.com
# HTTP/3(빌드에 따라)
curl --http3-only -sI https://cloudflare-quic.com/
  • -s: 진행률 표시와 에러 메시지를 숨깁니다. -S를 함께 주면(-sS) 진행률은 숨긴 채 에러 메시지만 다시 보여 주므로 스크립트에서 자주 씁니다.
  • -I: HEAD 요청에 해당하는 동작으로, 응답 헤더만 가져옵니다(본문 없음).
  • -X POST: 메서드를 POST로 지정합니다.
  • -H: 요청 헤더 한 줄을 추가합니다. Content-Type, Authorization 등에 사용합니다.
  • -d: 요청 본문을 지정합니다. JSON 문자열을 그대로 붙입니다.
  • -L: 3xx 리다이렉트를 따라갑니다(최종 URL까지).
  • -v: 상세 디버그(DNS, TLS, 헤더)를 출력합니다.
  • —http2: 클라이언트가 지원하면 HTTP/2로 협상해 봅니다.
  • —http3-only: HTTP/3만 시도합니다(빌드·서버 지원 필요). 아키텍처 관점: 클라이언트 → CDN/엣지 캐시 → 원본(또는 API)으로 요청이 흐를 때, 각 홉의 캐시 키·Vary·TLS 종단 위치가 응답 내용을 바꿉니다. 장애 시 어느 레이어의 502/504인지를 먼저 나눕니다.

REST API 설계 팁

  • 리소스 중심 URL: /users/42/orders처럼 명사·계층으로 표현합니다.
  • 상태 코드로 의미 고정: 생성은 201+Location, 잘못된 입력은 400/422, 인증은 401, 권한은 403.
  • 버전 관리: /v1/ 접두사 또는 Accept 헤더로 호환성을 명시합니다.
  • 페이지네이션: cursor 또는 offset/limit, 링크 헤더(Link)로 다음 페이지를 표준화합니다.
  • 멱등 키: 결제·주문처럼 중복 제출이 치명적이면 Idempotency-Key 패턴을 고려합니다.

캐싱 전략

  • 정적 자산: 파일명에 해시를 넣고 Cache-Control: public, max-age=31536000, immutable.
  • HTML 진입점: max-age는 짧게, 버전된 JS/CSS에 긴 캐시.
  • API: 개인 데이터는 private 또는 no-store; 공개 읽기 전용은 ETag+max-age로 조건부 요청 유도.
  • Vary: Accept-Encoding·Accept-Language에 따라 다른 표현이면 Vary를 정확히 명시합니다.

보안 고려사항

HTTPS와 TLS 1.3

평문 HTTP는 인증 정보·쿠키·페이로드가 노출됩니다. TLS 1.3은 핸드셰이크 단축·암호 스위트 현대화로 기본선이 되었으며, 인증서 체인·만료·SAN 관리가 운영 핵심입니다.

HSTS

HTTP Strict Transport Security(Strict-Transport-Security)는 브라우저가 해당 도메인을 HTTPS로만 로드하도록 강제합니다. 프리로드 리스트 등록은 정책에 따라 신중히 결정합니다.

CSP(콘텐츠 보안 정책)

Content-Security-Policy는 XSS 완화에 유효합니다. default-src, script-src에 nonce/hash 기반으로 좁히고, frame-ancestors로 클릭재킹을 제한합니다.

API 보안

  • OAuth 2.0 / OIDC로 위임 인증, 짧은 수명 액세스 토큰+리프레시 패턴.
  • CORS는 브라우저를 위한 정책—서버 간 호출을 막지는 않으므로 인증·네트워크 경계와 함께 설계합니다.

흔한 문제와 해결

증상점검
CORS 에러서버 Access-Control-Allow-Origin, 프리플라이트에서 메서드·헤더 허용, 자격 증명 시 credentials 일치.
타임아웃클라이언트·LB·서버 idle/read 타임아웃, 다운스트림 지연, DNS.
리다이렉트 루프301/302 체인, http↔https, www 정규화, 캐시된 301 확인.
캐시가 안 바뀜Cache-Control, CDN 퍼지, URL 버전 bump.

흔한 실수와 해결

실수결과해결
민감 API에 Cache-Control: public공유 캐시에 개인 데이터가 캐시될 위험private·no-store 등으로 재정의
ETag만 믿고 Vary를 무시압축·언어·쿠키에 따라 엉뚱한 표현이 캐시됨Vary를 정확히 명시
리다이렉트 체인만 길게 늘림지연·실패 지점 증가캐노니컬 URL로 단순화
HTTP/3만 켜고 UDP 차단을 미확인일부 사용자는 느리거나 폴백방화벽·관측 로그로 검증

참고

  • IETF HTTPbis 문서군(RFC 9110~9114 계열)
  • Mozilla MDN: HTTP, CORS, CSP
  • OWASP: API Security, Transport Layer Protection

같이 보면 좋은 글