--- title: 2026-04-21 Ironclad production cutover — apex 도메인 Worker 전환 updated: 2026-04-21 tags: [history, ironclad, website, cloudflare, production, crowdsec] --- ## 요약 `ironclad.it.com` (apex) production이 이전 경로(Cloudflare → APISIX → Caddy 컨테이너, Stamp/Flux volume_shared) 에서 **Cloudflare Worker `ironclad-site` (Next.js 16 + opennextjs-cloudflare)** 로 전환됨. 고객이 보는 화면이 6주 묵은 영문 "Ironclad Corp — Managed Security Hosting" 에서 최신 한국어 "Ironclad — 올어라운드 호스팅 & 보안서비스" 로 즉시 교체. ## 진행 경로 ### 1차 시도 — `v2026.04.21` 태그 push (실패) - `deploy-production.yml` 트리거, wrangler 빌드·업로드 성공 - DNS 등록 단계에서 Cloudflare API 실패: ``` code: 100117 Hostname 'ironclad.it.com' already has externally managed DNS records (A, CNAME). Either delete them, try a different hostname, or use the option 'override_existing_dns_record' to override. ``` - 원인: 기존 `ironclad.it.com A 172.233.93.180 (Proxied)` 레코드가 먼저 있어 `custom_domain: true` 등록 거부 ### 2차 시도 — custom_domain 제거하고 zone route로 전환 (성공) - `wrangler.jsonc` production env routes 변경: - Before: `{ pattern: "ironclad.it.com", custom_domain: true }` - After: `{ pattern: "ironclad.it.com/*", zone_name: "ironclad.it.com" }` - 로컬에서 `CLOUDFLARE_API_TOKEN=... pnpm deploy:production` 실행 → Worker 업로드 + route 등록 성공 - 결과: Cloudflare에 `ironclad.it.com/* → ironclad-site` route binding 생성됨 ### 3차 보정 — crowdsec bouncer wildcard route 버그 수정 (결정타) - 배포 후에도 prod 응답이 여전히 구 HTML. 원인 조사 결과 Cloudflare Workers Routes 상태: ``` www.ironclad.it.com/* -> ironclad-site ironclad.it.com/* -> ironclad-site *ironclad.it.com/* -> crowdsec-cloudflare-worker-bouncer ← 문제 ``` - `*ironclad.it.com/*` 패턴이 의도는 "subdomain wildcard" 였으나 **dot 누락**으로 apex까지 포괄(prefix wildcard로 해석되어 `ironclad.it.com/*` 도 매칭). crowdsec bouncer Worker가 먼저 잡아서 origin(APISIX → Caddy)로 pass-through. - Cloudflare API로 zone route `c148ae77ebb74f20b61ecd71d0e86b77`를 `*.ironclad.it.com/*`로 변경: ```bash curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE/workers/routes/$ROUTE_ID" \ -d '{"pattern":"*.ironclad.it.com/*","script":"crowdsec-cloudflare-worker-bouncer"}' ``` - 수정 직후 `ironclad.it.com` 응답에 `x-opennext: 1` + `x-powered-by: Next.js` 확인 → Worker가 정상 서빙 ## 현재 prod 상태 (2026-04-21 post-cutover) - `https://ironclad.it.com/` → Cloudflare → `ironclad-site` Worker (Next.js 16) - title: `Ironclad — 올어라운드 호스팅 & 보안서비스 | Ironclad` - `x-opennext: 1`, `x-powered-by: Next.js` - DNS A record `172.233.93.180`은 건드리지 않음 (롤백 준비) - apex 도메인의 bot 차단은 Next.js Worker 내부의 CrowdSec middleware (KV binding `CROWDSECCFBOUNCERNS = 9af0d1c1c14a4bc1a3835c2a5b22fd7a`)가 담당. Edge Worker bouncer는 서브도메인(`*.ironclad.it.com`)만 커버. ## 후속 정리 필요 | 항목 | 담당 | 우선순위 | |------|------|----------| | APISIX 라우트 `ironclad.it.com` 제거 (volume_shared 모드) | 씬 | 낮음 (트래픽 안 감, 남아있어도 무해) | | Stamp-Flux 파이프라인에서 ironclad.it.com 배포 경로 deprecate | 헤임달 | 중 (혼선 방지) | | `openclaw/openclaw-agents.md` volume_shared 모드 ironclad 관련 기록 정리 | 헤임달 | 중 | | Cloudflare DNS A record 변경 (172.233.93.180 → Cloudflare 기본) | kappa | 낮음 (zone route 방식이라 origin 무관) | | `v2026.04.21` 태그는 `deploy-production.yml`에서 실패로 남음 → 재태깅 불필요 (로컬에서 직접 배포 완료) | — | — | ## 커밋 - `hosting/ironclad`: - `v2026.04.21` (태그, f736487) — Header dark nav 승격 시점 - `6f2e2cc` fix(deploy): custom_domain → zone route로 전환 (DNS 충돌 회피) - Cloudflare API 변경: zone route `c148ae77ebb74f20b61ecd71d0e86b77` pattern 업데이트 (wrangler 소스 외) ## 학습 포인트 - **apex 도메인에 `custom_domain: true`를 쓰려면 기존 DNS 레코드가 없어야 한다**. Stamp/Flux 같이 기존 정적 배포 경로가 있는 도메인은 `zone_name` 기반 route 방식이 자연스럽다. - **Cloudflare Worker Routes wildcard의 dot 누락은 apex까지 포괄**. `*example.com/*` vs `*.example.com/*`는 전혀 다른 패턴. `*example.com/*`은 prefix wildcard로 apex 포함. - 기존 wildcard route가 apex로 해석되면 "내가 새로 만든 route가 우선" 될 거라 기대하기 어렵다. Cloudflare는 specificity tie-breaker가 있지만 명확히 의도한 대로 동작하지 않음. 가장 안전한 건 **wildcard pattern이 apex를 포함하지 않게 수정**. - `x-opennext: 1` 헤더는 OpenNext가 서빙하고 있음을 확인하는 가장 빠른 시그널.