본문으로 건너뛰기

Field guide / soft-404

soft 404 해결하기: 없는 페이지의 응답과 오류 캐시 함정

존재하지 않는 URL에 200을 돌려주는 soft 404의 정의와 원인, 404와 410의 차이, 일시 장애 응답이 캐시되는 함정, 서버 설정 예시와 애드브랜치 site-404-probe 판정 기준을 정리합니다.

작성
수정
작성자
애드브랜치 편집팀
검토
애드브랜치 편집팀

soft 404란 무엇인가

soft 404란 사용자에게는 “페이지를 찾을 수 없습니다”라고 안내하면서 HTTP 상태 코드는 404가 아닌 값, 대개 200을 돌려주는 응답을 말합니다. 브라우저 화면만 보면 정상적인 오류 안내처럼 보이지만, 크롤러는 상태 코드로 먼저 판단하기 때문에 “여기에 문서가 있다”는 신호를 받습니다. Google은 이 상태를 “찾을 수 없다는 메시지가 있지만 404 코드가 없는 페이지”로 정의하고 Search Console의 페이지 색인 보고서에서 별도 항목으로 보여 줍니다. Search Console 도움말: 페이지 색인 보고서

핵심은 사람이 읽는 문구와 기계가 읽는 상태 코드가 서로 다른 말을 하고 있다는 점입니다. 안내 문구는 “없다”고 말하는데 상태 코드는 “있다”고 말합니다. 검색엔진은 이 모순을 나름의 방법으로 추정해 처리하지만, 추정에 기대는 것보다 처음부터 올바른 상태 코드를 보내는 편이 훨씬 확실합니다.

검색엔진의 추정에 맡기지 말고, 없는 문서에는 404 또는 410을 반환하고 사이트맵과 내부 링크에서도 해당 URL을 정리하세요.

404와 410, 그리고 다른 4xx

“삭제한 페이지는 410을 써야 더 빨리 빠진다”는 말을 자주 듣습니다. Google 공식 문서 기준으로는 그렇지 않습니다. Google은 429를 제외한 모든 4xx 상태 코드를 같은 방식으로 다룬다고 안내합니다. 콘텐츠가 존재하지 않는다는 뜻으로 받아들이고, 이미 색인된 URL이라면 색인에서 제거한다는 것입니다. 404와 410 사이에 처리 속도 차이를 두지 않는다고 명시하고 있습니다. Google 크롤링 문서: HTTP 상태 코드가 색인에 미치는 영향

상태 코드의미Google의 처리
404 Not Found문서가 없음색인하지 않음, 기존 색인은 제거
410 Gone문서가 있었으나 영구히 사라짐404와 동일하게 처리
401·403 등 기타 4xx인증·권한 문제역시 “콘텐츠 없음”으로 처리
429 Too Many Requests요청이 너무 잦음예외. 문서 없음 응답으로 보지 않음

그러니 410으로 바꾸는 작업에 시간을 쓸 이유는 별로 없습니다. 이미 404를 잘 돌려주고 있다면 그대로 두면 됩니다. 검색 결과에서 급하게 지워야 하는 URL이 있다면 상태 코드를 바꾸는 대신 Search Console의 삭제 도구를 쓰는 편이 목적에 맞습니다.

한 가지 주의할 점은 401과 403입니다. 로그인이 필요한 페이지를 크롤러에 403으로 돌려주면 Google 입장에서는 404와 같은 “없음”입니다. 검색에 노출하고 싶은 페이지라면 크롤러가 200으로 받을 수 있게 해야 하고, 노출하고 싶지 않은 페이지라면 403이든 404든 결과는 같습니다.

soft 404가 생기는 흔한 원인

SPA 호스팅의 모든 경로 index.html 200 설정

React, Vue, Svelte 등으로 만든 단일 페이지 애플리케이션(SPA)은 클라이언트 라우팅을 위해 “어떤 경로든 index.html을 돌려주라”는 설정을 자주 씁니다. 이 설정은 /products/123 같은 실제 라우트에는 필요하지만, /asdfgh 같은 없는 경로에도 똑같이 200과 index.html을 돌려줍니다. JavaScript가 실행된 뒤 화면에는 “찾을 수 없음”이 그려지지만 상태 코드는 이미 200으로 나간 뒤입니다. soft 404의 가장 흔한 형태입니다.

없는 글을 홈으로 리디렉션

삭제한 글의 URL을 301이나 302로 홈이나 카테고리 목록으로 보내면 “이 문서가 없다”는 상태를 명시하지 못합니다. 대체할 문서가 정말 있을 때만 그 문서로 301을 보내고, 없다면 404를 돌려주세요.

검색 결과 0건 페이지와 빈 목록

