브라우저 캐시를 지우고 새로고침을 눌렀는데도 favicon이 여전히 하얀 빈 칸으로 보인다면, 브라우저 탓을 하기 전에 서버 쪽을 의심해봐야 합니다. 캐시를 삭제해도 favicon이 갱신되지 않는 문제는 생각보다 흔한 케이스입니다.
지난주에 모 서비스의 스테이징 환경에서 이 문제 때문에 세 시간을 날렸습니다. Chrome의 favicon 데이터베이스를 직접 삭제하고, 브라우저 재시작도 해봤지만 아무 변화가 없었습니다. 원인은 브라우저 캐시가 아니었습니다.
이 글에서는 가장 빠르게 해결할 수 있는 방법과, 앞으로 같은 문제가 재발하지 않도록 체크해야 할 근본적인 원인들을 정리해보겠습니다.
가장 빠른 해결책: 브라우저 캐시 우회하기
브라우저가 새 favicon을 가져오지 않는다면, 강제로 새로운 요청을 보내도록 만들어야 합니다. favicon 경로에 query string을 추가하면 됩니다. 브라우저는 이를 완전히 새로운 파일로 인식해서 다시 다운로드합니다.
HTML의 <link> 태그에 version parameter를 추가해보세요.
<link rel='icon' href='/favicon.ico?v=20260115' type='image/x-icon'>favicon을 교체할 때마다 이 version 숫자를 바꿔주면 됩니다. 사용자에게 캐시 삭제를 요청하지 않고도 강제 갱신할 수 있는 가장 확실한 방법입니다. 장기적인 관리 전략이 궁금하다면 favicon cache busting best practices 가이드를 참고해보세요.
캐시 삭제가 통하지 않는 이유
브라우저는 생각보다 고집이 섭니다. 일반적으로 캐시를 삭제할 때 지워지는 것은 HTTP cache뿐입니다. favicon은 표준 캐시 삭제로는 닿지 않는 별도의 영구적인 데이터베이스에 저장되는 경우가 많습니다.
숨겨진 SQLite Favicon Database
Chrome과 Firefox는 favicon 전용 SQLite database를 따로 관리합니다. 이 데이터베이스는 브라우저 history 및 bookmark와 연결되어 있어서, 일반적인 캐시 삭제로는 지워지지 않습니다. 브라우저가 favicon URL이 바뀌지 않았다고 판단하면 새로 fetch를 시도하지 않습니다.
이것이 브라우저 데이터를 싹 다 지웠는데도 favicon이 옛날 것으로 굳어있는 이유입니다. 브라우저는 하드코딩된 URL을 보고 SQLite DB를 확인한 뒤, 매칭되는 항목을 찾아 낡은 파일을 그대로 보여줍니다.
Service Worker Interception
PWA를 개발 중이라면, service worker가 Cache API에서 옛날 favicon을 직접 내려주고 있을 수 있습니다. 브라우저 캐시를 삭제해도 service worker cache에는 아무 영향이 없습니다.
이 경우 DevTools에서 service worker를 직접 unregister 해야 합니다. (Application > Service Workers > Unregister) 또는 service worker 파일을 수정해서 기존에 캐싱된 favicon entry를 삭제하도록 업데이트해야 합니다.
근본 원인: 서버에서 점검할 것들
query string 트릭을 써도 해결되지 않는다면, 서버가 요청을 차단하거나 잘못 라우팅하고 있는 상황입니다. 실무에서 가장 자주 마주치는 서버 측 문제 세 가지를 소개합니다.
1. 과도한 CDN Edge Caching
CDN에서 오래된 파일을 글로벌 사용자에게 계속 내려주고 있을 수 있습니다. Cloudflare나 Vercel 같은 CDN은 favicon.ico 같은 정적 자원을 기본적으로 몇 달 단위로 캐싱합니다.
CDN 대시보드에 접속해서 favicon 파일의 캐시를 수동으로 purge 해야 합니다. 또는 icon 파일에 대해 더 짧은 TTL을 설정하는 것도 방법입니다.
2. MIME Type 선언 누락
서버가 파일의 종류를 브라우저에게 알려주지 않으면, 최신 브라우저는 해당 파일을 거부합니다. Nginx나 Apache 환경에서 .ico 파일이 application/octet-stream으로 서빙되는 경우가 있는데, Chrome은 이를 조용히 무시해버립니다.
Nginx 설정에 올바른 MIME type이 포함되어 있는지 확인하세요.
types {
image/x-icon ico;
image/svg+xml svg;
}3. 엄격한 CORS 정책
favicon을 서브도메인이나 별도의 정적 자원 버킷에 호스팅하는 경우, CORS 정책이 이를 차단할 수 있습니다. response에 적절한 Access-Control-Allow-Origin header가 없으면 브라우저는 favicon을 렌더링하지 않습니다.
GitHub가 이 부분을 모범적으로 처리하고 있습니다. SVG favicon을 별도의 CDN 도메인에 호스팅하면서도, CORS header를 통해 메인 도메인이 이를 fetch 할 수 있도록 허용해두었습니다. 대형 서비스가 이 문제를 어떻게 다루는지 확인하고 싶다면, GitHub의 tab icon에 대한 network request를 직접 inspect 해보세요. 정교한 header 설정으로 icon이 보안 경고 없이 즉시 로드되도록 보장하고 있습니다.
2026년에는 이런 삽질을 예방하자
수동으로 캐시를 지우는 방식에 의존하는 건 그만두세요. 이건 끝없는 싸움입니다. 낡은 favicon이 서빙될 가능성 자체를 원천 차단하는 배포 전략이 필요합니다.
- 항상 파일에 version을 붙이세요: 파일명을
favicon-20260115.ico로 바꾸거나, 앞서 보여드린 query string 방식을 사용하세요. - web manifest도 함께 업데이트하세요:
manifest.json을 사용 중이라면, 거기 있는 icon path의 version도 함께 변경해야 합니다. 브라우저는 HTML과 별개로 manifest를 독립적으로 읽습니다. - 배포 시 CDN 캐시를 purge 하세요: CI/CD pipeline에 빌드 성공 후 favicon 캐시를 자동으로 purge 하는 단계를 추가하세요.
배포 전에 설정이 제대로 되었는지 검증하는 것도 잊지 마세요. Mzu favicondl 같은 도구를 사용하면 서버가 올바른 header와 file type을 반환하는지 쉽게 확인할 수 있습니다.
추측은 멈추고 검증하자
캐시를 삭제해도 favicon이 보이지 않는 문제는 브라우저가 고장 났다는 착각 때문에 더욱 답답하게 느껴집니다. 하지만 대부분의 경우, 브라우저는 서버나 자체 SQLite database의 오래된 지시사항을 충실하게 따라가고 있을 뿐입니다.
query string을 추가해 강제 다운로드를 유도하고, service worker를 점검하고, CDN과 MIME type을 확인해보세요. icon 누락 문제에 대한 더 폭넓은 트러블슈팅이 필요하다면 favicon not showing 가이드를 읽어보시길 권합니다.
여러 플랫폼에서 icon 설정이 제대로 되었는지 추측 없이 확인하고 싶다면, 여러분의 URL을 Mzu favicondl에 입력해보세요. 사용자가 실제로 보는 화면을 정확하게 보여드립니다.