내 R2 버킷에서 영상을 바로 서빙할 수 있습니다. 다만 R2 혼자서는 못 합니다.
오브젝트 스토리지는 바이트를 보관합니다. 재생에는 화질 사다리, 캐시가 붙는 도메인, 스트림을 제한할 방법 세 가지가 더 필요하고, 대부분이 가장 먼저 시도하는 지름길은 Cloudflare 문서가 직접 막아 두었습니다.
네 질문에 먼저 답합니다
각 답은 근거를 보여주는 절로 이어집니다.
- Cloudflare R2 버킷에서 영상을 바로 스트리밍할 수 있나요?
- HLS 결과물이 이미 버킷에 있고 앞에 내 도메인이 있다면 됩니다. R2는 매니페스트와 세그먼트를 다른 객체와 똑같이 전송합니다. 다만 그 객체를 만들어 주지는 않으므로 업로드 전에 인코더가 돌아야 합니다. 네 조각 보기
- Cloudflare R2는 영상 트래픽 비용을 받나요?
- 받지 않습니다. Cloudflare 공개 가격 기준 R2의 직접 인터넷 egress는 0달러입니다. 저장은 GB당 월 0.015달러, 오퍼레이션은 백만 요청 단위로 과금되며 둘 다 재생 횟수에 따라 늘지 않습니다. 실제 비용 보기
- HLS 재생을 presigned URL로 왜 못 막나요?
- presigned URL은 객체 하나를 인가합니다. HLS 세션은 매니페스트 하나에 수백 개의 세그먼트 객체이고 그 경로는 매니페스트에 평문으로 들어 있습니다. 재생을 제한하려면 요청마다 엣지에서 인가해야 하며, 보통 수명이 짧은 토큰을 Worker가 검사합니다. 제한된 재생 보기
- R2로 영상을 서빙하면 Cloudflare 약관 위반인가요?
- 2026-08-27에 읽은 약관에는 그런 조항이 없습니다. 2025-09-12 발효 자체서비스 구독 약관의 2절은 2.1부터 2.7까지이고, 사람들이 아직 인용하는 비HTML 콘텐츠 전송 제한 조항은 그 안에 없습니다. 2.7 허용 이용 조항은 그대로 적용됩니다. 약관 확인 내용 보기
2026-08-27 NULX 엔지니어링팀 확인. 아래 모든 가격은 1차 출처로 연결됩니다.
경계
R2가 하는 일과, 끝내 해 주지 않는 일
실패하는 시도는 대부분 버킷을 영상 서비스로 착각하면서 시작합니다. R2는 경제성이 뛰어난 버킷입니다.
R2가 하는 일
- 원본과 HLS 결과물을 GB당 월 0.015달러로 보관합니다.
- 내 도메인과 Cloudflare 캐시를 앞에 두고 그 객체들을 전송합니다.
- 몇 번을 재생하든 직접 인터넷 egress에는 비용을 물리지 않습니다.
- S3 호환 API를 제공하므로 기존 업로드·수명주기 도구가 대체로 그대로 동작합니다.
R2가 하지 않는 일
- 인코딩. 오브젝트 스토리지에는 인코더가 들어 있지 않습니다.
- HLS·DASH 패키징. 세그먼트와 매니페스트는 이미 만들어진 채로 도착해야 합니다.
- 재생 세션 제한. presigned URL은 객체 하나에 서명하는 것이지 스트림에 서명하는 것이 아닙니다.
- r2.dev으로 실서비스 트래픽 처리. Cloudflare가 속도 제한을 걸고 개발 전용이라고 문서에 적어 두었습니다.
동작하는 구성
네 조각, 이 순서대로
하나라도 빠지면 알아보기 쉬운 특정 증상이 나옵니다. 어떤 증상인지는 아래 그림에 있습니다.
-
1
비공개 버킷
원본과 HLS 결과물을 보관
-
2
인코더
업로드 전에 화질 사다리를 생성
-
3
내 도메인
버킷 앞에 Cloudflare 캐시를 배치
-
4
플레이어와 인가하는 엣지
매니페스트를 읽고, 제한 재생일 때 요청을 검사
각 조각 아래에 그것이 없을 때 무엇이 깨지는지 적었습니다.
- 1. 비공개 버킷
- 공개 r2.dev은 속도 제한이 걸리고 캐시·접근 관리·봇 관리를 쓸 수 없습니다. 취향이 아니라 문서에 적힌 제품의 한계입니다.
- 2. 인코더
- 아무것도 재생되지 않습니다. 흔한 출발점은 1080p 약 5Mbps, 720p 약 2.8Mbps, 480p 약 1.4Mbps에 6초 세그먼트입니다. 해상도는 2로 나누어떨어져야 하고 원본보다 키우면 안 됩니다.
- 3. 내 도메인
- 세그먼트 요청이 전부 R2까지 도달하고 시청자가 r2.dev 속도 제한에 걸립니다. 세그먼트 요청을 캐시 적중으로 바꿔 주는 단계입니다.
- 4. 플레이어와 인가하는 엣지
- 공개 영상은 재생되지만 제한된 재생은 만들 수 없습니다. 공개된 예제 대부분이 이 조각을 TODO로 비워 둡니다.
산수
겁내기 전에 계산부터 합니다
HLS는 영상 하나를 수천 개의 작은 객체로 쪼개고, 그 하나를 가져올 때마다 백만 요청당 0.36달러인 Class B 오퍼레이션이 발생합니다. 6초 세그먼트면 시청 1시간·화질 하나당 세그먼트 요청이 약 600회입니다. 화질 하나로 1,000 시청시간을 채웠을 때 그것이 얼마인지 봅니다.
-
R2, 캐시 적중 $0.02
R2까지 도달 60,000회
-
R2, 롱테일 $0.11
R2까지 도달 300,000회
-
R2, 캐시 없음 $0.22
600,000회, 그리고 속도 제한
-
Cloudflare Stream $60.00
전송 60,000분
1시간 영상, 1,000 시청시간 기준. Cloudflare Stream은 공개 가격인 전송 1,000분당 1달러로 계산했습니다.
캐시가 전혀 안 먹는 줄조차 0.25달러가 안 됩니다. 진짜 비용은 그 뒤에 둘 있습니다. 저장: 8Mbps 원본 1시간이 약 3.6GB, 1080/720/480 사다리가 약 4.2GB를 더하므로 둘 다 보관하면 영상 1시간당 월 0.12달러 언저리입니다. 가용성: 내 도메인을 건너뛰면 돈을 더 내는 게 아니라 속도 제한에 걸립니다. 캐시를 생략하는 것은 요금 위험이 아니라 장애 위험입니다.
세그먼트당 요청 1회, 화질 1종 가정입니다. 세션이 실제로 오가는 화질 수만큼 곱하세요. R2 무료 티어가 매달 Class B 1,000만 요청까지 흡수하므로, 소규모 파일럿은 청구서가 0으로 나오고 그다음에 무슨 일이 생기는지는 알려 주지 못합니다.
developers.cloudflare.com/r2/pricing · developers.cloudflare.com/stream/pricing · developers.cloudflare.com/r2/buckets/public-buckets
내 라이브러리 기준으로 계산하기제한된 재생
HLS에서 presigned URL이 통하지 않는 이유
직접 구축한 사례 글 대부분이 여기까지 와서 멈춥니다.
- presigned URL은 정확히 객체 하나를 정해진 시간 동안 인가합니다. 반면 HLS 세션은 매니페스트 하나에 수백 개의 개별 세그먼트 객체입니다.
- 매니페스트는 세그먼트 경로를 평문으로 나열합니다. 매니페스트에만 서명하면 그것이 가리키는 세그먼트는 전부 열려 있고, 세그먼트마다 서명하려면 시청자마다 세션마다 매니페스트를 다시 써야 합니다.
- 실제로 쓰이는 방식은 엣지에서 인가하는 것입니다. 플레이어가 수명이 짧은 토큰을 들고 다니고, 버킷 앞의 Worker가 요청마다 검사하며, 버킷은 계속 비공개로 둡니다.
- 이 결정은 공개 전에 하세요. 공개 객체로 운영하다 나중에 인가 재생으로 옮기면, 이미 나간 URL은 전부 유출된 것으로 취급해야 합니다.
아무도 답하지 않는 질문
Cloudflare 약관을 직접 확인한 내용
이 질문의 검색 결과는 묻는 사람만 가득하고 확인한 사람이 없어서, 저희가 직접 확인했습니다. 2026-08-27 기준 cloudflare.com/terms에 게시된 자체서비스 구독 약관은 2025-09-12 발효본이며, 2절은 2.1 서비스 접근, 2.2 서비스 이용, 2.3 자격증명, 2.4 구독 조건, 2.5 고객 콘텐츠와 네트워크 데이터, 2.6 무료·체험 서비스, 2.7 허용 이용으로 구성됩니다. 포럼에서 아직도 인용되는 비HTML 콘텐츠 전송 제한 조항은 그 안에 없습니다.
- R2는 GB·오퍼레이션 단위 가격이 공개된 유료 제품이고, Cloudflare의 R2 문서 자체가 실서비스 트래픽에 커스텀 도메인을 붙이라고 안내합니다.
- 2.7 허용 이용 조항은 그대로 적용됩니다. 영상이 무엇이고 어떻게 확보한 것인지는 여전히 그 조항의 대상입니다.
- 약관은 바뀌고 이 페이지는 특정 시점의 기록입니다. 이 구조로 가기 전에 약관을 직접 열어 발효일과 2절을 확인하세요.
명시된 날짜에 읽은 내용의 기록이며, 법률 자문이 아닙니다.
실패 유형
여섯 가지 증상과 실제 원인
| 보이는 증상 | 실제 원인 |
|---|---|
| 나는 잘 되는데 남들은 429를 받는다 | 실서비스에 r2.dev을 썼습니다. Cloudflare가 속도 제한을 걸고 개발 전용이라고 문서에 적어 둔 경로입니다. |
| 매니페스트는 내려오는데 재생이 안 된다 | Content-Type 누락입니다. .m3u8은 application/vnd.apple.mpegurl, .ts는 video/mp2t가 필요하고, 뭉뚱그린 octet-stream은 대부분의 플레이어를 멈춥니다. |
| 브라우저에서만 실패한다 | 버킷 도메인에 CORS 헤더가 없습니다. hls.js는 XHR로 세그먼트를 가져와 CORS의 적용을 받지만, 같은 파일을 가리키는 네이티브 video 요소는 받지 않습니다. |
| 원본 .mp4에서 탐색이 느리거나 깨진다 | 프로그레시브 MP4는 HTTP range 요청과 앞쪽에 놓인 moov atom이 필요합니다. mp4를 그대로 버킷에 올린 경우 여기서 걸립니다. |
| 오퍼레이션 수가 예상보다 훨씬 많다 | 캐시가 한 번도 동작하지 않았습니다. 트래픽이 내 도메인으로 들어오는지, 캐시 규칙이 매니페스트만이 아니라 세그먼트 경로까지 잡는지 확인하세요. |
| 화질별 파일 해상도가 이상하거나 원본보다 커진다 | ffmpeg scale 필터에 2의 배수 보정과 원본 해상도 상한이 없습니다. |
이 페이지가 일부러 남겨 둔 두 질문
여기서는 이 구조가 어떻게 동작하는지를 설명합니다. 셋 중 무엇을 고를지, 내 라이브러리가 얼마일지는 각각 다른 페이지에 있습니다.
NULX는 이미 갖고 계신 저장소 위에서 이 구성을 대신 운영합니다. 무료 플랜에 인코딩 2시간이 포함됩니다.
무료 플랜으로 시작하기