synology / self-hosting / security / performance

웹 운영

Cloudflare 이메일 난독화 때문에 /cdn-cgi/l/email-protection이 404로 잡힐 때

Cloudflare가 mailto 링크를 /cdn-cgi/l/email-protection으로 바꿔 링크 점검에 404가 잡힌 원인과, 세 가지 해결 방법 중 email_off 주석으로 두 링크만 뺀 과정입니다.

사이트 내부 링크를 전부 따라가 보는 점검을 돌렸더니, 콘텐츠 페이지는 다 정상인데 /cdn-cgi/l/email-protection이라는 처음 보는 주소가 404로 잡혔습니다. 제가 만든 적 없는 경로입니다.

범인은 Cloudflare의 이메일 주소 난독화(Email Address Obfuscation)였습니다. 당시 rapsso.com은 Cloudflare 프록시를 거치고 있었고, Cloudflare가 HTML 안의 mailto: 링크를 이 경로로 바꿔 끼우고 있었습니다.

브라우저로 보면 멀쩡해서 더 헷갈립니다

연락처 페이지를 브라우저로 열면 이메일 주소가 정상적으로 보이고 눌러도 메일 앱이 열립니다. 그래서 눈으로만 보면 문제가 없습니다.

그런데 서버가 내려준 HTML 원본을 받아서 링크를 따라가는 크롤러 입장에서는 이야기가 다릅니다. href에 적힌 건 mailto:가 아니라 /cdn-cgi/l/email-protection…이고, 그 주소를 요청하면 404가 나옵니다. 제 점검 스크립트에는 이게 "내부 링크 깨짐"으로 집계됐습니다.

원본 HTML을 보고 싶으면 브라우저 개발자 도구 말고 curl로 받아 보는 게 확실합니다. 개발자 도구의 Elements 탭은 스크립트가 돌고 난 뒤 모습이라 원래 주소처럼 보일 수 있습니다.

curl -s https://example.com/contact/ | grep -o 'href="[^"]*email-protection[^"]*"' 

이 명령에 뭔가 걸리면 난독화가 적용된 상태입니다.

고칠 수 있는 방법은 세 가지였습니다

Cloudflare 공식 문서를 보면 난독화를 피하는 방법이 여러 가지 나옵니다. 제 상황에 맞춰 정리하면 이렇습니다.

Cloudflare 이메일 난독화를 피하는 방법 비교
방법적용 범위제 판단
대시보드 Scrape Shield에서 기능 끄기도메인 전체본문에 적힌 다른 이메일까지 스팸 수집에 노출됩니다.
응답에 Cache-Control: no-transform 헤더그 헤더가 붙은 페이지 전체캐시 정책까지 같이 바뀌어서 이메일 하나 때문에 쓰기엔 큽니다.
<!--email_off--> 주석으로 감싸기감싼 부분만링크 두 개만 빼면 되니 이걸 골랐습니다.

문서에는 이 밖에도 <script>, <noscript>, <textarea>, <head> 안의 이메일이나 HTML이 아닌 응답은 애초에 변환하지 않는다고 적혀 있습니다. 반대로 말하면 <a href="mailto:…">는 기본으로 변환 대상입니다.

실제로 바꾼 코드는 두 줄입니다

이 사이트는 PHP 파일 하나가 모든 페이지를 그리는 구조라, 연락처와 개인정보처리방침에 있는 mailto 링크 두 곳만 주석으로 감쌌습니다. 2026년 8월 19일 커밋입니다.

- <p><a href="mailto:<?= e(CONTACT_EMAIL) ?>"><?= e(CONTACT_EMAIL) ?></a></p>
+ <p><!--email_off--><a href="mailto:<?= e(CONTACT_EMAIL) ?>"><?= e(CONTACT_EMAIL) ?></a><!--/email_off--></p>

닫는 주석은 <!--/email_off-->입니다. 슬래시 위치를 틀리면 그냥 평범한 주석이 돼서 아무 효과가 없으니 복사해서 쓰는 게 안전합니다.

배포 뒤 확인한 것

  • 공개 페이지 HTML에서 /cdn-cgi/l/email-protection 문자열이 사라졌고, mailto: 링크는 그대로 남았습니다.
  • 홈에서 시작한 내부 HTML 링크 10개를 다시 따라갔고, 10개 모두 정상 응답해 깨진 링크는 0개였습니다.
  • 일반 브라우저, Googlebot, Mediapartners-Google 사용자 에이전트로 홈을 요청해 셋 다 HTTP 200을 받았습니다.

덧붙이면, rapsso.com은 9월 22일부터 Cloudflare 프록시를 끄고 DNS만 쓰고 있어서 지금은 응답 헤더가 Server: nginx로 나오고 난독화 자체가 걸리지 않습니다. 그래도 주석은 지우지 않았습니다. 나중에 프록시를 다시 켜면 같은 404가 또 생기기 때문입니다.

링크 점검에서 모르는 경로가 404로 잡히면, 그 경로를 내가 만들었는지부터 확인하는 게 빠릅니다. /cdn-cgi/로 시작하면 Cloudflare가 끼워 넣은 겁니다.

확인한 공식 자료

구현 과정에서 판단 기준을 교차 확인한 공식 문서입니다. 글의 사례와 결론은 운영자가 직접 겪은 작업을 바탕으로 작성했습니다.