Cloudflare Tunnel 경로 기반 라우팅: catch-all 규칙은 왜 마지막이어야 할까
하나의 도메인에서 메인 웹사이트, 블로그, 문서, FAQ를 각각 다른 서비스로 운영하려면 URL 경로에 따라 요청을 나눠야 합니다. 이를 **경로 기반 라우팅(Path-based Routing)**이라고 합니다.
Cloudflare Tunnel에서는 Published Application Route 또는 ingress rule에 호스트명과 경로 정규식을 지정해 이 구조를 만들 수 있습니다. 설정 자체는 간단하지만 규칙의 순서를 잘못 잡으면 새로 추가한 경로가 계속 404를 반환할 수 있습니다.
핵심은 한 문장입니다.
구체적인 경로 규칙을 먼저 두고, 모든 요청을 받는 catch-all 규칙은 마지막에 둡니다.
경로 기반 라우팅이 필요한 이유
다음처럼 하나의 호스트 아래에 여러 서비스가 있다고 가정해 보겠습니다.
| 공개 경로 | 역할 | 전달 대상 |
|---|---|---|
/docs |
공개 문서 | SERVICE_DOCS_AND_FAQ |
/blog |
기술 블로그 | SERVICE_BLOG |
/faq |
자주 묻는 질문 | SERVICE_DOCS_AND_FAQ |
| 그 외 경로 | 메인 웹사이트 | SERVICE_MAIN |
사용자는 모두 duolabs.co.kr을 방문하지만, Cloudflare Tunnel은 요청 경로를 보고 서로 다른 오리진 서비스로 전달합니다. 이 방식은 콘텐츠 주소를 한 도메인에 모으면서도 애플리케이션을 독립적으로 배포할 수 있다는 장점이 있습니다.
Cloudflare Tunnel은 위에서부터 규칙을 평가합니다
Cloudflare 공식 문서에 따르면 cloudflared는 ingress rule을 위에서 아래로 평가하고, 처음 일치한 규칙을 사용합니다. 마지막 규칙은 모든 나머지 요청을 처리하는 catch-all이어야 합니다.
따라서 다음 순서가 안전합니다.
1. duolabs.co.kr ^/docs(/|$) -> SERVICE_DOCS_AND_FAQ
2. duolabs.co.kr ^/blog(/|$) -> SERVICE_BLOG
3. duolabs.co.kr ^/faq(/|$) -> SERVICE_DOCS_AND_FAQ
4. duolabs.co.kr * -> SERVICE_MAIN반대로 * 규칙을 위에 두면 /faq 요청도 먼저 catch-all과 일치합니다. 이후의 /faq 규칙은 평가되지 않으며 요청은 메인 웹사이트로 전달됩니다. 메인 웹사이트에 /faq 페이지가 없다면 사용자는 404를 보게 됩니다.
/faq 경로 정규식은 어떻게 작성할까요?
권장하는 짧은 표현은 다음과 같습니다.
^/faq(/|$)이 정규식은 /faq 다음에 슬래시가 오거나 경로가 끝나는 경우만 허용합니다.
/faq→ 일치합니다./faq/→ 일치합니다./faq/getting-started→ 일치합니다./faq-old→ 일치하지 않습니다.
다음처럼 전체 문자열을 명시하는 표현도 같은 용도로 사용할 수 있습니다.
^/faq(/.*)?$두 표현 모두 올바르지만 기존 /docs, /blog 규칙이 ^/경로(/|$) 형태라면 같은 형식으로 통일하는 편이 읽고 관리하기 쉽습니다.
단순히 ^/faq만 사용하면 /faq-old처럼 의도하지 않은 경로까지 매칭할 수 있으므로 경계 조건을 넣는 것이 좋습니다.
설정 후 루트 페이지만 확인하면 부족합니다
라우팅을 저장한 뒤에는 최소한 다음 주소를 각각 확인해야 합니다.
curl -I https://duolabs.co.kr/faq
curl -I https://duolabs.co.kr/faq/
curl -I https://duolabs.co.kr/faq/sitemap.xml화면 HTML이 200이어도 CSS나 JavaScript가 다른 경로에서 로드되면 빈 화면처럼 보일 수 있습니다. 브라우저 개발자 도구의 Network 탭에서 정적 자산 요청도 같은 오리진으로 전달되는지 함께 확인하는 것이 좋습니다.
자주 발생하는 실수
1. catch-all을 구체적인 규칙보다 위에 배치합니다
가장 흔한 문제입니다. 첫 번째 일치 규칙에서 평가가 끝나므로 구체적인 경로가 catch-all 아래에 있으면 실행될 기회가 없습니다.
2. 경로 경계를 생략합니다
^/faq는 짧지만 /faq-old도 포함할 수 있습니다. ^/faq(/|$)처럼 슬래시 또는 경로 끝을 명시하는 편이 안전합니다.
3. 페이지 하나만 확인합니다
목록 페이지, 상세 페이지, sitemap, robots, 정적 자산은 서로 다른 하위 경로를 사용합니다. 대표 URL 하나만 확인하면 일부 경로가 잘못된 상태를 놓칠 수 있습니다.
4. Tunnel ingress와 Cache Rules의 순서를 같은 방식으로 이해합니다
Tunnel ingress는 위에서부터 첫 번째로 일치한 규칙을 사용합니다. 반면 Cloudflare Cache Rules는 여러 규칙이 함께 적용될 수 있고 충돌하는 설정은 마지막 일치 규칙이 우선할 수 있습니다. 제품마다 평가 방식이 다르므로 같은 순서 원칙을 모든 Cloudflare 규칙에 일반화하면 안 됩니다.
운영 체크리스트
- 호스트명이 정확한지 확인합니다.
^/docs(/|$),^/blog(/|$),^/faq(/|$)같은 구체적인 경로를 위에 둡니다.*catch-all 규칙은 동일 호스트의 마지막에 둡니다.- 루트, 하위 페이지, sitemap, 정적 자산을 각각 확인합니다.
- WAF와 Cache Rules는 Tunnel ingress와 별도로 검토합니다.
- 변경 직후 외부 공개 주소에서 실제 응답을 확인합니다.
결론
Cloudflare Tunnel의 경로 기반 라우팅은 정규식 자체보다 규칙 순서가 더 중요할 때가 많습니다. /docs, /blog, /faq 같은 구체적인 규칙을 먼저 배치하고 * catch-all을 마지막에 두면 새로운 서비스를 추가할 때 발생하는 조용한 404 문제를 피할 수 있습니다.
정규식은 ^/faq(/|$)처럼 경계를 분명히 하고, 저장 후에는 실제 공개 주소와 하위 자산까지 확인하는 습관이 필요합니다.
참고 자료
함께 읽기
- AWS 서비스 10개를 3개로 줄이면 무엇이 달라질까?Next.js로 웹 서비스를 만들 때 AWS 서비스를 하나씩 고르면 꽤 긴 목록이 나온다. CDN, API 입구, 함수 실행, PostgreSQL, 인증, 파일 저장, Redis, Queue, 이벤트 예약, 배포 파이프라인이 각각 다른 서비스다.
- Vercel vs Cloudflare Pages vs Netlify vs 자체 서버: 배포 플랫폼 선택 가이드웹사이트를 만들 때 자주 듣는 이름으로 Vercel, Cloudflare Pages, Netlify가 있습니다. 여기에 AWS나 직접 관리하는 서버까지 더하면 선택지가 너무 많아 보입니다. 하지만 먼저 구분할 것이 있습니다. 이들은 완성된 홈페이지를 만들어 주는 서비스가 아니라, 개발자가 작성한 코드를 빌드하고 실행하는…
- 태그 485개를 전부 색인시키고 있었습니다: 블로그 SEO를 다시 손본 기록블로그에는 이미 canonical, sitemap, robots.txt, 글별 Open Graph 이미지, 구조화 데이터가 들어가 있었습니다. RSS와 Atom, JSON Feed도 있었고 관련 글 링크도 붙어 있었습니다. 그래서 처음에는 큰 구멍보다는 메타 설명을 조금 다듬는 정도를 예상했습니다.
- 한국에서 접속하는데 Cloudflare가 로스앤젤레스에서 응답했습니다이미지 엣지 캐시를 손보고 나서 얼마나 빨라졌는지 재고 있었습니다. 캐시에서 나오면 156ms, 오리진까지 가면 622ms. 4배 차이니까 좋아하고 있었는데, 156ms라는 숫자가 자꾸 걸렸습니다.
- 엣지 캐시를 마저 켜자 비공개 이미지 문제가 드러났습니다전에 CDN의 파일 경로에 확장자가 없어서 Cloudflare가 캐시하지 않던 문제를 고쳤습니다. 헤더를 아무리 강하게 줘도 CF-Cache-Status가 DYNAMIC으로 나오던 그 이야기입니다.