1인 회사 시스템 build

링크 미리보기가 안 뜨는 세 가지 이유 — 카카오 용량·크롤러 UA·AI 봇 차단

카톡·스레드에 링크를 넣어도 미리보기가 안 뜨던 문제를 og:image 용량, 크롤러 UA, Cloudflare AI 봇 차단 순서로 진단한 기록입니다.


운영하는 사이트의 글 링크를 카톡 방에 공유했는데 미리보기 카드가 안 떴습니다. 제목만 파랗게 뜨고 이미지도 설명도 없는, 그 밋밋한 링크였습니다. 처음엔 og 태그를 빼먹었나 싶어 소스를 열어 봤지만 og:title, og:description, og:image 전부 멀쩡히 박혀 있었습니다. 결국 원인 세 개를 차례로 걷어냈는데, 셋 다 사이트 코드 바깥에 있었습니다. 이미지 용량, 크롤러의 UA, 그리고 방화벽이었습니다. 이번 글은 그 세 가지를 진단한 순서 그대로 정리한 기록입니다.

진단의 원칙 — 눈이 아니라 크롤러의 눈으로 본다

가장 먼저 정한 원칙은 “내 브라우저로 보지 않는다”였습니다. 브라우저로 열면 페이지가 멀쩡히 렌더링되니 아무 단서가 안 나옵니다. 링크 미리보기를 만드는 건 사람이 아니라 각 서비스의 크롤러이고, 크롤러가 받는 HTML은 내가 브라우저에서 보는 것과 다를 수 있습니다. 그래서 크롤러의 UA를 흉내 내 직접 요청하고, 응답 HTML에 og: 태그가 실제로 들어 있는지를 셌습니다.

# 페이스북/스레드 계열 크롤러를 흉내 내 요청하고 og 태그만 추린다
curl -sA "meta-externalagent" https://example.com/posts/hello | grep -i 'og:'

이 명령이 진단 내내 기준점이 됐습니다. og: 라인이 여러 줄 잡히면 서버는 제대로 응답한 것이고, 아무것도 안 잡히면 크롤러가 빈 껍데기를 받고 있다는 뜻이었습니다. 카카오는 페이스북의 셰어링 디버거 같은 별도 도구가 없어서, 카톡 미리보기 문제는 이 curl 확인이 사실상 유일한 검증 방법이었습니다.

첫 번째 — 카카오는 큰 이미지를 조용히 버린다

curl로 확인해 보니 og:image 태그는 응답에 멀쩡히 들어 있었습니다. 태그가 있는데도 카톡에서 이미지가 안 뜬다는 건 카카오가 이미지를 받아 놓고 안 쓴다는 뜻이었습니다. 이미지 URL을 직접 열어 용량을 재 보니 답이 나왔습니다. PNG로 만든 대표 이미지가 1MB를 훌쩍 넘고 있었습니다.

카카오는 og:image가 500KB를 넘으면 미리보기를 아예 띄우지 않습니다. 텍스트가 많은 썸네일을 PNG로 뽑으면 1MB는 우습게 넘어가니, 여기에 걸린 겁니다. 그래서 같은 이미지를 JPEG 품질 90으로 다시 뽑았습니다. 눈으로는 차이를 거의 못 느끼는데 용량은 150~250KB로 내려갔습니다. 500KB 아래로 확실히 들어온 것입니다.

한 가지 더 챙긴 건 파일명입니다. 카카오는 한 번 긁어간 이미지 URL을 캐시해 두는데, 같은 파일명으로 덮어쓰면 예전 캐시를 계속 보여 줄 수 있습니다. 그래서 이미지를 교체할 때 파일명 자체를 바꿔 URL을 다르게 만들었습니다. 새 URL이니 카카오도 새로 긁어갔고, 그제서야 카톡에서 미리보기가 떴습니다.

두 번째 — 크롤러 UA가 바뀌어 있었다

카톡은 해결됐는데 스레드와 인스타그램에서는 여전히 안 떴습니다. 다시 curl로 확인해 보니, 이번엔 카카오 때와 달리 응답 HTML에 og: 태그가 하나도 없었습니다. 크롤러가 og 태그가 담긴 페이지가 아니라 빈 SPA 셸을 받고 있었던 것입니다.

이 사이트는 SPA라서, 일반 방문자에게는 자바스크립트로 내용을 채우는 빈 HTML 셸을 보냅니다. 문제는 크롤러가 자바스크립트를 실행하지 않는다는 점입니다. 그래서 nginx 앞단에서 크롤러의 UA를 알아보면 미리 렌더링해 둔(프리렌더) HTML을 대신 내주는 구조를 씁니다. 이때 “이 UA는 크롤러다”를 판별하는 화이트리스트가 있어야 분기가 됩니다. 목록에 없는 UA는 그냥 일반 방문자로 취급돼 빈 셸을 받게 됩니다.

메타 계열 크롤러의 UA가 그사이 meta-externalagent로 바뀌어 있었고, 우리 화이트리스트에는 예전 이름만 있어 새 UA가 목록 밖으로 새어 나가고 있었습니다. 목록에 한 줄 추가했습니다.

