2017년 8월 4일 금요일

[HTTP 프로토콜 강좌]#16 HTTP 응답 헤더 I - Accept-Range, ETag, Location, Server, Vary

바로 이전 강좌(#15)까지 HTTP 요청헤더를 모두 마치고, 오늘부터는 응답헤더에 대해 정리해본다.
응답헤더가 끝나게 되면 계획으로는 일반(General) 헤더를 정리하고 HTTP 헤더에 대해서 마무리를 할 것이다.

요청헤더는 클라이언트가 캐시(프록시)서버 또는 웹서버에게 전달하려는 내용들에 반해 응답헤더는 웹서버가 캐시(프록시)서버 또는 클라이언트에게 주는 정보들이다.

요청헤더와 마찬가지로 캐시관련 헤더들이 있고, 일반적인 정보를 포함하는 헤더들이 있다.

응답헤더도 다양하게 종류가 많지만, 그래도 자주 볼수 있는 것들에 대해서 가급적 자세히 정리해 볼 것이다.

1. Accept-Range

이전 강좌 중 HTTP 요청헤더의 Range를 다룬적이 있다.
(Range 헤더의 자세한 내용은 이곳 을 참고하는게 좋겠다.)

웹서버로부터 대용량의 파일을 다운로드 하는 도중 중간에 끊기는 경우에 우리는 파일을 다시 처음부터 받아야 하는가? 아니면 중간부터 다시 받는게 좋은가?
당연히 이전에 받았던 내용을 참고하여 끊긴 부분부터 다시 연속해서 받을 수 있다면 베스트 일 것이다.
바로 이러한 경우를 가능케 하는 것이 Range 헤더이다.

클라이언트가 Range 헤더를 이용해서 요청하는 경우 서버 역시 Range 요청을 처리할 준비가 되어 있어야 한다. 서버가 클라이언트에게 Range 요청에 대한 처리 지원 여부를 알려주는데 바로 Accept-Range 헤더가 사용된다.

HTTP 1.1에서는 Accept-Range 에 다음 두가지 형태의 값을 명시하고 있다.

  1. Accept-Range: bytes
  2. Accept-Ragne: none

예상되겠지만 첫번째는 Range 요청을 처리할 수 있다는 것이고, 두번째는 Range 요청을 지원하지 않는다는 것이다.

하지만, 클라이언트의 Range 요청에 대해 서버는 Accept-Range 헤더를 반드시 포함하는 것은 아니다. 서버가 Accept-Range 헤더를 포함하지 않을 수도 있다는 뜻이다.

클라이언트 역시 서버의 Accept-Range 헤더 정보가 없다고 해서 Range 요청을 할수 없는것은 아니다. 서버의 Accept-Range 헤더 정보가 있든 없든 필요하다면 Range 요청을 할 수 있다는 의미이다.

단, 클라이언트의 Range 요청에 대해 서버가 이를 지원하지 않는다면, 서버는 Range 요청에도 불구하고 전체 컨텐츠를 전달하게 된다.

다음은 Range 헤더를 포함하여 요청한 예이다. 서버에서 Range 요청을 지원하기 때문에 206 Partial Contents 메시지와 함께 데이터를 전달한다.



60~65 바이트를 전달해 줄것을 요청했고, 웹서버는 이에 대한 응답을 정상적으로 주었다.
응답헤더의 Accept-Range: bytes 와 Content-Length: 6 을 통해 확인할 수 있다.


다음은 서버에서 Range 요청을 지원하지 않는 경우이다.
클라이언트에서 Range 요청을 했음에도 불구하고 서버는 컨텐츠의 전체 용량을 전달하는 것을 확인 할 수 있다.




응답헤더의 Accept-Ranges: none 헤더와 Content-Length: 298997 을 통해 전체 사이즈가 모두 전달되었음을 확인했다.




2. ETag

ETag 헤더는 캐시 관련 헤더이다.  서버에서 제공되는 컨텐츠의 식별번호로 이해하면 좋다. 사람들도 주민등록 번호를 가지고 있듯이 웹서버에서 제공하는 컨텐츠도 각각의 고유 식별정보를 가지고 있다.

캐시버서와 웹브라우저는 바로 이정보를 이용해서 캐싱된 컨텐츠들이 유효한지 갱신이 필요한지를 결정한다.

ETag의 종류는 Strong / Weak 두가지가 있다.
Strong ETag 는 바이트단위로 컨텐츠가 동일함을 의미한다. 따라서 Strong ETag 를 가지고 있는 캐싱된 컨텐츠는 Range 요청에 대해서 응답이 가능하다.

반면, Weak ETag는 바이트단위로 동일하지는 않다. 일반적으로 캐시된 컨텐츠로는 사용할 수 있으나 바이트 단위까지 같지는 않기 때문에 Range 요청에 대한 응답이 불가능하다.

Strong ETag와 Weak ETag 의 구분은 다음과 같다.

  • Strong ETag : "abcdefg"
  • Weak ETag : w/"abcdefg"


다음은 실제 헤더의 ETag 값이다.



ETag 값은 보통 "리소스의 해시값 - 마지막 수정 시간의 해시값" 으로 구성된다.
위 예도 대시(-)를 기준으로 좌/우 값이 있는데.. 이와 같다고 보면 된다.

캐시서버는 일반적으로 자신의 캐싱된 컨텐츠를 비교할때 ETag 값을, If-Match, If-None-Match와 함께 사용한다.

If-Match와 If-None-Match 에 대한 상세한 정보는 여기를 참고하자.

3. Location

웹서버는 클라이언트의 요청을 다른 페이지 또는 웹사이트로 리다이렉트 할 수 있다.
바로 이때 사용되는 헤더가 Location 헤더이다.





3xx 상태코드와 함께 보통 Location 헤더가 사용되는데 201 Created 메시지에 사용되기도 한다. 201 Created 는 우리 HTTP 요청방식 중에서 PUT 요청방식을 다룰때 사용했던 예제에 잠깐 언급되었던 적이 있다.
(PUT 요청방식에 대한 내용은 여기 에서 확인 할 수 있다.)

다음은 Location 헤더가 사용된 예를 동작흐름으로 표시한것이다.
클라이언트가 요청한 컨텐츠가 자신에게는 없지만 다른 웹서버에 있다는 것을 알고 있는 경우 이를 리다이렉트하여 정상적으로 처리한 상황이다.





  1. 서버 A의 URI로 클라이언트가 GET 요청을 함
  2. 서버 A는 해당 URI의 요청을 서버 B의 /main.php 로 리다이렉트 함
    302 Found 메시지와 함께 Location 헤더를 전달, Location 헤더에는 서버 B의 도메인 주소와 URI 정보가 포함됨
  3. 클라이언트는 Location 헤더에 포함된 내용을 참고하여 서버 B로 요청
  4. 서버 B는 200 OK로 정상 응답


다음은 실제 HTTP 요청과 응답 메시지이다.
테스트 서버에 302 Found 응답과 Location 의 값으로 다음(www.daum.net)을 설정한 후 테스트 한 결과이다.





4. Server

Server 헤더는 그 값으로 웹서버의 정보를 담는다.
웹서버에서 운영중인 HTTP 소프트웨어의 버젼 정보를 명시한다.

다음은 실제 사용되는 Server 헤더의 정보이다.

먼저 Apache 2.2 를 사용한 서버의 예..



두번째는 MS 의 IIS 6.0을 사용한 예...
이 처럼 실제 서버에서 운영중인 HTTP 데몬의 정보를 Server 응답헤더에 담아 클라이언트에게 전달한다.

보안적인 측면에서 보면 썩 좋은 정보는 아니다. 웹서버의 운영중인 데몬의 버젼 정보를 아는경우 이미 알려진 취약점을 이용하여 공격을 시도할 수 있기 때문이다.

5. Vary

하나의 웹컨텐츠를 데스크톱과 모바일에서 서로 다르게 보여줘야 할 경우가 있다.
웹서버에서는 프로그램으로 잘 표현되어 있으나, URI 정보로는 동일한 컨텐츠이다 보니, 캐시서버가 이 컨텐츠를 캐싱하고 있는 경우 문제가 될 수 있다.

모바일 버젼으로 접속했는데, 캐시서버가 데스크톱 컨텐츠를 직접 전달한다던가.. 아니면 그 반대의 경우가 그렇다.

이 경우 웹서버에서 Vary 헤더를 사용하게 되면 캐시서버에게 캐싱정책을 가이드 할 수 있다.

Vary 헤더의 값으로는 HTTP 요청헤더명이 사용된다.
아래는 User-Agent 를 값으로 사용한 경우이다.

Vary: User-Agent 

위 경우는 캐싱하고 있는 컨텐츠의 최초 User-Agent 정보가 일치하지 않으면 캐싱된 컨텐츠를 전달하지 않는다.

즉, 최초 모바일로 트와이스 이미지를 요청하여 전달받게 되면.. 캐싱된 트와이스 이미지는 모바일 User-Agent 값인 경우에만 직접 전달한다는 뜻이다.

다음 동작 그림을 참고하자.


1~4 번까지는 모바일 사용자가 twice.jpg 이미지를 요청한 경우이다.
최초 요청이기 때문에 중간 프록시(캐시) 서버는 웹서버로 클라이언트의 요청을 그대로 전달하고 이미지를 전달 받는다. 전달받은 이미지는 저장공간에 저장한 후 모바일 사용자에게 전달해 준다.

그런데 이후 동일한 이미지를 모바일 사용자가 아닌 데스크톱 사용자가 요청한 경우이다.
5~8 번까지가 그 상황인데, 분명 동일한 URI 경로임에도 불구하고 캐싱된 이미지를 전달하지 않는다.

여기서 중요한건 3번 과정에서 최초 컨텐츠를 웹서버가 프록시에게 전달할 때 Vary 헤더를 포함했다는 것이다.
그렇기에 프록시 서버는 동일한 URI 요청임에도 불구하고 캐싱할 당시의 User-Agent 정보를 비교하고 내용이 다르기에 캐싱된 컨텐츠를 직접 전달하지 않은 것이다.

오늘은 이렇게 5개의 응답헤더를 정리하고 마무리 하려 한다.

이상 끝!



댓글 2개:

  1. 블로그에 헤더필드에 관한 설명이 정말 잘 돼있어서 도움이 많이 됐습니다!!!

    답글삭제