
셀프 호스팅 GitLab 에서 git LFS 푸시가 ContentLength=N with Body length M 으로 끊기면, GitLab 이 http 로 내려 준 LFS 업로드 주소가 https 로 301 리다이렉트되며 업로드 본문이 따라가지 못한 것일 수 있습니다. 당장은 클라이언트 설정 두 줄(lfs.transfer.enablehrefrewrite + insteadOf)로, 근본적으로는 external_url 을 https 로 맞춰 풉니다. 큰 파일을 LFS 로 관리하는 개인 프로젝트 저장소에서 재시도해도 같은 자리에서 멈추던 문제를 푼 기록입니다.
요약
- 셀프 호스팅 GitLab 에 LFS 푸시가
ContentLength=N with Body length M으로 끊기고, 재시도해도 같은 자리에서 멈춥니다.- GitLab 이 내려 준 LFS 업로드 주소가 http 라서, 프록시의 https 301 을 거치며 업로드 본문이 따라가지 못한 것입니다.
- 당장은
lfs.transfer.enablehrefrewrite+insteadOf두 줄, 근본은 GitLabexternal_url을 https 로 맞추는 것입니다.
1. 증상: 재시도해도 같은 자리에서 멈춘다
2026년 9월 말, LFS 파일 76개(약 150MB)가 딸린 커밋을 푸시하자 이렇게 끊겼습니다(호스트·경로는 예시로 바꿨습니다).
LFS: Put "https://git.example.com/group/project.git/gitlab-lfs/objects/<oid>/1564645": net/http: HTTP/1.x transport connection broken: http: ContentLength=1564645 with Body length 1400805
LFS: Put "https://git.example.com/group/project.git/gitlab-lfs/objects/<oid>/228463": net/http: HTTP/1.x transport connection broken: http: ContentLength=228463 with Body length 0
error: failed to push some refs to 'ssh://git.example.com/group/project.git'
ContentLength=N with Body length M 은 Go net/http 의 오류로, 「N 바이트를 보낸다고 해 놓고 본문은 M 바이트뿐이었다」는 뜻입니다. 76개 중 34개에서 멈췄고 재시도해도 풀리지 않았습니다. 매번 재현되는 실패라면 네트워크보다 설정을 의심할 때입니다.
2. GIT_CURL_VERBOSE 로 응답 코드 세기
GIT_CURL_VERBOSE=1 을 주면 git-lfs 도 요청·응답 헤더를 찍습니다. 푸시 한 번 동안의 응답 코드를 셌습니다.
GIT_CURL_VERBOSE=1 git -c lfs.concurrenttransfers=1 lfs push origin main > lfs_trace.log 2>&1
grep -E "^< HTTP" lfs_trace.log | sort | uniq -c
grep -i "^< Location" lfs_trace.log | sort | uniq -c | head
433 < HTTP/1.1 301 Moved Permanently
60 < HTTP/1.1 200 OK
대부분이 301 이었고, Location 은 전부 같은 주소의 https 판이었습니다. 요청이 http 로 나가고 있었다는 뜻입니다. 이 로그에는 Authorization 헤더가 그대로 찍히니, 어디에 붙일 때는 꼭 지우세요.
3. 원인: LFS 업로드 주소가 http 로 내려온다
git 원격이 SSH 여도 LFS 파일은 HTTP(S) 로 올라갑니다. git-lfs 는 서버가 내려 준 파일별 업로드 주소(href)를 그대로 쓰는데, 이 주소가 http:// 였습니다. 저희 GitLab 은 앞단 리버스 프록시가 TLS 를 끝내는 구성이고, GitLab 자신의 주소 설정(external_url)이 http 로 되어 있었습니다.
그래서 업로드(PUT)가 http 로 나가고, 프록시가 301 로 https 에 넘깁니다. 리다이렉트를 따라 다시 보내는 요청에 본문이 온전히 실리지 않아, 선언한 길이와 실제 본문이 어긋나며 끊깁니다. 오류의 Body length 가 0 이거나 선언보다 짧은 것이 그 흔적입니다.

4. 바로 쓰는 해결: 클라이언트 설정 두 줄
git config lfs.transfer.enablehrefrewrite true
git config url."https://git.example.com/".insteadOf "http://git.example.com/"
git lfs push origin main
git push -u origin main
Uploading LFS objects: 100% (76/76), 150 MB | 23 MB/s, done.
76개가 전부 올라갔습니다.
url.<base>.insteadOf는 git 의 주소 바꿔 쓰기 설정이지만, git-lfs 는 서버가 내려 준 href 에는 기본으로 적용하지 않습니다.lfs.transfer.enablehrefrewrite를 켜야 href 에도 적용됩니다.--global없이 넣으면 그 저장소에만 적용됩니다. 커밋하는.lfsconfig로는 나눠 줄 수 없습니다 — 보안상 정해진 키만 읽고, 이 둘은 목록에 없습니다.
5. 근본 해결: external_url 을 https 로
GitLab 문서는 사용자가 실제로 접속하는 주소를 external_url 에 적으라고 합니다. 프록시가 TLS 를 끝낸다면 external_url 은 https 로 두고, GitLab 에 딸린 NGINX 의 SSL 처리는 끕니다(2026년 10월 문서 기준).
# /etc/gitlab/gitlab.rb
external_url 'https://git.example.com'
gitlab_rails['nginx']['listen_port'] = 80
gitlab_rails['nginx']['listen_https'] = false
그다음 sudo gitlab-ctl reconfigure 입니다. 저희는 서버 설정이라 이쪽은 손대지 않고 클라이언트 설정으로만 풀었습니다. 다만 우회 설정은 사람마다·저장소마다 넣어야 하니, 여럿이 쓰는 서버라면 근본 해결이 맞습니다.
6. 체크리스트
- ☐ LFS 푸시가 같은 자리에서 멈추면 네트워크보다 설정을 의심한다
- ☐
GIT_CURL_VERBOSE=1로 301 이 끼어 있는지 센다(Authorization은 지운다) - ☐ 우회:
enablehrefrewrite+insteadOf두 줄(저장소마다) - ☐ 근본:
external_url을 https 로, 번들 NGINX 의 SSL 은 끈다
다음 글에서는 「macOS launchd·ssh 에서 Desktop 파일을 못 읽을 때 — TCC 함정」을 다루겠습니다.
참고
'AI' 카테고리의 다른 글
| FastAPI 캐치올 라우트가 새 페이지를 삼킬 때 — 등록 순서가 곧 매칭 순서 (0) | 2026.10.21 |
|---|---|
| macOS launchd·ssh 에서 Desktop 파일을 못 읽을 때 — TCC 함정 (0) | 2026.10.20 |
| Seedance 2.5 API 를 보류한 이유와 「구독 크레딧 ≠ API」 결제 함정 (0) | 2026.10.18 |
| Kling 3.0 API 요소(element)로 여러 컷에서 같은 인물·목소리 유지하기 (0) | 2026.10.17 |
| Veo 3.1 API 실사용 후기 — 시키지 않은 대답과 8초 단위 과금, 뺀 이유 (0) | 2026.10.16 |