웹사이트의 정적 리소스나 콘텐츠를 수정했음에도 사용자의 브라우저에는 계속 옛날 버전이 노출되어 난감했던 경험이 있을 것이다.
다만 Cloudflare CDN을 사용하는 환경에서 관리자 페이지 내 콘텐츠 업데이트 기능과 연동하여 캐시를 자동으로 지우는 방법을 몰라, 매번 Cloudflare 대시보드에 로그인하여 수동으로 버튼을 누르는 경우가 많다.
이번에는 Cloudflare API v4가 정확히 무엇이고 왜 필요한지, 그리고 PHP cURL을 이용해 특정 파일의 캐시를 안전하게 자동 퍼지(Purge)하는 방법까지 완벽하게 정리해서 소개하겠다.
Cloudflare는 세계적인 CDN(Content Delivery Network) 서비스로, 웹서버 전면에 위치하여 이미지, CSS, JS 등의 정적 파일과 HTML 응답을 엣지 서버에 캐싱해 원본 서버의 부하를 줄여준다.
그러나 웹사이트 관리자 페이지에서 게시글을 수정하거나 메인 배포를 진행할 때 CDN 캐싱이 유지되고 있으면 최신 변경 사항이 사용자에게 즉시 반영되지 않는 문제가 발생한다.
Cloudflare API v4를 이용하면 관리자 시스템에서 게시물이 수정되거나 배포 스크립트가 실행되는 시점에 특정 URL의 캐시만 핀포인트로 삭제(Purge)할 수 있다.
| 구분 | 대시보드 수동 퍼지 | API v4 자동 퍼지 |
|---|---|---|
| 작업 방식 | Cloudflare 웹 콘솔 접속 후 클릭 | PHP 백엔드 코드에서 HTTP 요청으로 즉시 실행 |
| 작업 범위 | 전체 캐시 삭제(Purge Everything) 중심 | 수정된 특정 URL/파일만 선택적 삭제 가능 |
| 서버 영향도 | 전체 캐시 삭제 시 원본 서버 순간 부하 급증 | 해당 파일만 재요청하므로 서버 부담 최소화 |
| 자동화 가능 여부 | 불가능 (사람의 수동 개입 필요) | CMS, CI/CD 배포 파이프라인 연동 완전 자동화 |
API 연동을 진행하기 전, 요청을 승인할 인증 토큰과 대상 도메인을 식별할 Zone ID가 필요하다.
이전의 계정 전체 권한을 가졌던 Global API Key 방식은 보안상 매우 위험하므로, 최소 권한만 부여하는 API Token 방식을 사용하는 것이 실무 표준이다.
- Zone ID 확인: Cloudflare 대시보드 접속 > 해당 도메인 선택 > [개요(Overview)] 탭 우측 하단에서 'Zone ID' 문자열을 복사한다.
- API Token 생성: 우측 상단 프로필 > [내 프로필] > [API 토큰] > [토큰 만들기] 선택.
- 권한 설정: 권한 목록에서
Zone - Cache Purge - Purge권한을 부여하고, 대상 리소스 영역에 작업할 도메인을 지정한다.
이제 PHP 환경에서 Cloudflare API v4 엔드포인트(https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache)로 POST 요청을 보내는 코드를 작성해본다.
✗ 대부분의 개발자들은 권한 범위를 생략하고 Global Key를 쓰거나, 편하다는 이유로 도메인 전체 캐시를 날려버리는 우를 범한다.
<?php
// ✗ 잘못된 코드: 보안에 취약한 Global Key 사용 및 전체 캐시 일괄 삭제
$zoneId = "your_zone_id_here";
$userEmail = "admin@example.com";
$globalApiKey = "1234567890abcdef1234567890abcdef";
$ch = curl_init("https://api.cloudflare.com/client/v4/zones/{$zoneId}/purge_cache");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-Auth-Email: {$userEmail}",
"X-Auth-Key: {$globalApiKey}",
"Content-Type: application/json"
]);
// purge_everything 사용 시 트래픽 급증으로 오리진 서버가 다운될 위험이 있음
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["purge_everything" => true]));
$response = curl_exec($ch);
curl_close($ch);
?>
✓ 보안성이 강화된 API Bearer 토큰을 사용하고, 변경된 파일의 Absolute URL만 배열로 전달하여 타겟 퍼지를 수행하는 정석 함수다.
<?php
/**
* Cloudflare 특정 URL 캐시 퍼지 서포트 함수
*
* @param string $zoneId Cloudflare Zone ID
* @param string $apiToken Cache Purge 권한을 가진 API Token
* @param array $urls 캐시를 삭제할 절대 URL 목록
* @return array 처리 결과 [success => bool, message => string]
*/
function purgeCloudflareCache(string $zoneId, string $apiToken, array $urls): array {
if (empty($urls)) {
return ['success' => false, 'message' => '삭제할 URL이 지정되지 않았습니다.'];
}
$endpoint = "https://api.cloudflare.com/client/v4/zones/{$zoneId}/purge_cache";
$payload = json_encode([
'files' => array_values($urls)
]);
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$apiToken}",
"Content-Type: application/json"
]);
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($curlError) {
return ['success' => false, 'message' => "cURL Error: {$curlError}"];
}
$result = json_decode($response, true);
if ($httpCode === 200 && isset($result['success']) && $result['success'] === true) {
return ['success' => true, 'message' => '캐시 퍼지 성공'];
}
$errorMsg = $result['errors'][0]['message'] ?? '알 수 없는 오류가 발생했습니다.';
return ['success' => false, 'message' => "API Error ({$httpCode}): {$errorMsg}"];
}
// --- 실전 사용 예시 ---
$zoneId = 'c1234567890abcdef1234567890abcdef';
$apiToken = 'v1.0-abc123xyz890-your-api-token-here';
// 수정된 이미지 및 페이지 URL만 지정
$targetUrls = [
'https://example.com/assets/css/style.css',
'https://example.com/uploads/banner.jpg'
];
$res = purgeCloudflareCache($zoneId, $apiToken, $targetUrls);
if ($res['success']) {
echo "✓ 지정한 URL의 캐시가 성공적으로 삭제되었습니다.";
} else {
echo "✗ 캐시 퍼지 실패: " . $res['message'];
}
?>
# 성공 응답 시 출력
✓ 지정한 URL의 캐시가 성공적으로 삭제되었습니다.
# Cloudflare API 응답 원본 데이터 (JSON)
{
"result": {
"id": "0123456789abcdef0123456789abcdef"
},
"success": true,
"errors": [],
"messages": []
}
실무에서 Cloudflare API를 연동할 때 빈번하게 부딪히는 문제들과 올바른 해결 방법이다.
- URL 스키마 및 도메인 오타:
http://와https://는 Cloudflare에서 서로 다른 캐시 키로 인식된다. 반드시 사이트에서 사용하는 실제 프로토콜과 완벽히 일치하는 풀 경로(Full URL)를 전달해야 한다. - HTTP 403 Forbidden 에러: 발급받은 API 토큰의 권한 범위를 확인해야 한다. 'Zone Read'만 부여된 경우 퍼지 요청 시 403 권한 거부 에러가 발생하므로
Zone - Cache Purge - Purge권한이 올바르게 들어가 있는지 체크하자. - 무분별한 Purge Everything 사용 금지:
purge_everything속성을 참(true)으로 보낼 경우 CDN의 모든 캐시가 날아가 원본 데이터베이스와 백엔드 서버에 순간적으로 수만 건의 요청이 몰려 장애를 유발할 수 있다. 특별한 배포 시점이 아니라면 반드시files옵션을 통한 개별 URL 삭제 방식을 채택해야 한다.
Cloudflare API v4 연동을 통한 자동 캐시 퍼지는 배포 자동화 및 관리자 시스템 운영 효율성의 핵심이다.
수동 작업을 줄여주는 작은 자동화 습관이 모여서 개발자의 피로도를 대폭 줄이고 서비스의 데이터 신뢰도를 높이는 큰 효과를 만든다는 점을 잊지 말자.
이 글의 PHP cURL 실전 연동 코드를 참고해 사용 중인 CMS나 관리자 기능에 캐시 퍼지 로직을 도입하면, 사용자 단의 캐시 불일치 문제를 깔끔하게 해결할 수 있을 것이다.