사이트 내 검색 결과 페이지, 필터를 잘못 조합한 목록 페이지, 재고가 모두 빠진 카테고리 페이지처럼 내용이 빈 URL도 상태 정책을 정해야 합니다. 검색 결과 페이지는 색인 대상인지 검토하고, 비어 있는 카테고리는 유지할 실제 목적이 없다면 404를 돌려주거나 목록에서 링크를 빼는 쪽이 낫습니다.

오류 안내 컴포넌트를 200 템플릿 안에서 렌더링

서버 렌더링 프레임워크에서 데이터 조회가 실패했을 때 예외를 던지지 않고 “없음” 컴포넌트를 그리는 코드도 soft 404를 만듭니다. 데이터가 없으면 명시적으로 404 응답을 만들어야 합니다.

// Next.js App Router 예시
import { notFound } from "next/navigation";

export default async function Post({ params }) {
  const post = await getPost(params.slug);
  if (!post) notFound(); // 404 상태 코드로 응답
  return <Article post={post} />;
}
// Astro 예시
const post = await getPost(Astro.params.slug);
if (!post) {
  return new Response(null, { status: 404 });
}

“없음”과 “못 가져옴”은 다르다: 오류 응답 캐시 함정

여기서부터는 벤더 문서에 나오는 내용이 아니라 운영하면서 겪게 되는 문제입니다. 404를 잘 돌려주기로 결정했다면 이번에는 반대 방향의 사고를 조심해야 합니다. 있는 페이지가 일시적으로 404로 나가고, 그 404가 캐시에 굳어 버리는 경우입니다.

전형적인 시나리오는 이렇습니다. 데이터베이스나 CMS API가 잠깐 응답하지 않습니다. 애플리케이션은 “데이터가 없다”와 “데이터를 못 가져왔다”를 구분하지 않고 둘 다 404로 처리합니다. CDN이나 ISR(Incremental Static Regeneration)이 그 404 응답을 정상 응답처럼 캐시합니다. 장애는 몇 초 만에 복구됐지만 캐시된 404는 TTL이 끝날 때까지, 또는 재검증 조건이 맞을 때까지 살아남습니다. 그 사이 크롤러가 방문하면 “이 문서는 없어졌다”는 신호를 받아 갑니다.

이 문제를 피하려면 응답 코드를 원인에 맞게 나눠야 합니다.

상황올바른 응답이유
조회 결과가 정상적으로 “없음”404 또는 410정말 없는 문서. 캐시돼도 문제없음
데이터 소스 연결 실패, 타임아웃502·503·504 등 5xx일시 장애. 크롤러가 재시도할 여지를 남김
상위 서비스가 429를 돌려줌503 (Retry-After 포함)과부하를 알리고 나중에 다시 오게 함
빌드 시점에 데이터가 누락됨빌드 실패 처리잘못된 산출물이 배포되지 않게 함

코드 수준에서는 예외 종류를 구분하는 것부터 시작합니다.

// 잘못된 예: 원인을 가리지 않고 404
try {
  const post = await cms.get(slug);
  if (!post) return notFound();
  return render(post);
} catch {
  return notFound(); // 네트워크 장애도 404로 나간다
}
// 수정된 예: "없음"과 "실패"를 분리
let post;
try {
  post = await cms.get(slug);
} catch (err) {
  return new Response("Upstream unavailable", {
    status: 503,
    headers: { "Retry-After": "60", "Cache-Control": "no-store" },
  });
}
if (!post) return notFound();
return render(post);

캐시 계층에서도 방어선을 하나 더 두세요. 5xx 응답에는 Cache-Control: no-store를 붙이고, CDN의 오류 캐시 기능(대개 “negative caching”이나 “error caching”이라는 이름으로 제공됩니다)이 켜져 있다면 404의 TTL을 짧게 잡습니다. ISR을 쓴다면 재생성 중 실패했을 때 마지막 정상 결과를 유지하는지, 아니면 오류 페이지로 덮어쓰는지 프레임워크 동작을 확인해야 합니다.

정리하면 “없음”은 확신이 있을 때만 말하는 응답입니다. 확신이 없으면 5xx로 “지금은 못 준다”고 말하고, 캐시가 그 응답을 오래 붙들지 않게 하세요.

호스팅별 404 설정 예시

Netlify _redirects

Netlify의 _redirects 파일은 마지막 열에 상태 코드를 적을 수 있습니다. SPA 폴백을 200으로 적은 줄이 있다면 그 줄이 모든 없는 경로를 200으로 만듭니다.

