dev.to

Two Cloudflare Pages behaviours I had to measure to believe

직접 측정해야 믿을 수 있었던 Cloudflare Pages의 두 가지 동작

Cloudflare Pages에서 정적 사이트를 운영하는 개발자가 플랫폼이 조용히 실패하는 두 가지 동작을 실측으로 검증한 기록입니다. _headers로 지정한 Cache-Control이 정적 자산에서 무시되는 문제와, _redirects 규칙이 약 100줄을 넘으면 경고 없이 적용되지 않는 한계를 다룹니다. 콘텐츠 해시 기반 캐시 무효화와 canary 규칙을 활용한 배포 검증 같은 실용적인 우회 방법을 제시합니다.

본문

저자는 Cloudflare Pages 위에서 약 4,700개의 프리렌더링된 HTML 파일과 작은 React 셀로 구성된 정적 브라우저 게임 사이트를 운영하고 있습니다. 서버 없이 동작하는 이 사이트에서 플랫폼이 큰 소리로 실패하지 않아 며칠을 소비한 두 가지 문제를, 의견이 아닌 측정값과 함께 정리했습니다.

_headers가 적용되지만 Cache-Control은 덮어쓰인다

첫 번째 증상은 배포 직후 한 페이지는 카탈로그에 게임 211개가 있다고 표시하는데, 같은 브라우저의 같은 순간 다른 라우트는 191개라고 표시한 것입니다. 프리렌더링된 HTML은 새 버전이었지만, 앱 셀이 읽는 JavaScript 데이터 파일은 네 시간 전 것이었습니다. 저자는 /data.js에 Cache-Control: public, max-age=0, must-revalidate 규칙을 _headers 파일에 추가하고 배포했지만, 응답은 여전히 cache-control: public, max-age=14400, must-revalidate를 반환했습니다. 흥미로운 점은 _headers 자체는 분명히 적용되고 있다는 사실입니다. catch-all 규칙으로 설정한 X-Content-Type-Options 헤더가 같은 응답에 그대로 붙어 있었기 때문입니다. 즉 파일은 읽히고 경로도 매칭되지만, 정적 자산에 대해서는 이 헤더 하나만 플랫폼이 덮어쓰는 것입니다. HTML은 예외로, Pages가 이미 max-age=0, must-revalidate로 서빙합니다. 플랫폼이 덮어쓸 수 없는 해법은 결국 전통적인 방식, URL에 콘텐츠 해시를 넣는 것이었습니다. 저자의 빌드는 이제 data.js, 컴파일된 번들, 스타일시트를 해시해서 index.html에 ?v=<hash> 형태로 기록합니다. 한 가지 순서 함정이 있는데, 해싱 단계는 반드시 번들러 이후에 실행해야 합니다. 그렇지 않으면 이전 빌드의 번들을 해시해서 새 버전처럼 보이지만 오래된 콘텐츠를 가리키는 URL을 배포하게 됩니다.

_redirects의 조용한 규칙 수 한계

문서상 한도는 정적 리다이렉트 2,000개, 동적 리다이렉트 100개입니다. 그런데 프로덕션에서는 파일의 대략 104번째 줄 이후의 모든 규칙이 동작하지 않았습니다. 빌드 오류도 경고도 없이 규칙이 그냥 발화하지 않았습니다. 저자는 처음에 크기 한계를 의심해 주석을 지우고 규칙을 병합해 파일을 15.3KB에서 6.4KB로 줄였지만, 경계는 같은 규칙에 머물렀습니다. 즉 바이트 한계가 아니라 개수 한계이며, 적어도 저자처럼 정적 규칙과 스플랫(splat) 규칙을 섞어 쓰는 파일에서는 그렇습니다. 이에 대한 대응으로 저자는 세 가지를 실천합니다. 파일을 100개 규칙 이하로 유지하고, 중요도 순으로 정렬해 핵심 301을 앞에 두고 덜 중요한 것을 뒤에 둬서 한계에 걸리더라도 덜 중요한 규칙이 죽게 하며, 마지막 규칙을 canary로 삼아 배포 후 curl -sI 한 번으로 전체 파일이 살아 있는지 확인합니다. 마지막 규칙이 301을 반환하면 그 위의 모든 규칙이 한계 안에 있다는 뜻이므로, 가장 저렴한 모니터링이라고 설명합니다.

삭제된 페이지가 200을 반환하는 캐시 문제

약 1,100개 페이지를 삭제한 뒤 삭제된 URL에 일반 요청을내면 여전히 200과 이전 본문, 그리고 Cache-Control: public, s-maxage=604800이라는 7일짜리 공유 캐시가 반환됐습니다. 같은 URL에 ?cb=8471 같은 임의 쿼리 문자열을 붙이면 같은 순간 404가 나오는데, 쿼리 문자열이 캐시 키를 바꿔 오래된 항목을 빗나가기 때문입니다. 따라서 Pages에서 페이지를 삭제할 때는 캐시 버스터로 검증해야 합니다. 일반 요청은 잔재를 보여줘서 배포가 실패했다고 착각하게 만들며, 캐시는 저절로 비워지고 서두르려면 캐시 퍼지가 유일한 방법입니다.

로컬 검증 방법

이런 정적 사이트에는 테스트 스위트가 없으므로, 저자는 Cloudflare와 같은 방식(파일이 있으면 그 파일을, 없으면 404.html을 실제 404와 함께)으로 빌드 결과물을 로컬에서 서빙하고 리다이렉트를 끈 채 Playwright로 전수 조사합니다. 리다이렉트를 따라가면 최종 200만 보고돼 깨진 사이트맵 항목과 잘못된 canonical을 숨기기 때문입니다. 측정은 2026년 8월 360playzone.com에서 이뤄졌으며 이후 변하지 않았다고 밝히고, 규칙 한계가 ~100이 아닌 다른 지점에서 발동한 사례를 본 사람이 있으면 비교하고 싶다고 덧붙였습니다.

원문: dev.to / 번역·요약: Trawling