ERR_HTTP2_PROTOCOL_ERROR 오류 원인과 해결 방법
핵심 요약
ERR_HTTP2_PROTOCOL_ERROR(Chromium 오류 -337)는 페이지를 실어 나르는 HTTP/2 스트림이 실패했다는 뜻입니다. 서버, 프록시 또는 CDN이 도중에 스트림을 재설정했거나(Chrome은 이런 재설정을 모두 -337로 보고합니다), Chrome이 HTTP/2 규칙을 어긴 헤더를 거부한 경우입니다. 방문자라면 새로고침, 시크릿 창, 해당 사이트의 쿠키 삭제, 백신의 HTTPS 검사 일시 중지로 해결될 때도 있지만, 대부분은 사이트 쪽에서만 고칠 수 있습니다. 사이트 운영자라면 흔한 원인은 서버나 프록시가 끝까지 보내지 못한 응답(디스크 가득 참, 쓸 수 없는 임시 폴더, 앱 충돌), 본문과 맞지 않는 Content-Length, Transfer-Encoding 같은 금지된 헤더입니다. curl --http2 -v로 재현해 보세요.
지금 확인하기

읽기 시작
- ERR_
HTTP2_ PROTOCOL_ ERROR란? - HTTP/2가 잘못된 형식으로 간주하는 것
- ERR_
HTTP2_ PROTOCOL_ ERROR의 원인 - 해결 방법 1: 강력 새로고침 후 시크릿 창에서 열기
- 해결 방법 2: 해당 사이트의 쿠키와 캐시 파일 삭제하기
- 해결 방법 3: 백신 HTTPS 검사와 확장 프로그램 일시 중지
- 해결 방법 4: Chrome 업데이트 및 다른 브라우저 확인
- 해결 방법 5: HTTP/2 없이 페이지 테스트하기
- 웹사이트 운영자용: HTTP/2 프로토콜 오류 해결하기
- 1. curl로 재현하기
- 2. 스트림 도중 잘린 응답 확인하기
- 3. Content-Length와 금지된 헤더 수정하기
- ERR_
HTTP2_ PROTOCOL_ ERROR와 비슷한 오류 비교 - 자주 묻는 질문
ERR_HTTP2_PROTOCOL_ERROR란?
ERR_HTTP2_PROTOCOL_ERROR는 페이지를 실어 나르는 HTTP/2 스트림이 실패했다는 뜻의 Chrome·Edge 오류입니다. 서버가 스트림이 끝나기 전에 재설정했거나, 브라우저가 거부하는 헤더를 보냈다는 의미입니다. Chromium 내부에서는 네트워크 오류 -337이며, "There is an HTTP/2 protocol error"로 설명되어 있습니다(net_error_list.h). Chrome에는 이 오류 전용 페이지가 없어 일반 오류 페이지가 표시됩니다. '사이트에 연결할 수 없음'이라는 제목과 함께 '…의 웹페이지가 일시적으로 다운되었거나 새 웹 주소로 완전히 이동했을 수 있습니다.'라는 문구가 나오고, 그 아래에 ERR_HTTP2_PROTOCOL_ERROR가 표시됩니다. Edge도 자체 오류 화면에 같은 코드를 표시합니다.
HTTP/2는 요즘 대부분의 HTTPS 사이트가 쓰는 더 빠른 HTTP입니다. 각 요청은 하나의 연결 안에서 독립된 스트림으로 오가며, 양쪽 모두 재설정으로 스트림을 취소할 수 있습니다. Chrome은 서버에서 온 재설정을 서버의 이유와 상관없이 프로토콜 오류로 보고합니다. 또한 헤더가 HTTP/2 규칙을 어길 때(Transfer-Encoding 헤더, 헤더 이름의 대문자, 누락된 상태 등)에는 Chrome이 직접 스트림을 취소합니다.
개발자는 DevTools 콘솔에서 net::ERR_HTTP2_PROTOCOL_ERROR 200 (OK) 형태로 자주 보게 됩니다. 이 조합은 강력한 단서입니다. 헤더는 올바르고 서버가 200으로 응답했지만, 본문이 도착하는 도중에 스트림이 실패했다는 뜻입니다.
페이지가 실제로 보내는 헤더 확인하기
HTTP/2가 잘못된 형식으로 간주하는 것
HTTP/2 표준(RFC 9113)은 응답을 잘못된 형식으로 만드는 실수를 나열합니다. 실제 오류 대부분이 이 문제들 때문이며, 각각이 브라우저에 어떻게 전달되는지는 다음과 같습니다.
| 규칙 | 이를 어기는 경우 |
|---|---|
| Content-Length는 본문 크기와 같아야 함 | 서버나 플러그인이 선언한 크기와 실제로 보낸 크기가 다릅니다. 앞단의 프록시, CDN 또는 서버가 스트림을 재설정합니다 |
| 응답은 깔끔하게 끝나야 함 | 서버, 프록시 또는 앱이 응답을 끝내지 못하고(디스크 가득 참, 충돌, 시간 초과) 스트림을 재설정합니다. nginx는 이미 시작한 응답을 완료하지 못할 때 이렇게 합니다 |
| 연결 전용 헤더 금지 | HTTP/2 응답의 Transfer-Encoding(Chrome이 거부). Connection, Keep-Alive, Proxy-Connection, Upgrade도 금지이며, curl 같은 엄격한 클라이언트는 이를 거부합니다 |
| 필드 이름은 소문자여야 함 | 대문자가 들어간 헤더 이름이 HTTP/2로 그대로 전달됩니다 |
| 유효한 헤더 값 | 헤더 값 안의 줄바꿈이나 그 밖의 금지된 문자 |
| 유효한 :status 의사 헤더 | 누락되었거나 읽을 수 없는 상태 코드 |
ERR_HTTP2_PROTOCOL_ERROR의 원인
대부분은 서버 쪽에서 발생하지만, 내 컴퓨터의 몇 가지 요소가 들어오는 응답을 손상시킬 수도 있습니다:
- 서버 쪽: 서버나 프록시가 끝내지 못하는 응답(디스크 공간이 부족하거나 임시 파일을 쓰지 못하는 프록시, 스트리밍 중 충돌하는 앱, 시간 초과), 앞단 프록시가 포기하게 만드는 잘못된 Content-Length 값, 금지되거나 유효하지 않은 헤더, 오래된 서버나 CDN 설정의 HTTP/2 지원 버그.
- 사용자 쪽: HTTPS를 검사하며 트래픽을 다시 쓰는 백신이나 방화벽 소프트웨어, 요청이나 응답을 수정하는 브라우저 확장 프로그램, 서버가 제대로 처리하지 못하는 요청(예: 지나치게 큰 쿠키), 그리고 드물게 오래된 Chrome 버전.
해결 방법 1: 강력 새로고침 후 시크릿 창에서 열기
Ctrl + Shift + R(Mac: Cmd + Shift + R)을 눌러 캐시 없이 새로고침하세요. 한 번 잘렸던 응답이 다음 시도에서는 정상적으로 도착할 수 있습니다.
그다음 시크릿 창(Ctrl + Shift + N, Mac Cmd + Shift + N)에서 페이지를 열어 보세요. 시크릿 창에는 쿠키도, 평소 프로필의 캐시도 없고 확장 프로그램도 기본적으로 꺼져 있습니다. 여기서 페이지가 열린다면 해결 방법 2나 해결 방법 3으로 평소 창에서도 해결됩니다.
해결 방법 2: 해당 사이트의 쿠키와 캐시 파일 삭제하기
사이트의 쿠키와 캐시 파일을 지우면 Chrome이 보내는 요청이 달라져(쿠키가 줄고, 다시 확인할 캐시 사본이 없어져) 서버 버그를 피할 수 있습니다. 주소창 왼쪽의 사이트 정보 보기를 클릭 → 쿠키 및 사이트 데이터 → 기기 내 사이트 데이터 관리 → 삭제 후 새로고침하세요. 더 넓게 정리하려면 Ctrl + Shift + Delete를 누르고 지난 7일 동안의 캐시된 이미지 및 파일을 지우세요.
해결 방법 3: 백신 HTTPS 검사와 확장 프로그램 일시 중지
HTTPS를 복호화해 검사하는 보안 소프트웨어는 모든 HTTP/2 연결의 중간에 자리 잡고 있습니다. 이 소프트웨어의 HTTP/2 처리에 버그가 있거나 오래되었다면 스트림을 망가뜨리거나 Chrome이 거부하는 헤더를 그대로 넘길 수 있습니다. HTTPS 검사 기능만 끄고(Avast/AVG: 웹 실드 → Enable HTTPS scanning, ESET: SSL/TLS 프로토콜 필터링, Kaspersky: 암호화된 연결 검사) 새로고침하세요. 그것으로 해결된다면 백신을 업데이트하고 해당 사이트를 예외로 추가하세요.
다음으로 chrome://extensions에서 모든 확장 프로그램을 끄고 새로고침한 뒤, 하나씩 다시 켜 보세요. 광고 차단기, 개인 정보 보호 도구, 헤더를 수정하는 모든 것이 흔한 원인입니다.
해결 방법 4: Chrome 업데이트 및 다른 브라우저 확인
chrome://settings/help를 열어 대기 중인 업데이트를 설치하고 브라우저를 다시 시작하세요. 그다음 같은 페이지를 Firefox나 Safari에서, 그리고 다른 기기나 네트워크에서도 열어 보세요. 어디서든 실패한다면 사이트 자체가 고장 난 것이며 고칠 수 있는 사람은 운영자뿐입니다(백신의 HTTPS 검사는 한 컴퓨터의 모든 브라우저에 영향을 주므로 두 번째 기기가 진짜 시험입니다). Chrome이나 Edge에서만 실패한다면 사이트가 Chrome이 거부하는 헤더를 보내고 있을 가능성이 높지만, 해결 방법 2와 3을 다시 시도해 볼 가치는 있습니다.
해결 방법 5: HTTP/2 없이 페이지 테스트하기
Chrome은 HTTP/2(와 HTTP/3)를 끈 채로 시작할 수 있으며, 이를 통해 HTTP/2가 관련되어 있는지 확인할 수 있습니다. 터미널에서 두 번째로 별도의 Chrome을 시작하세요. 추가 프로필 폴더를 지정하면 평소 Chrome이 열려 있어도 새 인스턴스가 됩니다.
# Windows (Command Prompt)
"C:\Program Files\Google\Chrome\Application\chrome.exe" --user-data-dir="%TEMP%\h2test" --disable-http2 --disable-quic
# macOS (Terminal)
open -na "Google Chrome" --args --user-data-dir=/tmp/h2test --disable-http2 --disable-quic그 창에서 페이지가 열린다면 HTTP/2 응답이 문제이므로 운영자가 고쳐야 합니다. 이제 ERR_CONTENT_LENGTH_MISMATCH나 ERR_INCOMPLETE_CHUNKED_ENCODING으로 실패한다면, 프로토콜과 상관없이 서버가 응답을 도중에 끊고 있는 것입니다. 끝나면 그 창을 닫으세요. 평소 Chrome에는 영향이 없습니다.
웹사이트 운영자용: HTTP/2 프로토콜 오류 해결하기
서로 다른 네트워크와 브라우저를 쓰는 방문자들이 이 오류를 보고한다면 문제는 내 서버 스택에 있습니다. 다음 세 가지 점검으로 거의 모든 원인을 찾을 수 있습니다.
1. curl로 재현하기
# HTTP/2: watch the end of the output
curl -sv --http2 https://example.com/broken-page -o /dev/null
# A stream the server gave up on ends with something like:
# HTTP/2 stream 1 was not closed cleanly: INTERNAL_ERROR (err 2)
# (curl 8.19+: HTTP/2 stream 1 reset by server (error 0x2 INTERNAL_ERROR);
# "reset by curl" means curl rejected the response itself)
# Same URL over HTTP/1.1 for comparison
curl -sv --http1.1 https://example.com/broken-page -o /dev/nullHTTP/1.1은 되는데 HTTP/2만 실패한다면 HTTP/2가 관련되어 있는 것입니다. "Reset by server"는 서버나 프록시가 응답 도중에 포기했다는 뜻입니다. curl은 Chrome과 똑같은 것을 검사하지 않으므로(Chrome이 통과시키는 Content-Length 불일치와 연결 헤더를 curl은 거부합니다) 증거가 아니라 강한 단서로 보세요. HTTP 헤더 검사기에서는 앱이 설정한 헤더를 네트워크 바깥에서 본 모습으로 확인할 수 있습니다.
2. 스트림 도중 잘린 응답 확인하기
서버 쪽에서 가장 흔한 원인은 시작은 했지만(상태 200, 헤더 전송 완료) 끝내지 못하는 응답입니다. nginx는 앱에서 온 큰 응답을 임시 파일에 버퍼링하는데, 디스크가 가득 찼거나 nginx가 임시 폴더에 쓰지 못하면 스트림을 재설정하고 브라우저는 프로토콜 오류를 보고합니다. 다음을 확인하세요.
df -h # is any disk full?
sudo grep -E "No space left|Permission denied" /var/log/nginx/error.log | tail
ls -ld /var/lib/nginx/proxy /var/lib/nginx/fastcgi # Debian/Ubuntu packages
ls -ld /var/cache/nginx/proxy_temp /var/cache/nginx/fastcgi_temp # nginx.org packages
# the temp folders must be writable by the nginx worker user앱 자체도 확인하세요. 큰 페이지나 다운로드를 스트리밍하는 도중 크래시하거나, 타임아웃되거나, 메모리 제한에 걸리는 PHP나 Node 프로세스도 같은 결과를 냅니다. 오류 발생 시각 전후의 앱 로그에 대개 흔적이 남아 있습니다.
3. Content-Length와 금지된 헤더 수정하기
- Content-Length: 서버가 계산하게 두세요. 플러그인, 미들웨어 또는 웹 서버가 출력을 함께 압축하거나 수정한다면 앱 코드에서 직접 설정하지 마세요. 선언한 길이가 실제로 보낸 바이트 수와 더 이상 맞지 않게 됩니다. PHP 코드에서 길이를 설정하는데 플러그인이나 PHP 설정도 출력을 압축하는 경우가 WordPress 사이트에서 이런 일이 생기는 한 가지 방식입니다.
- Transfer-Encoding: HTTP/2 응답에서는 절대 보내지 마세요. Chrome은 이 헤더를 보는 순간 스트림을 재설정합니다. RFC 9113은
Connection,Keep-Alive,Proxy-Connection,Upgrade도 금지합니다. Chrome은 이들을 무시하지만 curl 같은 엄격한 클라이언트는 거부하며, nginx는 프록시된 응답에서 대부분을 대신 제거해 줍니다. - 헤더 값: 어떤 헤더에도 줄바꿈이나 제어 문자가 없는지 확인하세요.
Content-Disposition의 파일 이름처럼 사용자 입력이 헤더에 그대로 들어가서 생기는 경우가 많습니다. - 앞단의 CDN: Cloudflare나 다른 CDN을 쓴다면 오리진을 직접 테스트해(curl의
--resolve또는 hosts 항목 사용) 오류가 오리진에서 오는지 CDN에서 오는지 가려내세요.
ERR_HTTP2_PROTOCOL_ERROR와 비슷한 오류 비교
| 오류 | 코드 | 무슨 일이 일어났나 |
|---|---|---|
| ERR_HTTP2_PROTOCOL_ERROR | -337 | HTTP/2 응답이 프로토콜 규칙을 어김 |
| ERR_QUIC_PROTOCOL_ERROR | -356 | HTTP/3(QUIC)에서 발생한 같은 종류의 실패 |
| ERR_SSL_PROTOCOL_ERROR | -107 | HTTP/2가 시작되기 전에 HTTPS(TLS) 핸드셰이크가 실패함 |
| ERR_CONNECTION_CLOSED | -100 | 페이지가 도착하기 전에 연결이 닫힘 |
| ERR_EMPTY_RESPONSE | -324 | 서버가 아무것도 보내지 않음 |
자세한 가이드: ERR_QUIC_PROTOCOL_ERROR, ERR_SSL_PROTOCOL_ERROR, ERR_CONNECTION_CLOSED, ERR_EMPTY_RESPONSE. 사이트의 인증서와 HTTPS 설정을 외부에서 확인하려면 SSL 인증서 확인 도구를 사용하세요.
내 사이트가 실제로 보내는 헤더 확인하기
DNS Robot의 HTTP 헤더 검사기는 저희 서버에서 HTTP/1.1로 아무 URL이나 가져와 상태 코드와 앱이 설정한 모든 응답 헤더(Content-Length, Content-Encoding, Content-Disposition)를 보여 줍니다. curl --http2 결과와 비교해 보세요.
출처
- net_error_list.h (HTTP2_PROTOCOL_ERROR -337), Chromium
- RFC 9113: HTTP/2, 잘못된 형식의 메시지, IETF
- HTTP/2: ERR_HTTP2_PROTOCOL_ERROR, Cloudflare