# 봇 UA면 프리렌더 서버로, 아니면 SPA 셸로 보낸다
map $http_user_agent $is_bot {
    default 0;
    "~*facebookexternalhit"  1;
    "~*meta-externalagent"   1;   # 스레드·인스타 크롤러의 새 UA
    "~*kakaotalk-scrap"      1;
    "~*twitterbot"           1;
}

server {
    location / {
        if ($is_bot) {
            proxy_pass http://prerender_upstream;
            break;
        }
        try_files $uri /index.html;   # 일반 방문자는 SPA 셸
    }
}

SPA에서 봇 UA 목록이 왜 필요한지는 여기서 분명해집니다. 콘텐츠가 클라이언트에서 그려지는 구조이기 때문에, 서버가 UA를 보고 “이건 봇이니 완성된 HTML을 줘야 한다”고 판단하는 지점이 반드시 있어야 합니다. 그 판단 근거가 UA 목록이고, 크롤러 이름은 예고 없이 바뀌니 이 목록은 한 번 짜고 끝나는 게 아니라 계속 손봐야 하는 대상이었습니다.

세 번째 — 방화벽이 크롤러를 AI 봇으로 오해했다

UA를 추가했으니 됐겠거니 했는데 여전히 안 떴습니다. 다시 curl -A "meta-externalagent"로 때려 보니 og 태그는커녕 HTML 본문 자체가 안 오고 403이 돌아왔습니다. 요청이 nginx까지 닿지도 못하고 있었던 것입니다. 앞단의 Cloudflare가 막고 있었습니다.

원인은 Cloudflare의 “Block AI bots” 기능이었습니다. 이 기능이 meta-externalagent를 AI 학습용 크롤러로 분류해 403을 돌려주고 있었습니다. 링크 미리보기용 크롤러와 AI 수집용 크롤러를 UA만으로 딱 잘라 구분하기 어렵다 보니 같이 걸린 셈입니다.

깔끔한 해법은 이 크롤러만 방화벽을 건너뛰게 하는 WAF Skip 규칙을 만드는 것입니다. 그런데 무료 플랜에서는 WAF Skip 규칙을 만들 수 없었습니다. 결국 대시보드에서 Block AI bots의 차단 범위를 “Do not block”으로 내리는 수밖에 없었습니다. 특정 봇만 예외 처리하는 대신 이 방어를 통째로 낮추는 방식이라 아쉬웠지만, 무료 플랜에서 고를 수 있는 카드가 그것뿐이었습니다. 설정을 내리고 나서야 curl 응답에 og 태그가 다시 잡혔고, 스레드에서도 미리보기가 떴습니다.

정리 — 용량, UA, 방화벽 순서로 훑는다

미리보기가 안 뜰 때 원인이 세 층에 걸쳐 있다는 게 이번에 얻은 지도입니다. 진단 순서는 안쪽부터 바깥쪽으로 잡는 게 좋았습니다. 먼저 curl로 og 태그가 응답에 들어 있는지 봅니다. 태그가 있는데 이미지만 안 뜨면 카카오 용량(500KB) 문제일 가능성이 큽니다. 태그 자체가 없으면 크롤러가 빈 셸을 받는 것이니 nginx의 봇 UA 목록을 의심합니다. HTML은커녕 403이 오면 그건 서버가 아니라 앞단 방화벽입니다.

세 원인 모두 애플리케이션 코드에는 손을 대지 않았다는 점이 인상에 남습니다. og 태그는 처음부터 멀쩡했고, 문제는 이미지 인코딩 설정, nginx의 UA 한 줄, Cloudflare 대시보드의 토글에 흩어져 있었습니다. 링크 미리보기가 안 뜰 때 소스 코드부터 뒤지지 않고, 크롤러의 눈으로 요청을 재현해 어느 층에서 막히는지부터 보는 것 — 그 한 가지가 이 문제에서 가장 빨랐던 길이었습니다.

자주 묻는 질문

카카오톡 링크 미리보기 이미지가 안 뜨는 이유는 뭔가요?

og:image 파일이 500KB를 넘으면 카카오는 미리보기를 아예 띄우지 않습니다. PNG로 만들면 1MB를 쉽게 넘기니 JPEG 품질 90으로 150~250KB까지 낮추면 뜹니다. 파일명을 함께 바꾸면 카카오의 캐시도 비껴갈 수 있습니다.

스레드·인스타그램 크롤러의 User-Agent는 무엇인가요?

메타 계열 크롤러의 UA가 meta-externalagent로 바뀌었습니다. nginx에서 봇 UA 화이트리스트로 프리렌더를 분기한다면 이 이름을 목록에 추가해야 SPA 셸 대신 og 태그가 담긴 HTML을 응답합니다.

meta-externalagent가 UA 목록에 있는데도 미리보기가 안 뜨면요?

Cloudflare의 Block AI bots 기능이 meta-externalagent를 AI 크롤러로 분류해 403을 돌려줄 수 있습니다. 무료 플랜은 WAF Skip 규칙을 만들 수 없어 대시보드에서 차단 범위를 Do not block으로 내려야 통과합니다.

미리보기가 뜨는지 어떻게 확인하나요?

curl -A로 크롤러 UA를 붙여 직접 요청하고 응답 HTML에 og: 태그가 들어 있는지 확인합니다. 카카오는 별도 디버거가 없어 이 방식이 사실상 유일한 확인 수단입니다.

#og:image#카카오#nginx#크롤러#Cloudflare