Ghost 6.0 ActivityPub 설정 완벽 가이드 - 셀프호스팅 블로그를 Fediverse에 연결하기
Ghost 6.0 셀프호스팅 블로그를 Fediverse(Mastodon, Threads)에 연결하는 방법. NPM 프록시 설정부터 트러블슈팅까지.
1. ActivityPub이란?
ActivityPub은 분산형 소셜 네트워크를 위한 프로토콜입니다. Ghost 6.0부터 이 기능을 지원하여 블로그를 Fediverse에 연결할 수 있습니다.
1.1 ActivityPub으로 할 수 있는 것
Ghost 블로그 + ActivityPub:
├── Mastodon에서 블로그 팔로우 가능
├── Threads에서 블로그 검색/팔로우
├── 새 글 발행 시 팔로워 피드에 자동 게시
├── Fediverse 댓글/좋아요 Ghost Admin에서 확인
└── @[email protected] 형태로 검색 가능
1.2 Ghost Pro vs 셀프호스팅
| 항목 | Ghost Pro | 셀프호스팅 (ap.ghost.org) | 셀프호스팅 (자체 컨테이너) |
|---|---|---|---|
| 설정 난이도 | 자동 | 중간 | 높음 |
| 팔로워 제한 | 무제한 | 2,000명 | 무제한 |
| 일일 상호작용 | 무제한 | 100회 | 무제한 |
| 추가 비용 | Ghost Pro 요금 | 무료 | 무료 (서버 리소스) |
| 데이터 저장 | Ghost 서버 | Ghost 서버 | 내 서버 |
이 글에서는 셀프호스팅 + ap.ghost.org 방식을 다룹니다. 대부분의 개인 블로그에 충분한 방식입니다.
2. 전제 조건
이 가이드는 다음 환경을 기준으로 합니다:
| 항목 | 버전/설정 |
|---|---|
| Ghost | 6.0 이상 (6.13.2 테스트) |
| 리버스 프록시 | Nginx Proxy Manager |
| CDN/DNS | Cloudflare (Proxy ON) |
| SSL | Full (Strict) 모드 |
다른 환경(Traefik, Caddy, 직접 Nginx)이라면 프록시 설정 부분만 해당 환경에 맞게 조정하면 됩니다.
3. 아키텍처 이해
ActivityPub이 작동하려면 특정 경로의 요청을 Ghost가 아닌 ap.ghost.org로 프록시해야 합니다.
3.1 요청 흐름
사용자/Fediverse 서버
│
▼
Cloudflare (CDN)
│
▼
Nginx Proxy Manager
│
├── /.ghost/activitypub/* ──▶ ap.ghost.org (ActivityPub 서비스)
├── /.well-known/webfinger ──▶ ap.ghost.org
├── /.well-known/nodeinfo ──▶ ap.ghost.org
│
└── 그 외 모든 요청 ──▶ Ghost 컨테이너 (localhost:2368)
3.2 필요한 경로
| 경로 | 용도 | 프록시 대상 |
|---|---|---|
/.ghost/activitypub/* |
ActivityPub API | ap.ghost.org |
/.well-known/webfinger |
계정 검색 (Fediverse) | ap.ghost.org |
/.well-known/nodeinfo |
서버 정보 | ap.ghost.org |
4. Nginx Proxy Manager 설정
4.1 Advanced 탭 열기
- NPM 관리 페이지 접속
blog.example.com호스트 Edit 클릭- Advanced 탭 선택 (Custom Locations가 아님!)
4.2 Custom Nginx Configuration 입력
아래 설정을 그대로 복사하여 붙여넣기:
# ActivityPub 프록시 설정
# Ghost 6.0+ 셀프호스팅용
# 1. ActivityPub API 엔드포인트
location /.ghost/activitypub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_ssl_server_name on;
proxy_pass https://ap.ghost.org;
}
# 2. WebFinger (Fediverse 계정 검색용)
location /.well-known/webfinger {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_ssl_server_name on;
proxy_pass https://ap.ghost.org;
}
# 3. NodeInfo (서버 정보)
location /.well-known/nodeinfo {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_ssl_server_name on;
proxy_pass https://ap.ghost.org;
}
4.3 설정 저장
Save 버튼 클릭. NPM이 자동으로 nginx를 reload합니다.
4.4 설정 확인 (선택)
서버에서 생성된 nginx 설정 확인:
docker exec nginx-proxy-manager cat /data/nginx/proxy_host/1.conf | grep -A5 "activitypub"
정상 출력:
location /.ghost/activitypub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
...
5. Cloudflare Cache Rules 설정
Cloudflare가 ActivityPub 요청을 캐시하면 문제가 발생합니다. 해당 경로를 캐시에서 제외해야 합니다.
5.1 Cache Rules 생성
- Cloudflare Dashboard → 도메인 선택
- Rules → Cache Rules
- Create rule 클릭
5.2 규칙 설정
Rule name:
Ghost ActivityPub Bypass
When incoming requests match... (Custom filter expression):
| Field | Operator | Value |
|---|---|---|
| URI Path | starts with | /.ghost/activitypub |
| Or | ||
| URI Path | starts with | /.well-known/webfinger |
| Or | ||
| URI Path | starts with | /.well-known/nodeinfo |
Then... (Cache eligibility):
- Bypass cache 선택
5.3 규칙 배포
Deploy 클릭
6. Ghost Admin에서 활성화
6.1 Network 기능 활성화
- Ghost Admin 접속:
https://blog.example.com/ghost - Settings → Growth → Network
- Enable 토글 ON
6.2 캐시 문제 해결 (중요!)
Network를 켰는데 "Loading interrupted" 에러가 나타나면:
- 시크릿/프라이빗 창 열기 (Cmd+Shift+N / Ctrl+Shift+N)
- Ghost Admin 접속
- Settings → Growth → Network → OFF
- 잠시 대기 (5초)
- Network → ON
- 페이지 새로고침
왜 시크릿 창인가?
- 일반 창의 브라우저 캐시가 이전 에러 응답을 저장
- 시크릿 창은 캐시 없이 깨끗한 상태로 요청
- Ghost가 ap.ghost.org에 새로 등록됨
7. 연결 테스트
7.1 WebFinger 테스트 (가장 확실한 방법)
curl -s "https://blog.example.com/.well-known/webfinger?resource=acct:[email protected]"
정상 응답:
{
"subject": "acct:[email protected]",
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://blog.example.com/.ghost/activitypub/users/index"
}
]
}
7.2 Mastodon에서 검색
Mastodon 계정이 필요합니다. 무료 가입 가능한 인스턴스:
- mastodon.social - 공식
- mas.to - 즉시 가입 가능
가입 후:
- 검색창에
@[email protected]입력 - 블로그 프로필이 나타나면 성공
7.3 Ghost Admin에서 확인 (가장 간단)
- Ghost Admin → Settings → Growth → Network
- "Loading interrupted" 없이 Network 탭이 정상 로딩되면 성공
- 프로필, 팔로워, 피드 등이 표시됨
8. 트러블슈팅
8.1 "Loading interrupted" 에러
증상:
Ghost Admin의 Network 탭에서 "Loading interrupted" 메시지
원인:
- NPM 프록시 설정 누락
- 브라우저 캐시에 에러 응답 저장됨
해결:
- NPM Advanced 설정 확인 (섹션 4 참조)
- 시크릿 창에서 Network 토글 OFF → ON
8.2 ROLE_MISSING 에러
증상:
curl -s https://blog.example.com/.ghost/activitypub/v1/site
# {"error":"Forbidden","code":"ROLE_MISSING"}
원인:
- Ghost가 ap.ghost.org에 등록되지 않음
- 이전 등록이 손상됨
해결:
- Ghost Admin → Settings → Growth → Network → OFF
- 브라우저 캐시 완전 삭제 또는 시크릿 창 사용
- Network → ON
- 재테스트
8.3 SITE_MISSING 에러
증상:
{"error":"Forbidden","code":"SITE_MISSING"}
원인:
- 이전에 같은 도메인으로 다른 Ghost 인스턴스가 등록됨
- ap.ghost.org에 오래된 데이터 존재
해결:
- Ghost 포럼에서 지원 요청
- 또는 시크릿 창에서 Network 토글 재시도
- 최후 수단: Ghost 지원팀에 도메인 초기화 요청
8.4 404 에러 (Ghost 로그)
증상:
Ghost 로그에 /.ghost/activitypub/* 경로가 404로 기록됨
[INFO] "GET /.ghost/activitypub/v1/site" 404 5ms
원인:
- NPM 프록시가 ap.ghost.org가 아닌 Ghost로 요청 전달
해결:
- NPM Advanced 설정이 Custom Locations가 아닌 Advanced 탭에 있는지 확인
- location 블록이
location /보다 먼저 매칭되는지 확인 - NPM 재시작:
docker restart nginx-proxy-manager
8.5 301 Redirect Loop
증상:
브라우저에서 무한 리다이렉트
원인:
- Cloudflare SSL 설정과 NPM SSL 설정 충돌
해결:
- Cloudflare SSL/TLS → Full (Strict) 확인
- NPM에서 Force SSL 활성화 확인
- Cloudflare에서 해당 경로 캐시 바이패스 확인
9. ARM64 서버 사용자 참고
Oracle Cloud Free Tier 등 ARM64(aarch64) 서버를 사용하는 경우:
9.1 제한 사항
Ghost의 ActivityPub 컨테이너(ghcr.io/tryghost/activitypub)는 AMD64만 지원합니다.
ghcr.io/tryghost/activitypub:edge → AMD64 only ❌
9.2 대안
| 옵션 | 설명 |
|---|---|
| ap.ghost.org 사용 (권장) | 이 글의 방법, 제한 있지만 간편 |
| 소스에서 빌드 | ARM64용으로 직접 빌드 (복잡) |
| AMD64 서버 사용 | 별도 서버에 ActivityPub 컨테이너 배포 |
대부분의 개인 블로그는 ap.ghost.org의 제한(2,000 팔로워, 100 상호작용/일) 내에서 충분히 운영 가능합니다.
10. 핵심 개념 정리
| 개념 | 설명 |
|---|---|
| ActivityPub | 분산형 소셜 네트워크 프로토콜 |
| Fediverse | ActivityPub으로 연결된 서버들의 네트워크 |
| ap.ghost.org | Ghost가 운영하는 ActivityPub 게이트웨이 |
| WebFinger | Fediverse에서 사용자 검색용 프로토콜 |
| NodeInfo | 서버 정보 공개용 표준 |
| ROLE_MISSING | Ghost가 ap.ghost.org에 미등록 상태 |
11. 베스트 프랙티스
체크리스트
- [ ] NPM Advanced 탭에 프록시 설정 추가
- [ ] Cloudflare Cache Rules로 ActivityPub 경로 바이패스
- [ ] 시크릿 창에서 Network 활성화
- [ ] curl로 API 응답 테스트
- [ ] Mastodon에서 블로그 검색 테스트
권장 설정
| 항목 | 권장값 |
|---|---|
| Cloudflare SSL | Full (Strict) |
| NPM Force SSL | ON |
| NPM HTTP/2 | ON |
| NPM Websockets | ON |
12. FAQ
Q: ActivityPub을 활성화하면 성능에 영향이 있나요?
A: 거의 없습니다. ActivityPub 요청은 ap.ghost.org로 프록시되므로 Ghost 서버 부하는 증가하지 않습니다.
Q: 팔로워 2,000명 제한에 도달하면 어떻게 되나요?
A: 더 이상 새 팔로워를 받을 수 없습니다. 자체 ActivityPub 컨테이너를 호스팅하면 제한이 없습니다.
Q: Threads에서도 검색되나요?
A: Threads는 아직 ActivityPub을 제한적으로 지원합니다. Mastodon에서는 확실히 작동합니다.
Q: 기존 글도 Fediverse에 보이나요?
A: 새로 발행하는 글만 팔로워 피드에 나타납니다. 기존 글은 블로그 프로필에서 볼 수 있습니다.
Q: ActivityPub을 비활성화하면 팔로워 데이터는 어떻게 되나요?
A: ap.ghost.org에 보관됩니다. 다시 활성화하면 복구됩니다.
13. 다음 단계
ActivityPub 설정이 완료되었습니다! 이제 블로그가 Fediverse의 일부가 되었습니다.
시리즈 목차:
- Oracle Cloud 무료 서버 세팅
- Ghost 블로그 Docker 설치
- Ghost 블로그 백업 자동화
- 검색엔진 등록 (Google/Naver)
- Ghost 6.0 업그레이드
- Ghost ActivityPub 설정 (Fediverse) ← 현재 글
- Ghost Analytics 설정 (Tinybird)