Featured image of post [Hugo] GitHub Pages에서 alias·canonical로 리디렉션 SEO 가이드

[Hugo] GitHub Pages에서 alias·canonical로 리디렉션 SEO 가이드

GitHub Pages는 서버 301/308을 지원하지 않습니다. Hugo alias와 meta refresh(0초), rel=canonical, noindex, noscript 링크를 조합해 검색 노출과 신호를 올바르게 통합하는 설정을 단계별로 안내합니다. 정적 호스팅 환경에서 SEO 친화적 리디렉션을 적용하려는 블로거와 개발자에게 추천합니다.

요약

GitHub Pages(*.github.io)는 정적 호스팅 구조상 서버 단에서 3xx HTTP 헤더(301·308) 를 설정할 수 없습니다. 그래서 URL을 바꾼 뒤에도 예전 주소로 들어오는 사용자와 검색엔진 크롤러를 올바른 페이지로 보내려면, Hugo의 aliases 로 만드는 alias 전용 페이지가 현실적인 대안입니다. 이 페이지에는 meta refresh(0초)rel="canonical" 을 함께 두어, Google이 영구 리디렉션 신호로 인식하고 표준 URL로 신호를 묶을 수 있게 합니다.

추천 구성은 다음과 같습니다. alias 페이지에는 canonical을 최종 URL로 지정하고, 필요 시 robots noindex, follow 를 두며, noscript 구간에 눈에 보이는 링크를 넣어 JavaScript 없이도 이동할 수 있게 합니다. 그렇게 하면 Search Console에서 alias URL이 색인에 남지 않으면서, 신호가 대상 URL로 강하게 통합됩니다.


배경: 왜 301이 아닌가

일반적인 웹 서버(Apache, Nginx 등)에서는 .htaccess 나 서버 설정으로 301 Moved Permanently308 Permanent Redirect 를 보낼 수 있습니다. 검색엔진은 이런 서버 측 리디렉션을 가장 신뢰하는 방식으로 취급합니다. 그러나 GitHub Pages 는 정적 파일 호스팅만 제공하므로, 요청 URL별로 HTTP 상태 코드를 바꾸는 설정을 할 수 없습니다. 요청이 오면 해당 경로의 HTML 파일이 있으면 200 OK 로 그대로 내려줍니다. 따라서 “예전 URL → 새 URL” 을 서버가 301/308로 응답하는 형태로 구현할 수 없습니다.

이 제약 때문에, GitHub Pages 위에서 “예전 주소로 들어오면 새 주소로 보낸다” 를 구현하려면 클라이언트(브라우저) 쪽 동작에 의존해야 합니다. 대표적으로 HTML meta http-equiv="refresh" 로 0초 후 대상 URL로 이동시키는 방식이 널리 쓰입니다. Google은 공식 문서에서 0초 meta refresh영구 리디렉션과 유사한 신호로 해석한다고 밝히고 있으며, canonical 과 함께 사용하면 어떤 URL이 최종(표준) 주소인지 판단하는 데 더 유리합니다.


핵심 개념 정리

이 방식에서 쓰는 요소를 짧게 정리합니다.

  • alias(별칭)
    한 문서가 여러 URL로 접근 가능하도록 할 때, “진짜” URL이 아닌 과거·대체 경로를 alias라고 합니다. Hugo에서는 front matter의 aliases 에 경로 목록을 넣으면, 각 경로마다 별도 HTML 페이지가 생성됩니다.

  • meta refresh (0초)
    <meta http-equiv="refresh" content="0; url=최종URL" /> 로, 페이지 로드 직후 지정한 URL로 이동하게 합니다. 서버는 200을 주지만, 사용자·크롤러는 곧바로 다른 URL로 넘어가므로 리디렉션처럼 동작합니다.

  • rel=“canonical”
    <link rel="canonical" href="최종URL" /> 로, “이 페이지의 표준 URL은 이것이다” 라고 검색엔진에 알립니다. alias 페이지에 canonical을 최종(대상) URL 로 두면, 검색엔진이 신호를 그 URL로 묶어 줍니다.

  • noindex, follow
    alias 페이지를 검색 결과에 넣지 말고(noindex), 링크는 따라가게(follow) 할 때 사용합니다. 중복·과거 URL 색인을 줄이면서 링크 가치는 최종 URL로 전달하고 싶을 때 선택할 수 있습니다. Google은 “같은 사이트 내에서 canonical만으로 표준을 정할 수 있으면 noindex는 보통 쓰지 말라” 고 권장하므로, 꼭 필요할 때만 사용하는 것이 좋습니다.

아래 다이어그램은 사용자 또는 크롤러가 예전 URL(alias) 에 접속했을 때, meta refreshcanonical 이 어떻게 동작해 최종 URL로 이어지는지 흐름을 보여줍니다.

