왜 Transform Rule인가
R2의 커스텀 도메인으로 정적 파일을 서빙할 때 기본 동작은 정확한 객체 키를 요청하는 것뿐이다. 즉 /
를 열었다고 해서 자동으로 /index.html
을 찾지 않는다. 이때 URL Rewrite를 사용하면 브라우저 주소를 바꾸지 않고 서버 사이드에서 경로만 index.html
로 바꿔 전달할 수 있다. Transform Rule은 원 서버에 도달하기 전에 경로/쿼리를 변형하는 규칙이며, 브라우저 주소창은 그대로 유지된다. Cloudflare Docs+1
참고: 리다이렉트는 주소가 바뀌는 클라이언트 사이드 동작이며, 이 글의 목적에는 부적합하다. 필요 시 Redirect Rules/Page Rules를 사용하면 된다. Cloudflare Docs
목표
https://r2.example.com/
요청을 내부적으로/index.html
로 Rewrite하여 주소는 유지하고 콘텐츠만index.html
을 보여주도록 만든다.
- 하위 폴더(
.../docs/
)도 동일하게.../docs/index.html
을 보여주도록 한다.
- 선택 사항: SPA 라우팅을 위해 확장자가 없는 경로 전부를
/index.html
로 돌리는 보조 규칙을 추가한다.
사전 준비
- R2 버킷이 퍼블릭 액세스이며 커스텀 도메인에 연결되어 있어야 한다. 연결 절차는 R2 문서의 Public buckets / Custom Domains 가이드를 따른다. Cloudflare Docs+1
설정 절차: Transform Rule로 /
→ index.html
Rewrite이트
- Cloudflare 대시보드에서 사이트(예:
r2.example.com
이 속한 Zone)를 연다.
- Rules → Transform Rules → URL Rewrite → Create rule로 들어간다. Cloudflare Docs
(http.request.method in {"GET" "HEAD"})
and (not ends_with(http.request.uri.path, "index.html"))
and (
http.request.uri.path eq "/"
or ends_with(http.request.uri.path, "/")
)
- 의미: GET/HEAD 이면서, 이미
index.html
로 끝나지 않고, 경로가/
이거나/
로 끝나는 경우에만 매칭한다.
- 이 필터 표현식은 Cloudflare Rules 언어를 사용하며
ends_with
등 문자열 함수가 지원된다. Cloudflare Docs
- 동작(Then) – Rewrite to → Dynamic을 선택한다.
- URI path:
concat(http.request.uri.path, "index.html")
- Query string: Preserve
- URI path:
Dynamic rewrite에서는 concat()
등 전용 함수 사용이 가능하다. Cloudflare Docs
- 저장 및 배포한다.
이제 /
는 /index.html
, /docs/
는 /docs/index.html
이 내부적으로 호출되어 콘텐츠가 표시된다.
(선택) SPA 라우팅용 보조 규칙
리액트·뷰 등 SPA에서 확장자가 없는 모든 경로를 /index.html
로 돌리고 싶다면, 위 규칙 아래에 보조 규칙을 추가한다.
- 필터(When)
(http.request.method in {"GET" "HEAD"})
and not (http.request.uri.path matches ".*\\.[^/]+$")
- 의미: 파일 확장자(마침표 포함)가 없는 경로만 매칭한다. 정규식 매칭은
matches
연산자를 사용한다. Cloudflare Docs
- Rewrite to → Dynamic
URI path = "/index.html"
Query string = Preserve
주의: 이 보조 규칙은 실제 정적 자원(/main.js, /styles/app.css, /image.png)에는 매칭되지 않도록 설계되어야 한다. 규칙 순서를 위의 루트/폴더용 규칙 다음으로 둔다.
검증 방법
브라우저로 확인
https://r2.example.com/
접속 시 주소창이 그대로이면서index.html
내용이 보여야 한다.
적용전
적용후
curl로 헤더만 확인
# 주소는 유지되지만 서버 내부적으로 index.html을 제공함
curl -I https://r2.example.com/
자주 하는 실수와 체크리스트
- Rewrite와 Redirect트 혼동: 리다이렉트는 주소가 바뀐다. 본 글은 URL Rewrite가 목적이다. Cloudflare Docs
- 호스트네임 변경 불가: URL Rewrite만으로 호스트명을 바꿀 수 없다. 호스트 변경은 Origin Rule 등 별도 기능을 사용한다. Cloudflare Docs
- R2 커스텀 도메인 선행: Transform Rule은 내 Zone 트래픽에 적용된다. R2 커스텀 도메인을 먼저 연결해야 의미가 있다. Cloudflare Docs
대안: 리다이렉트 규칙이나 워커
- 주소가 바뀌어도 되면 Redirect/Page Rules로
/
→/index.html
301/302를 줄 수 있다. 단, 브라우저 주소가 바뀌기 때문에 권장하지 않는다. Cloudflare Docs
- 더 복잡한 로직이 필요하면 Cloudflare Worker에서 R2를 직접 읽어 동적으로 응답하도록 구현할 수 있다. R2를 Workers에서 다루는 공식 예제가 제공된다. Cloudflare Docs
참고 문서
- Cloudflare Docs — URL Rewrite Rules 개요 및 동작, Static/Dynamic Rewrite 설명 Cloudflare Docs
- Cloudflare Docs — Transform Rules 제품 안내 Cloudflare Docs
- Cloudflare Docs — Rewrite/Redirect 표현식 함수(Rules 언어 함수) Cloudflare Docs
- Cloudflare Docs — URL Rewrite: 대시보드에서 생성하기 Cloudflare Docs
- Cloudflare Docs — R2 Public buckets & Custom Domains Cloudflare Docs
- Cloudflare Docs — Origin Rules로 R2 커스텀 도메인 연동 튜토리얼 Cloudflare Docs
부록: 최종 규칙 요약
규칙 A — 루트/폴더 경로를 index.html로 Rewrite
- 필터
(http.request.method in {"GET" "HEAD"}) and (not ends_with(http.request.uri.path, "index.html")) and ( http.request.uri.path eq "/" or ends_with(http.request.uri.path, "/") )
- Rewrite to → Dynamic
- URI path:
concat(http.request.uri.path, "index.html")
- Query string: Preserve
- URI path:
규칙 B — SPA Fallback (선택)
- 필터
(http.request.method in {"GET" "HEAD"}) and not (http.request.uri.path matches ".*\\.[^/]+$")
- Rewrite to → Dynamic
- URI path:
/index.html
- Query string: Preserve
- URI path: