프레임워크/Spring

HTTP Header는 case-sensitive인가

pythaac 2021. 12. 3. 23:27

1. (동기) HTTP header를 가져오지 못하는 상황

  Spring으로 REST API Controller를 구현하던 중, HTTP 헤더를 읽어오지 못하는 상황이 발생하였습니다. HTTP request는 Postman으로 전송하였고, 읽고자하는 HTTP header는 "Content-Type"이었습니다.

 

Postman의 header 확인

Postman에서 작성하는 HTTP header에 "Content-Type"이 포함되어있음

 

Controller 확인

Map<String, String> 형식의 header에서 "Content-Type"을 key로 갖는 value를 response에 작성

 

Response 확인

Postman이 받은 response에는 "null"이 작성됨

 

2. (문제접근) HTTP header가 정상적으로 서버에 실려왔는가?

  먼저 HTTP header가 정상적으로 서버에 도착했는지 확인이 필요했습니다. 이를 위해 서버 controller에서 수신한 HTTP header의 모든 내용을 출력해보았습니다.

 

Controller에서 출력

Map<String, String> 타입으로 받은 header의 각 entry를 출력

 

Controller에서 출력 확인

"Content-Type"이 실려온 key가 "contetn-type"으로 출력되는 것을 확인

 

3. (문제해결) controller에서 Map의 key를 lowercase로 접근

  서버에서 HTTP header의 key를 lowercase로 처리하여 해결할 수 있습니다.

Controller에서 HTTP header의 key를 lowercase로 접근하기

key를 "Content-Type"에서 "content-type"으로 수정

 

Postman에서 response 확인

Content-Type이 정상적으로 response에 작성된 것을 확인

 

 

4. (원인분석-1) Postman이 header를 lowercase로 바꾸어 전송하는가?

  지금까지 확인한 사실은 Postman이 보낸 HTTP request를 Controller가 수신하였으나, Postman에서는 "Content-Type"이라 적혀있던 header가 "content-type"으로 바뀌어 수신되었다는 사실입니다. 그렇다면 언제 lowercase로 변경되었는지 확인해봐야합니다. 검색해보니 아래와 같은 issue를 발견했습니다.

https://github.com/postmanlabs/postman-app-support/issues/6394

 

Header names are converted to lower case · Issue #6394 · postmanlabs/postman-app-support

Describe the bug When sending requests with (custom) HTTP headers, all (custom and non-custom) header names are converted to lower case!!! Applications that rely on case-sensitive values (and/or he...

github.com

  해당 내용에 따르면, wireshark로 찍어본 결과 Postman이 전송하는 패킷에 포함된 해당 header는 lowercase로 변경하지 않았다는 내용입니다. 아래 사진은 직접 wireshark로 capture한 패킷으로, 해당 내용이 사실임을 확인하였습니다.

Wireshark를 통해 Postman의 패킷에서 lowercase로 변경되지 않은 "Content-Type" 확인

 

4. (원인분석-2) Spring boot에서 HTTP header를 lowercase로 바꾸는가?

  클라에서 바꾸지 않으면 서버에서 변경될 가능성이 높은데, Tomcat이 관련되어있다는 내용을 아래 스택오버플로우에서 찾았습니다. Tomcat이 헤더 조회를 단순화하기 위해 lowercase로 헤더를 저장한다고 합니다. 즉, lowercase의 원인은 Tomcat을 servlet container로 사용하기 때문이었습니다.

https://stackoverflow.com/questions/55158188/how-to-prevent-of-converting-header-name-into-lower-case-spring-boot

 

How to prevent of converting header name into lower case - Spring boot?

I am developing a java based web application in spring-boot where I am sending http-header from server to client end. Server-side I have used Spring-boot in form of REST API, whereas at client end...

stackoverflow.com

https://github.com/mitre/HTTP-Proxy-Servlet/issues/65

 

Header parameter names are converted to lower case by proxy · Issue #65 · mitre/HTTP-Proxy-Servlet

Our internal server ran into problems when it experienced that all parameter names of the HTTP header have been converted to lower case by the proxy. More precisely: The first letter is converted t...

github.com

https://bz.apache.org/bugzilla/show_bug.cgi?id=58464#c4 

 

58464 – servletRequest.getHeaderNames() returns all header names in lower case

Status: RESOLVED WONTFIX Alias: None Product: Tomcat 7 Classification: Unclassified Component: Servlet & JSP API (show other bugs) Version: unspecified Hardware: All All Importance: P2 normal (vote) Target Milestone: --- Assignee: Tomcat Developers Mailing

bz.apache.org

 

5. (결론) HTTP header는 case-insensitive이다.

  위의 사례를 통해 "HTTP header의 대소문자가 바뀌어도 되는 것인가?"라는 궁금증이 생겼고, 이에 대해 확인한 결과 HTTP 1.1 header는 case-insensitive, 즉 대소문자 구분이 없다는 것을 확인했습니다. 또한, HTTP 2.0은 case-insensitive지만, Encoding전에 lowercase로 바꿔야한다고 합니다. HTTP 1.1 spec인 RFC2616의 4.2 Message Headers, 2014년 대체된 RFC7230의 3.2 Header Fields, 그리고 HTTP 2.0 spec인 RFC7540의 8.1.2 HTTP Header Fields에서 다음과 같은 내용을 확인할 수 있었습니다.

Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.
Just as in HTTP/1.x, header field names are strings of ASCII characters that are compared in a case-insensitive fashion. However, header field names MUST be converted to lowercase prior to their encoding in HTTP/2.