flowchart LR
  Visitor["사용자 또는
크롤러"] AliasPage["alias 페이지
meta refresh + canonical"] CanonicalUrl["canonical 대상 URL
실제 콘텐츠"] Visitor --> AliasPage AliasPage -->|"0초 후 이동"| CanonicalUrl AliasPage -->|"rel=canonical 신호"| CanonicalUrl

구현: Hugo alias.html 레이아웃

Hugo는 aliases 에 지정한 각 경로에 대해 alias 전용 레이아웃을 사용해 HTML을 냅니다. 이 레이아웃에서 canonical, meta refresh, 그리고 noscript 안에 보이는 링크**를 넣으면 됩니다. 아래는 현재 사이트에서 쓰는 alias 레이아웃 예시입니다. rel="canonical"최종(대상) URL{{ .Permalink }} 로 두고, meta refresh도 같은 URL로 보내며, JavaScript가 꺼진 환경을 위해 noscript 안에 클릭 가능한 링크를 둡니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
  <head>
    <title>{{ .Permalink }}</title>
    <link rel="canonical" href="{{ .Permalink }}"/>
    <meta charset="utf-8" />
    <meta http-equiv="refresh" content="0; url={{ .Permalink }}" />
  </head>
  <body>
    <noscript>
      <p>
        This page has moved permanently. If you are not redirected automatically, follow this link:
        <a href="{{ .Permalink }}">{{ .Permalink }}</a>
      </p>
    </noscript>
  </body>
</html>

{{ .Permalink }} 는 이 alias가 가리키는 진짜 문서의 최종 URL 입니다. 따라서 “이 페이지는 영구히 옮겨졌고, 표준 주소는 여기다” 라는 메시지가 일관되게 전달됩니다.


콘텐츠에 예전 URL 연결하기: Front Matter aliases

각 콘텐츠에서 과거에 사용했던 URL 을 alias로 등록하면, Hugo가 위 레이아웃으로 해당 경로에 대한 HTML을 생성합니다. 문서 front matter에 aliases 배열을 두고, 예전 경로를 문자열로 넣습니다. 슬래시로 시작하고 끝나는 경로를 쓰는 것이 일반적입니다.

1
2
3
4
5
---
title: "[TV Show] 예시 글"
aliases:
  - "/post/old-path-slug/"   # 이전에 사용하던 URL
---

빌드 시 /post/old-path-slug/ 에 접근하면 alias 전용 페이지가 제공되고, 그 페이지의 meta refresh와 canonical에 의해 실제 문서 URL로 리디렉션·신호 통합이 이루어집니다.


검증 방법

설정이 의도대로 동작하는지 확인하는 방법입니다.

  • 브라우저
    예전 URL(alias)로 접속했을 때, 즉시 새 URL로 이동하는지 봅니다. 주소창이 최종 URL로 바뀌고, 콘텐츠가 그 문서의 것이면 됩니다.

  • 응답 헤더
    GitHub Pages는 alias 경로에도 200 OK 를 반환합니다. 서버가 301/308을 주는 것이 아니기 때문입니다. 리디렉션은 HTML 내 meta refresh 로만 일어납니다.

  • HTML 내용 확인 (PowerShell)
    alias 페이지의 본문에서 meta refreshlink canonical 이 올바른 최종 URL을 가리키는지 확인할 수 있습니다.

1
irm https://42jerrykim.github.io/post/old-path-slug/ -UseBasicParsing | Select-String -Pattern "http-equiv=`"refresh`"|rel=`"canonical`""

출력에 content="0; url=…"rel="canonical" 이 나오고, URL이 의도한 최종 주소와 같으면 설정이 맞습니다.


Google 가이드 요약

Google 검색 동작과 권장사항을 요약하면 다음과 같습니다.

  • 리디렉션과 Google 검색
    가능하면 서버 측 301/308 을 쓰라고 권장합니다. 그게 불가능한 환경(예: GitHub Pages)에서는 0초 meta refresh영구 리디렉션과 비슷한 신호로 처리합니다.

  • Canonicalization
    리디렉션(또는 meta refresh)과 rel="canonical" 을 함께 쓰면, “어느 URL이 표준인가” 에 대한 신호가 더 분명해집니다. alias 페이지의 canonical을 항상 최종(대상) URL 로 두는 것이 좋습니다.

  • noindex 사용 시 주의
    Google은 같은 사이트 안에서 “어느 쪽이 대표 URL인지” 를 정하기 위해 noindex를 쓰는 것을 권장하지 않습니다. 대부분의 경우 meta refresh(0초) + canonical 만으로도 신호가 잘 묶이므로, alias 페이지에 noindex를 넣지 않는 편이 좋습니다. 색인에서 반드시 빼고 싶은 특수한 경우에만 noindex를 신중히 고려하세요.


참고 문서


마무리 및 적용 체크리스트

GitHub Pages처럼 서버 측 301/308을 쓸 수 없는 환경에서는, Hugo aliasesmeta refresh(0초) + rel=canonical 조합이 Search Console과 검색 노출 측면에서 현실적인 최선에 가깝습니다. 서버가 301/308을 줄 수 있는 호스팅(예: 커스텀 도메인 + Cloudflare 리디렉션 규칙, Netlify, Vercel 등)으로 옮기면 그때는 진짜 301/308을 쓰는 편이 더 낫습니다.

이 글을 적용할 때 확인할 것:

  • alias 전용 레이아웃에 rel="canonical"최종(대상) URL 로 설정되어 있는가?
  • meta refreshurl= 값이 동일한 최종 URL인가?
  • noscript 안에 “이동한 주소” 로 가는 보이는 링크가 있는가?
  • 각 이전 URL이 front matter aliases 에 정확한 경로로 들어가 있는가?
  • 브라우저에서 예전 URL 접속 시 즉시 새 URL로 이동하는가?
  • noindex는 필요한 경우에만 사용하고, 일반적으로는 canonical + meta refresh만으로 두었는가?

위를 만족하면, GitHub Pages 위에서도 리디렉션과 SEO 신호를 안정적으로 통합할 수 있습니다.