HTTP/2 Server Push, NGINX로 구현
NGINX 1.13.9부터 HTTP/2 Server Push 를 지원하는 기능이 포함되어 있습니다.
HTTP/2 사양에서 정의된 Server Push는 서버가 원격 클라이언트에게 리소스를 미리 예측하여 Push할 수 있는 기능입니다. 이를 통해 클라이언트가 이러한 리소스를 곧 요청할 것으로 예상되는 상황에서 RTT(Round Trip Time – Request와 Response에 필요한 시간) 수를 하나 이상 줄여 사용자에게 더 빠른 응답을 제공할 수 있습니다.
Server Push 는 웹 페이지를 렌더링하는 데 필요한 스타일 시트, 이미지 및 기타 리소스를 클라이언트에 미리 제공하는 데 사용할 수 있습니다. 필요한 리소스만 푸시하고, 클라이언트가 이미 캐시하고 있을 가능성이 있는 리소스는 Push 하지 않도록 주의해야 합니다.
목차
1. HTTP/2 Server Push 구성
2. HTTP/2 Server Push 확인
2-1. 개발자 도구로 확인(Google Chrome)
2-2. Command Line 클라이언트로 확인(nghttp
)
3. Automatically Push
4. Selectively Push
5. HTTP/2 Server Push 의 효과 측정
5-1. 몇 가지 기본적인 관찰
5-2. 결과 해석: Preload
5-3. 결과 해석: Server Push
5-4. 테스트 노트
6. 결론
7. 부록: HTTP/2 Server Push 작동 방법
1. HTTP/2 Server Push 구성
페이지 로드와 함께 리소스를 푸시하려면 다음과 같이 http2_push
지시문을 사용합니다.
server {
# Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# whenever a client requests demo.html, also push
# /style.css, /image1.jpg and /image2.jpg
location = /demo.html {
http2_push /style.css;
http2_push /image1.jpg;
http2_push /image2.jpg;
}
}
2. HTTP/2 Server Push 확인
HTTP/2 Server Push 가 적용되었는지 확인하는 방법은 두 가지입니다.
- 웹 브라우저의 개발자 도구를 사용하는 방법
- nghttp와 같은 명령 줄 HTTP/2 클라이언트를 사용하는 방법
2-1. 개발자 도구로 확인(Google Chrome)
다음은 Google Chrome 웹 브라우저의 개발자 도구를 사용하여 HTTP/2 Server Push 가 적용되었는지 확인하는 방법입니다. 그림에서는 Chrome의 개발자 도구의 Network 탭에서 Initiator 열이 /demo.html에 대한 요청의 일부로 클라이언트로 푸시된 여러 리소스를 나타내고 있습니다.