# 잘못된 예: 모든 경로를 index.html 200 으로
/*    /index.html   200
# 수정된 예: 알려진 앱 경로만 200, 나머지는 404 페이지
/app/*        /index.html   200
/*            /404.html     404

정적 사이트라면 폴백 줄 없이 404.html을 배포 루트에 두는 것만으로 404 상태 코드가 나갑니다.

Nginx try_files

Nginx에서 흔히 보는 try_files $uri $uri/ /index.html;은 SPA 폴백입니다. 정적 사이트라면 마지막 인자를 =404로 바꿉니다.

# 잘못된 예: 없는 파일도 index.html 200
location / {
  try_files $uri $uri/ /index.html;
}
# 수정된 예: 없는 파일은 404, 사용자에게는 커스텀 페이지
error_page 404 /404.html;

location / {
  try_files $uri $uri/ =404;
}

location = /404.html {
  internal;
}

error_page 404 /404.html;은 응답 본문만 바꾸고 상태 코드는 404로 유지합니다. 반대로 error_page 404 =200 /404.html;처럼 =200을 붙이면 상태 코드가 200으로 바뀌어 soft 404가 되니 주의하세요.

직접 확인하는 방법

브라우저 개발자 도구의 네트워크 탭에서 없는 주소를 열어 첫 문서 요청의 상태 코드를 봅니다. 터미널이 편하다면 curl -I로 헤더만 받아 볼 수 있습니다.

curl -I https://example.com/this-page-does-not-exist-9f3a
# HTTP/2 404  ← 이렇게 나와야 한다

리디렉션이 끼어 있으면 -L 없이 첫 응답만 보고, 필요하면 -L을 붙여 최종 목적지까지 따라가 봅니다. Google 크롤러는 최대 10회까지 리디렉션을 따라가고, 301과 308을 강한 신호로, 302와 307을 약한 신호로 다룹니다. 그래서 없는 페이지를 여러 단계 리디렉션 끝에 홈으로 보내는 구성은 더 나쁩니다. Google 크롤링 문서: HTTP 상태 코드가 색인에 미치는 영향

Search Console에서는 페이지 색인 보고서의 “소프트 404” 항목에서 Google이 그렇게 분류한 URL 목록을 볼 수 있습니다. 목록에 잡힌 URL은 실제 상태 코드와 본문을 함께 확인해 서버 응답이 의도와 맞는지 점검하세요.

애드브랜치 검사기의 판정 기준

애드브랜치 SEO·GEO 검사기는 이 주제를 세 개의 규칙으로 나눠 봅니다. 모두 technical-seo 카테고리에 속합니다.

site-404-probe (2점)

검사 대상 페이지와 같은 출처(origin)에 /adbranch-404-check- 뒤에 무작위 12자리 16진수를 붙인 경로를 한 번 GET 요청합니다. 실제로 존재할 수 없는 경로이므로 서버가 “없음”을 어떻게 표현하는지 그대로 드러납니다. 리디렉션은 따라가지 않고 첫 응답만 봅니다.

첫 응답판정해석
404 또는 410pass없음을 올바르게 알림
429를 제외한 기타 4xxinfo없음으로 처리되지만 의도한 코드인지 확인 권장
429unknown요청 제한에 걸려 판정 불가
5xxwarning없는 경로가 서버 오류로 나감. 오류 처리 경로 점검 필요
홈 또는 검사 대상 페이지로 가는 3xxwarning없음을 알리지 않는 리디렉션
그 밖의 3xxinfo최종 도착지는 확인하지 않음
2xxwarningsoft 404 가능성

이 규칙은 검사 한 번에 요청을 한 번만 추가합니다. 검사기는 리디렉션 이동을 포함해 한 번의 검사에서 최대 6회까지만 요청합니다. 이 한도 안에서 페이지, robots.txt, 사이트맵, /llms.txt, 404 탐침을 확인하고 페이지는 하나만 읽습니다.

soft-404-signals (2점)

검사 대상 페이지 자체가 200 계열로 응답했을 때만 봅니다. 제목과 본문 앞부분에서 “찾을 수 없”, “존재하지 않”, “not found” 같은 부재 안내 표현을 찾고, 본문 길이가 200자 미만이면 warning을 줍니다. 제목에만 표현이 있고 본문은 충분하면 404를 주제로 다루는 글일 수 있으므로 info로 낮춥니다. 초기 HTML에 본문이 거의 없는 클라이언트 렌더링 페이지는 판정하지 않고 unknown으로 둡니다. 최종 응답이 200 계열이 아니면 이 규칙은 not-applicable이고 상태 코드는 다음 규칙이 맡습니다.

http-status-success (2점)

검사 대상 URL의 최종 응답 상태를 보는 2점 규칙입니다. 다만 실제 검사 API는 입력 페이지가 최종 2xx로 응답해야 점수 계산으로 진행합니다. 입력한 URL이 404·410·5xx 등 비-2xx로 끝나면 422 http-error를 반환하므로 정상 점수표에서 해당 상태 카드의 감점을 읽는 흐름이 아닙니다. 네트워크나 TLS 실패도 별도 오류 응답으로 끝납니다.

반면 site-404-probe는 정상 페이지 검사 중 같은 사이트의 무작위 경로를 따로 요청합니다. 이 보조 요청의 404·410은 기대한 응답이므로 통과입니다. 정상 페이지는 200인데 탐침이 2xx라면 적어도 그 무작위 경로에서 없는 문서의 응답을 확인할 필요가 있습니다. 한 경로의 결과만으로 사이트 전체의 응답 정책을 단정하지는 않습니다. soft-404-signals만 warning이면 입력한 정상 응답의 본문에서 부재 안내 신호를 발견했다는 뜻입니다.

적용 시 주의점

  • 커스텀 404 페이지를 예쁘게 만드는 것과 404 상태 코드를 보내는 것은 별개입니다. 디자인을 바꾼 뒤 상태 코드가 200으로 바뀌지 않았는지 꼭 확인하세요.
  • 사이트맵에서 삭제된 URL을 제거하세요. 사이트맵에 404 URL이 남아 있으면 크롤러가 계속 헛걸음합니다. sitemap.xml 만들기와 제출하기를 참고하세요.
  • 삭제된 글을 대체하는 글이 정말 있을 때만 301을 씁니다. 주제가 다른 글이나 홈으로 보내는 것은 없음을 알리지 않는 응답입니다.
  • 404 페이지 자체에는 noindex가 필요 없습니다. 상태 코드가 이미 색인하지 말라는 뜻입니다. 다만 404 페이지 템플릿에 canonical 태그가 들어가 있다면 빼세요. canonical 태그 완전 가이드에서 이유를 설명합니다.
  • 오류 응답의 캐시 정책을 배포 전 체크리스트에 넣으세요. 5xx는 no-store, 404는 짧은 TTL이 안전한 기본값입니다.

자주 묻는 질문

삭제한 글을 410으로 바꾸면 검색 결과에서 더 빨리 사라지나요

Google 문서 기준으로는 차이가 없습니다. Google은 429를 제외한 4xx를 모두 같은 방식으로 처리하고, 404와 410을 동등하게 다룬다고 안내합니다. 급하게 지워야 한다면 Search Console의 삭제 도구가 목적에 맞는 수단입니다. 410으로 바꾸는 작업이 해가 되는 것은 아니지만, 그 시간에 다른 문제를 고치는 편이 낫습니다.

SPA라서 모든 경로가 200인데 어떻게 해야 하나요

세 가지 선택지가 있습니다. 첫째, 서버 렌더링이나 정적 생성으로 옮겨 없는 경로를 서버에서 판단하게 합니다. 둘째, 호스팅 설정에서 알려진 앱 경로만 index.html 폴백을 적용하고 나머지는 404로 돌려줍니다. 셋째, 당장 구조를 바꿀 수 없다면 없는 경로에서 noindex 메타 태그를 JavaScript로 넣고 사이트맵과 내부 링크에서 없는 URL이 나가지 않게 관리합니다. 세 번째는 임시 조치이고, 애드브랜치 site-404-probe는 여전히 warning을 줍니다.

검사기의 404 탐침 요청이 서버 로그에 남는데 문제없나요

요청 예산이 남아 있으면 /adbranch-404-check- 뒤에 무작위 문자열이 붙은 경로를 검사당 최대 한 번 요청합니다. 그래서 로그에 남을 수 있습니다. 이 접두사만으로 모든 요청이 안전하다고 판단하거나 보안 예외를 자동으로 적용하지 말고, 실제 요청과 응답을 확인하세요.

관련 가이드

Check the evidence

이 페이지를 지금 검사해보기

읽고 있는 페이지나 운영 중인 URL을 넣으면 이 가이드와 연결된 SEO·GEO 항목을 바로 확인할 수 있습니다.

무료 검사 시작

Next reading

관련 문서

SEO 기본 점검 체크리스트

페이지 하나를 검색엔진에 제대로 노출하기 위해 확인할 항목을 우선순위 순서로 정리하고, 항목마다 확인 방법과 고치는 코드까지 함께 다룹니다.

가이드 읽기

관련 문서

canonical 태그 완전 가이드

중복 URL 문제를 해결하는 표준 URL 지정 방법, 자기참조 canonical 규칙, 파라미터와 페이지네이션 처리, 리디렉션·hreflang과의 관계를 예제로 설명합니다.

가이드 읽기

관련 문서

sitemap.xml 만들기와 제출하기

사이트맵의 XML 형식과 URL 개수·용량 제한, sitemap index 분할, robots.txt 선언, Search Console 제출, lastmod의 올바른 사용법을 정리합니다.

가이드 읽기