블로그 관리자 페이지를 만들면서 로그인 기능을 붙여야했다. 처음에는 구글 OTP 로그인을 붙이려고 했으나 블로그 도메인이 Cloudflare 기반으로 동작하는 점에 착안하여 Cloudflare Access로 해결하기로 했다. Cloudflare Access를 걸어서 관리자가 브라우저로 직접 접속할 때만 로그인 화면을 거치게 만드는 방식으로 구축했는데, 이 설정 하나가 전혀 예상 못 한 곳에서 자동화 파이프라인 전체를 조용히 멈춰 세웠다.

Cloudflare Access는 경로 단위로 보호를 건다. 관리자 페이지 전체를 Cloudflare Access 로그인이 필요하도록 설정하면, 그 경로로 들어오는 모든 요청이 예외 없이 이 검사를 통과해야 한다 — 브라우저든, curl이든, 서버 간 자동화 스크립트든 구분하지 않는다. 문제는 코인 분석과 미국 증시 마감 시황 브리핑, 이 두 자동화 파이프라인이 새 글을 올릴 때 정확히 이 관리자 경로로 POST 요청을 보내고 있었다는 것이다.

증상은 겉으로 보기엔 평범한 실패였다. 자동화 스크립트가 실행되고 정상 종료된 것처럼 보이는데, 정작 블로그에는 새 글이 올라오지 않는 경우가 반복됐다. 로그를 들여다보니 원인이 나왔다 — 자동화 스크립트가 보낸 POST 요청이 실제로는 302 리다이렉트로 Cloudflare의 로그인 페이지로 튕겨 나가고 있었다. 글 작성 API가 실패한 게 아니라, 그 API에 도달하기도 전에 Access가 먼저 막아선 것이다.

Access 정책을 자동화 스크립트만 예외로 빼는 방법도 있었지만, 서비스 토큰 발급과 관리가 늘어나는 방식이라 더 단순한 길을 택했다. 같은 백엔드를 가리키되 Cloudflare Access가 걸리지 않는 경로를 하나 더 여는 것이다. Caddy 설정에 별도의 새 handle_path 블록을 추가하고, 이걸 관리자 페이지와 동일하게 관리자 API 백엔드 즈소로 그대로 리버스 프록시했다. 관리자가 직접 로그인해서 쓰는 관리자 페이지는 Access로 보호된 채 남겨두고, 서버 간 자동화는 handle_path 블록으로 우회시키는 구조다. 인증 자체가 없어지는 건 아니다 — 이 경로도 여전히 백엔드 자체의 비밀 토큰 검사를 통과해야 글이 올라간다. 다만 그 검사 앞에 관리자 전용 로그인 절차가 끼어들지 않을 뿐이다.

설정을 바꾼 뒤에는 caddy validate로 문법을 먼저 확인하고, caddy reload로 무중단 반영한 다음, 실제로 두 경로를 모두 curl로 찔러 확인했다. 새로 만든 포스팅 작성 경로는 정상적으로 글을 생성했고, 관리자 페이지의 포스팅 작성경로는 여전히 로그인 없는 접속에 302를 돌려줬다 — 의도한 대로 두 경로가 서로 다른 손님(?) 받고 있는 셈이다.

이 경험에서 남은 교훈은, 보안 설정 하나를 추가할 때 “누가 이 경로를 실제로 쓰고 있는가"를 사람뿐 아니라 이미 돌고 있는 자동화까지 포함해서 다시 점검해야 한다는 것이었다. 관리자 직접 로그인을 위해 넣은 안전장치가, 그 경로를 공유하던 다른 프로세스를 함께 막아버릴 수 있다.