2-2. Command Line 클라이언트로 확인(nghttp
)
웹 브라우저 도구 외에도, nghttp2.org 프로젝트의 nghttp Command line 클라이언트를 사용하여 Server Push 가 적용되었는지 확인할 수 있습니다. GitHub에서 nghttp Command line 클라이언트를 다운로드하거나, 해당 운영 체제 패키지를 사용할 수 있는 경우 해당 패키지를 설치할 수 있습니다. Ubuntu의 경우, nghttp2-client 패키지를 사용하세요.
출력 결과에서 별표(*)는 서버에 의해 푸시된 리소스를 나타냅니다.
$ nghttp -ans https://example.com/demo.html
id responseEnd requestStart process code size request path
13 +84.25ms +136us 84.11ms 200 492 /demo.html
2 +84.33ms * +84.09ms 246us 200 266 /style.css
4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg
6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg
3. Automatically Push
많은 상황에서는 NGINX 구성 파일에 푸시하려는 리소스를 나열하는 것이 불편하거나 불가능할 수 있습니다. 이러한 이유로 NGINX 는 Link preload header 를 가로채고, 이 헤더에서 식별된 리소스를 푸시하는 규칙도 지원합니다. preload를 활성화하려면 구성에 http2_push_preload 지시문을 포함하면 됩니다.
server {
# Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# Intercept Link header and initiate requested Pushes
location = /myapp {
proxy_pass http://upstream;
http2_push_preload on;
}
}
예를 들어, NGINX 가 프록시로 작동하는 경우 (HTTP, FastCGI 또는 기타 트래픽 유형에 대해), 상위 서버는 다음과 같은 Link 헤더를 응답에 추가할 수 있습니다.
Link: </style.css>; as=style; rel=preload
NGINX는 이 헤더를 가로채고 /style.css의 Server Push 를 시작합니다. Link 헤더의 경로는 반드시 절대 경로여야 하며, ./style.css와 같은 상대 경로는 지원되지 않습니다. 경로에는 선택적으로 쿼리 문자열을 포함할 수 있습니다.
여러 개의 객체를 푸시하려면 여러 개의 Link
Header를 제공하거나, 더 좋은 방법으로는 모든 객체를 쉼표로 구분된 목록에 포함시킬 수 있습니다.
Link: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload
NGINX가 사전 로드된 리소스를 푸시하지 않도록 하려면 헤더에 nopush
매개변수를 추가하십시오.
# Resource is not pushed
Link: </nginx.png>; as=image; rel=preload; nopush
http2_push_preload가 활성화되어 있을 때, NGINX 구성에서 Response Header를 설정하여 Preload 서버 푸시를 시작할 수도 있습니다.
add_header Link "</style.css>; as=style; rel=preload";
4. Selectively Push
HTTP/2 사양은 리소스를 푸시할지 여부를 결정하는 도전에 대해 다루지 않습니다. 분명히, 클라이언트가 리소스를 필요로 할 가능성이 높고 이미 캐시되어 있지 않을 경우에만 리소스를 푸시하는 것이 가장 좋습니다.
한 가지 가능한 접근 방법은 클라이언트가 사이트를 처음 방문할 때만 리소스를 푸시하는 것입니다.
예를 들어 세션 쿠키의 존재 여부를 테스트하고, 조건부로 Link 헤더를 설정하여 세션 쿠키가 없는 경우에만 리소스를 사전로드할 수 있습니다.
클라이언트가 잘 동작하고 이후 요청에 쿠키를 포함하는 경우, 다음과 같은 설정으로 NGINX는 브라우저 세션 당 한 번만 리소스를 클라이언트에 푸시합니다.
server {
listen 443 ssl http2 default_server;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
http2_push_preload on;
location = /demo.html {
add_header Set-Cookie "session=1";
add_header Link $resources;
}
}
map $http_cookie $resources {
"~*session=1" "";
default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
}
5. HTTP/2 Server Push의 효과 측정
서버 푸시의 효과를 측정하기 위해, 별도의 스타일시트인 /style.css를 참조하는 간단한 테스트 페이지인 /demo.html을 만들었습니다. 스타일시트는 또한 두 개의 이미지를 참조합니다. 우리는 세 가지 다른 구성을 사용하여 페이지 로드 시간을 테스트했습니다.
- 순차적인 GET (최적화 없음) – 브라우저는 필요한 리소스를 발견하면 로드했습니다.
- Preload Hints – Preload 힌트 (링크 헤더)가 첫 번째 응답에 포함되어 브라우저에 종속성을 로드하도록 알려줍니다.
- HTTP/2 Server Push – 종속성이 미리 브라우저로 푸시되었습니다.

HTTP, HTTPS 또는 HTTP/2를 사용하여 각 구성에 대해 여러 번의 테스트 실행을 수행했습니다. 첫 번째 두 구성은 모든 세 가지 프로토콜에 적용되며, 서버 푸시는 HTTP/2에만 적용됩니다.
Chrome 개발자 도구를 사용하여 동작을 측정했습니다. 각 구성의 가장 일반적인 동작을 평가하고 평균을 내어 시간을 링크의 RTT와 상관관계를 맺었습니다 (ping를 사용하여 측정). 이를 통해 각 방법의 기계적인 효과를 설명했습니다.

