vercel.app 서브도메인으로 배포한 포트폴리오 사이트에서 Lighthouse SEO 점수가 100점에서 63점으로 급락했다. 코드를 아무리 뜯어봐도 문제가 없는데, 원인은 의외로 단순했다.
포트폴리오 사이트를 Vercel에 배포한 후 Lighthouse로 성능을 측정했다.
SEO 항목을 클릭해서 펼쳐보니 이런 오류가 표시됐다.
크롤링 및 색인 생성
▲ 페이지의 색인 생성이 차단됨
Blocking Directive Source
x-robots-tag: noindex
meta 태그도 문제없고, sitemap도 있고, 코드 상에서 noindex를 설정한 적이 없는데 왜 이런 헤더가 붙는 걸까?
x-robots-tag는 HTTP 응답 헤더로, 검색엔진 크롤러에게 해당 페이지를 어떻게 처리할지 지시하는 역할을 한다.
x-robots-tag: noindex
이 헤더가 응답에 포함되면 Google, Bing 등 검색엔진은 해당 페이지를 크롤링은 하지만 검색 결과에는 포함시키지 않는다.
HTML에서 사용하는 아래 메타 태그와 동일한 효과를 HTTP 헤더 레벨에서 발휘한다.
<meta name="robots" content="noindex" />
차이점은 메타 태그는 HTML 파싱 후 적용되지만, HTTP 헤더는 페이지 로드 전에 이미 적용된다는 점이다.
원인은 vercel.app 서브도메인 정책 때문이다.
Vercel은 무료 플랜에서 *.vercel.app 서브도메인으로 프로젝트를 배포한다.
https://my-project.vercel.app
문제는 이 vercel.app 도메인 아래에 수천, 수만 개의 프로젝트가 배포되어 있다는 점이다. 만약 모든 프로젝트가 검색엔진에 인덱싱된다면:
vercel.app 도메인 자체의 검색 신뢰도가 낮아짐이런 이유로 Vercel은 커스텀 도메인이 연결되지 않은 vercel.app 서브도메인에는 자동으로 x-robots-tag: noindex 헤더를 삽입한다.
즉, 내 코드의 문제가 아니라 Vercel 인프라 레벨에서 강제로 삽입하는 헤더다.
터미널에서 curl 명령어로 응답 헤더를 확인할 수 있다.
curl -I https://my-project.vercel.app
응답 헤더에서 아래와 같이 확인된다.
HTTP/2 200
...
x-robots-tag: noindex
...
커스텀 도메인이 연결된 경우에는 이 헤더가 없다.
next.config.js에서 응답 헤더를 직접 설정해서 index로 덮어쓰려고 시도해봤다.
// next.config.js
const nextConfig = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "X-Robots-Tag", value: "index" },
],
},
];
},
};
결과는 효과 없음이다.
Vercel 인프라 레벨에서 응답 헤더를 후처리하여 noindex를 덮어쓰기 때문에, 애플리케이션 코드에서 설정한 헤더는 무시된다. 이것이 핵심 포인트다.
코드 레벨에서는 해결이 불가능하다.
커스텀 도메인을 연결하면 Vercel이 noindex 헤더를 자동으로 제거한다.
x-robots-tag: noindex 헤더 사라짐GitHub Pages는 무료 플랜에서도 noindex 헤더를 붙이지 않는다.
Next.js 프로젝트의 경우 next.config.js에서 정적 내보내기 설정이 필요하다.
// next.config.js
const nextConfig = {
output: "export",
};
Netlify도 무료 플랜에서 noindex 없이 배포가 가능하다. 설정 없이 GitHub 연동 후 바로 배포할 수 있다.
| 배포 환경 | SEO 인덱싱 여부 |
|---|---|
| vercel.app 서브도메인 | ❌ noindex 자동 삽입 |
| Vercel + 커스텀 도메인 | ✅ 정상 인덱싱 |
| GitHub Pages | ✅ 정상 인덱싱 |
| Netlify | ✅ 정상 인덱싱 |
이번 문제를 겪으면서 x-robots-tag를 처음 접했다면, noindex 외에도 다양한 디렉티브가 있다는 걸 알아두면 나중에 유용하다.
| 디렉티브 | 동작 |
|---|---|
noindex | 검색 결과에 노출하지 않음 |
nofollow | 페이지 내 링크를 따라가지 않음 |
none | noindex, nofollow 단축 표기 |
nosnippet | 검색 결과에서 텍스트 스니펫 미표시 |
noimageindex | 페이지 내 이미지를 색인화하지 않음 |
notranslate | 검색 결과에서 자동 번역 옵션 미제공 |
숫자로 세밀하게 제어하는 디렉티브도 있다.
# 스니펫 최대 150자
X-Robots-Tag: max-snippet: 150
# 이미지 미리보기 크기 제한 (none | standard | large)
X-Robots-Tag: max-image-preview: standard
# 특정 날짜 이후 검색 결과에서 제거
X-Robots-Tag: unavailable_after: Tue, 31 Dec 2025 23:59:59 GMT
봇 이름을 앞에 붙이면 해당 크롤러에게만 규칙이 적용된다.
X-Robots-Tag: googlebot: nofollow
X-Robots-Tag: googlebot: nofollow, BadBot: noindex, nofollow
헷갈리기 쉬운 둘의 차이를 정리하면 다음과 같다.
| x-robots-tag | robots.txt | |
|---|---|---|
| 적용 단위 | 개별 URL | 디렉토리/경로 단위 |
| 색인화 제어 | ✅ noindex 등 | ❌ 접근 차단만 가능 |
| 크롤링 차단 | ❌ | ✅ Disallow |
| 비 HTML 리소스 | ✅ PDF, 이미지 등 | ✅ |
한 가지 주의할 점이 있다. robots.txt로 접근이 차단된 URL은 크롤러가 x-robots-tag를 읽지 못한다. 색인 제거가 목적이라면 크롤링을 허용한 상태에서 noindex를 쓰는 것이 맞다.
커스텀 도메인이 연결된 환경에서 특정 경로만 색인화를 막고 싶다면 이렇게 쓴다.
// next.config.js
async headers() {
return [
{
source: '/admin/:path*',
headers: [{ key: 'X-Robots-Tag', value: 'noindex, nofollow' }],
},
];
},
Lighthouse SEO 점수가 갑자기 낮아졌다고 해서 코드를 한참 뒤졌는데, 원인은 Vercel의 플랫폼 정책이었다.
포트폴리오 사이트처럼 실제 검색 노출이 중요한 경우라면 커스텀 도메인 연결을 고려하는 것이 좋다. 도메인 가격은 연간 1~2만원 수준으로 부담이 크지 않다.
반대로 개발 중인 프로젝트나 테스트 배포라면 vercel.app 서브도메인의 noindex 정책이 오히려 의도치 않은 검색 노출을 막아주는 장점이 되기도 한다.
참고