5-1. 몇 가지 기본적인 관찰
- DOM 로드는 새로운 연결을 초기화하고 demo.html 페이지를 검색하는 시간입니다. 스타일시트는 CSS 리소스를 검색하는 시간입니다.
- HTTP를 통해 연결을 설정하는 데는 1 RTT가 소요됩니다. HTTPS 및 HTTP/2의 경우 2 RTT가 소요됩니다.
- HTML 및 CSS 리소스의 페이로드는 최대 전송 단위(MTU) 크기보다 작기 때문에 GET 작업은 약 1 RTT에서 완료됩니다.
5-2. 결과 해석: Preload
- CSS 리소스는 HTML 리소스 내에서 직접 참조되기 때문에 Preload 힌트는 CSS 리소스에는 최소한의 영향을 미칩니다. 또한 HTML 리소스는 빠르게 전달되기 때문에 브라우저는 HTML 페이지가 전달되자마자 CSS 요청을 시작합니다.
- Preload 힌트는 CSS 리소스에서 선언된 리소스(여기서는 두 개의 이미지)의 다운로드를 빠르게 시작하는 효과가 있습니다. Preload 힌트를 사용하면 다운로드가 1 RTT 더 빨리 시작될 수 있습니다.
- Preload 힌트로 인한 순수한 절약은 1 RTT 이상일 수 있습니다. 리소스를 병렬로 다운로드할 때 브라우저는 하나 이상의 추가 연결을 열어야 합니다. 성능은 느린 요청(더 큰 응답)이 먼저 예약되는지, 아니면 새로운 연결이 열리는 동안 지연되는지에 따라 달라집니다. 이 예측할 수 없는 요청 순서가 HTTP 및 HTTP/2의 1-RTT 가속 및 HTTPS의 2-RTT 가속을 설명합니다.
5-3. 결과 해석: HTTP/2 Server Push
HTTP/2 Server Push는 Preload 힌트 시간을 추가로 1 RTT 개선했습니다. 푸시 “응답”은 첫 번째 요청에 대한 응답과 동시에 시작되었으며, 반면 Preload 힌트 응답은 1 RTT의 지연이 발생했습니다 – 첫 번째 요청에 대한 응답에 대한 0.5 RTT와 Preload GET 요청에 대한 0.5 RTT입니다.
5-4. 테스트 노트
- 각 구성에 대해 여러 번의 테스트 실행이 있었습니다. 각 실행은 브라우저 캐시가 비어 있고 NGINX 서버와의 기존 keepalive 연결이 없는 상태에서 시작되었습니다. NGINX 지시문인 keepalive_timeout 및 http2_idle_timeout을 사용하여 keepalive 연결을 빠르게 닫았습니다.
- 현재 Chrome에서는 글꼴 리소스를 푸시하는 것이 불가능한 것으로 보입니다. 이는 알려진 복잡성 때문일 수 있습니다. Chrome은 이미 푸시된 글꼴 리소스에 대해서도 명시적인 요청을 수행합니다.
- 각 테스트 전에 브라우저 캐시를 명시적으로 지우는 데 주의를 기울였으며, 모든 콘텐츠는 만료된 캐시 제어 헤더로 제공되었습니다.
- 각 테스트 전에 브라우저 캐시를 명시적으로 지우는 데 주의를 기울였으며, 모든 콘텐츠는 만료된 캐시 제어 헤더로 제공되었습니다. Chrome은 Preload된 리소스를 캐시에 저장합니다. 이러한 캐시된 리소스는 캐싱을 비활성화하더라도 일관되게 무시되지 않으며, 명시적인 “브라우저 캐시 지우기” 작업으로 일관되게 지워지지 않습니다. (Chrome에서 캐싱을 비활성화하는 한 가지 방법은 개발자 도구 네트워크 탭에서 캐시 사용 안 함 확인란을 선택하는 것입니다. 브라우저 캐시를 지우려면 개발자 도구를 열고 브라우저 새로 고침 버튼을 마우스 오른쪽 단추로 클릭한 다음 “캐시 비우기 및 강력한 새로 고침”을 선택할 수 있습니다).
- 일부 콘텐츠를 Preload하려는 시도는 Chrome에서 캐시된 사본을 다시 유효성 검사하지 못하고 리소스를 정상적으로 다운로드하도록 만들었습니다. 이러한 시도는 측정에 포함되지 않았습니다.
- Chrome은 이전에 수락된 자체 서명된 인증서를 사용하는 모든 새로운 SSL 연결에 불필요한 2-RTT 지연을 추가합니다. 이 테스트는 이러한 2-RTT 지연을 피하기 위해 CA-서명된 Let’s Encrypt 인증서를 사용하여 수행되었습니다.
- DNS 지연은 로컬 /etc/hosts 파일을 편집하여 해결되었습니다.
- 이 간단한 테스트는 클라이언트가 이미 리소스의 캐시된 사본을 가지고 있는 경우 서버 푸시의 영향을 측정하려는 시도는 하지 않았습니다. 테스트에서는 모든 캐시가 각 테스트 전에 지워지고, 대부분의 푸시는 취소하기에는 너무 빨랐습니다.
6. 결론
이 테스트는 Preload 힌트와 서버 푸시의 메커니즘을 강조하기 위해 고의로 간단하게 설계되었습니다. HTTP/2 Server Push 는 간단한 상황에서 Preload 힌트에 비해 1-RTT의 개선 효과를 제공하며, 최적화되지 않은 순차적인 GET 요청 및 종속 리소스의 검색과 비교했을 때 더 큰 개선 효과를 제공합니다.
더 현실적인 사용 사례에는 많은 변수가 있습니다. 여러 종속 리소스, 여러 소스, 이미 캐시된 리소스를 푸시하거나 즉시 필요하지 않은 리소스를 푸시하여 대역폭을 낭비할 수도 있습니다. 브라우저의 일관성도 성능에 영향을 미칩니다. 이 간단한 테스트와는 달리 실제 결과는 달라질 수 있습니다.
예를 들어, Chrome팀은 HTTP/2 Server Push 를 언제 배포해야 하는지에 대한 자세한 권장 사항을 게시하고, 최적화되지 않은 상태, Preload 힌트 및 HTTP/2를 통한 서버 푸시의 효과를 비교하기 위해 더 복잡한 사이트에서 측정을 수행했습니다. HTTP/2 Server Push 에 대한 규칙을 읽는 것은 실제 운영 환경에서 HTTP/2 Server Push 를 배포하려는 모든 사람에게 유익할 것입니다.
실용적인 결론은 미리 필요한 리소스를 식별할 수 있다면 상위 서버에서 Preload 힌트를 보내는 것에 실질적인 이점이 있다는 것입니다.
이러한 리소스를 푸시하는 추가적인 이점은 작지만 측정 가능하지만, 필요한 리소스에 대한 대역폭 낭비와 지연을 초래할 수도 있습니다. 서버 푸시 구성을 신중하게 테스트하고 모니터링 해야 합니다.
아래 뉴스레터를 구독하고 NGINX와 NGINX STORE의 최신 정보들을 빠르게 전달 받아보세요.
7. 부록: HTTP/2 Server Push 작동 방법
HTTP/2 서버 푸시는 일반적으로 클라이언트가 리소스를 요청할 때 종속 리소스를 미리 보내는 데 사용됩니다.
예를 들어, 클라이언트가 웹 페이지를 요청하면 서버는 종속 스타일시트, 폰트 및 이미지를 클라이언트에게 푸시할 수 있습니다.
클라이언트가 HTTP/2 연결을 생성할 때, 서버는 연결을 통해 하나 이상의 서버 푸시 응답을 시작할 수 있습니다. 이러한 푸시는 클라이언트가 명시적으로 요청하지 않은 리소스를 전송합니다.
클라이언트는 푸시를 거부하기 위해 RST_STREAM 프레임을 보내거나 수락할 수 있습니다. 클라이언트는 HTTP/2 연결과 관련된 로컬 “푸시 캐시”에 푸시된 콘텐츠를 저장합니다.
나중에 클라이언트가 HTTP/2 연결을 통해 리소스에 대한 요청을 할 때, 클라이언트는 요청에 대한 완료된 또는 전송 중인 응답을 확인하기 위해 연결의 푸시 캐시를 확인합니다.
클라이언트는 리소스에 대한 새로운 HTTP/2 요청을 만드는 대신 캐시된 리소스를 우선적으로 사용합니다.
푸시된 리소스는 (a) 사용되거나 (b) HTTP/2 연결이 닫힐 때까지 해당 연결의 푸시 캐시에 유지됩니다.
- 리소스가 사용되면 클라이언트는 복사본을 가져오고 푸시 캐시의 항목이 삭제됩니다. 리소스가 캐시 가능한 경우, 클라이언트는 자체 HTTP 페이지 캐시에 복사본을 캐시할 수 있습니다.
- HTTP/2 연결이 어떤 이유로든 닫히면 해당 로컬 푸시 캐시가 삭제됩니다.
이에는 몇 가지 영향이 있습니다.
- 브라우저의 HTTP 페이지 캐시에 있는 콘텐츠가 푸시 캐시에 있는 콘텐츠보다 우선적으로 사용됩니다. 푸시된 콘텐츠가 더 최신이더라도 마찬가지입니다.
- HTTP/2 연결은 다른 페이지 로드 사이에서 공유될 수 있습니다. 한 페이지 로드의 결과로 푸시된 리소스는 다른 페이지 로드에서 요청될 때 사용될 수 있습니다.
- 자격 증명을 사용하는 요청은 자격 증명이 없는 요청과 다른 HTTP/2 연결을 사용합니다. 예를 들어, 크로스 오리진 요청(자격 증명)으로 푸시된 리소스는 브라우저가 자격 증명이 없는 요청으로 리소스를 요청할 경우 찾을 수 없을 수 있습니다.
HTTP/2 Server Push는 흥미로운 기능입니다. HTTP/2 Server Push 구성을 철저히 테스트하고, 더 예측 가능하고 캐시를 고려한 동작을 제공하는 경우에는 Preload 힌트로 대체할 수 있도록 준비하세요.
HTTP/2 Server Push를 사용해보기 위해 NGINX STORE에 문의해보세요.
댓글을 달려면 로그인해야 합니다.