본문으로 건너뛰기
cccskills
무료GitHub에 공개

fastly-wildcard-subdomain-compute-edge

Fix wildcard subdomain routing for Fastly Compute@Edge services when subdomains resolve but return 500 "Domain Not Found" or empty responses. Use when: (1) TLS subscription "issued" but edge serves default certificate (CN=j.sni-644-default), (2) Fastly domain-v1 create succeeds but subdomain returns 500, (3) Static publisher/PublisherServer returns empty content for subdomain requests, (4) DNS wildcard CNAME resolves to wrong target. Covers TLS configuration SNI endpoint selection (x.sni vs j.sni vs w.sni), DNS trailing dot issues, Fastly domain-v1 activation, and reading from KV store directly when PublisherServer fails for subdomains.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md8.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Fastly Wildcard Subdomain Routing for Compute@Edge

Problem

Wildcard subdomains (*.example.com) don't work on Fastly Compute@Edge services even after:

  • Creating the domain with fastly domain-v1 create
  • Setting up DNS records
  • The static publisher working fine for the apex domain

Symptoms include:

  • 500 errors with "Fastly error: unknown domain: subdomain.example.com"
  • PublisherServer returning empty responses (content-length: 0) for subdomain requests
  • TLS subscriptions stuck in "pending" state
  • DNS resolving to wrong targets

Context / Trigger Conditions

  1. Wrong SNI endpoint: TLS certificate is "issued" but edge serves default certificate (CN=j.sni-644-default.ssl.fastly.net instead of your domain).

    CRITICAL: The TLS configuration name tells you which SNI endpoint to use:

    • "HTTP/3 & TLS v1.3 + 0RTT (x.sni)" → DNS to x.sni.global.fastly.net.
    • "HTTP/3 & TLS v1.3 (w.sni)" → DNS to w.sni.global.fastly.net.
    • Default/legacy → DNS to j.sni.global.fastly.net.

    Check your TLS configuration:

    FASTLY_KEY=$(fastly profile token) && curl -s -H "Fastly-Key: $FASTLY_KEY" \
      "https://api.fastly.com/tls/configurations" | jq '.data[] | {id: .id, name: .attributes.name}'
    

    Verify by connecting directly to the correct endpoint:

    echo | openssl s_client -servername subdomain.yourdomain.com \
      -connect x.sni.global.fastly.net:443 2>/dev/null | openssl x509 -noout -subject
    
  2. DNS trailing dot issue: CNAME records resolve to target.com.yourdomain.com instead of target.com because the DNS provider appends the zone name without a trailing dot.

    Check with: dig @ns-server *.yourdomain.com CNAME +short

    Bad: x.sni.global.fastly.net.yourdomain.com. Good: x.sni.global.fastly.net.

  3. Domain not activated: Fastly domain-v1 shows "activated": false, "verified": false even after DNS is set up.

    Check with:

    curl -s -H "Fastly-Key: $(fastly profile token)" \
      "https://api.fastly.com/domain-management/v1/domains/DOMAIN_ID" | jq
    
  4. PublisherServer returns empty for subdomains: The @fastly/compute-js-static-publish PublisherServer returns null/empty responses when the request hostname is a subdomain, even though it works for the apex domain and edgecompute.app URL.

Solution

Part 1: Fix DNS Configuration

  1. Wildcard CNAME with trailing dot (check your TLS config for correct SNI endpoint):

    Name: *
    Type: CNAME
    Value: x.sni.global.fastly.net.   <- Use endpoint from your TLS config name (x.sni, w.sni, or j.sni)
    
  2. ACME challenge CNAME with trailing dot (for TLS validation):

    Name: _acme-challenge
    Type: CNAME
    Value: CHALLENGE_VALUE.fastly-validations.com.   <- MUST include trailing dot
    
  3. Alternative: Use A records instead of CNAME (avoids trailing dot issues):

    Name: *
    Type: A
    Values: 151.101.1.242, 151.101.65.242, 151.101.129.242, 151.101.193.242
    

Part 2: Recreate Fastly Domain (if stuck)

If domain shows activated: false even after DNS is correct:

# 1. Delete TLS subscription first (if exists)
fastly tls-subscription delete --id SUBSCRIPTION_ID --force

# 2. Delete the domain
fastly domain-v1 delete --domain-id DOMAIN_ID

# 3. Recreate domain
fastly domain-v1 create --fqdn "*.yourdomain.com" --service-id SERVICE_ID

# 4. Create new TLS subscription
fastly tls-subscription create --domain "*.yourdomain.com"

# 5. Wait 1-5 minutes for verification and TLS issuance

Part 3: Fix PublisherServer for Subdomains

The PublisherServer from @fastly/compute-js-static-publish doesn't serve content for subdomain hostnames. You must read from the KV store directly:

if (subdomain) {
  // Open the content KV store
  const contentStore = new KVStore('your-content-store-name');

  // Read the index file - NOTE: collection name might be 'undefined' not 'default'
  const indexEntry = await contentStore.get('default_index_undefined');
  const indexData = await indexEntry.json();

  // Get the index.html entry - structure is { '/path': { key: 'sha256:HASH', ... } }
  const indexHtmlInfo = indexData['/index.html'];

  // Read the actual content
  const contentHash = indexHtmlInfo.key.replace('sha256:', '');
  const contentKey = `default_files_sha256_${contentHash}`;
  const htmlEntry = await contentStore.get(contentKey);
  const html = await htmlEntry.text();

  // Inject subdomain-specific data and return
  const modifiedHtml = html.replace('<head>', `<head><script>window.SUBDOMAIN_DATA = {...}</script>`);
  return new Response(modifiedHtml, {
    headers: { 'Content-Type': 'text/html; charset=utf-8' }
  });
}

Part 4: Key Format Discovery

The static publisher uses these KV key formats:

  • Index: ${publishId}_index_${collectionName} (e.g., default_index_undefined)
  • Settings: ${publishId}_settings_${collectionName}
  • Files: ${publishId}_files_sha256_${hash}

To discover your actual key names:

curl -s -H "Fastly-Key: $(fastly profile token)" \
  "https://api.fastly.com/resources/stores/kv/STORE_ID/keys?limit=50" | jq '.data[]' | grep index

Verification

# 1. Verify DNS is correct
dig @8.8.8.8 subdomain.yourdomain.com A +short
# Should return Fastly IPs

# 2. Verify domain is activated
curl -s -H "Fastly-Key: $(fastly profile token)" \
  "https://api.fastly.com/domain-management/v1/domains/DOMAIN_ID" | jq '{activated, verified}'
# Both should be true

# 3. Verify TLS is issued
fastly tls-subscription list | grep yourdomain
# State should be "issued"

# 4. Test the subdomain
curl -sI "https://subdomain.yourdomain.com"
# Should return 200 with content

Example

Complete fix for *.divine.space subdomain routing:

// In Compute@Edge handler
if (subdomain && namesStore) {
  const entry = await namesStore.get(`name:${subdomain}`);

  const contentStore = new KVStore('divine-space-content');
  const indexEntry = await contentStore.get('default_index_undefined');
  const indexData = await indexEntry.json();
  const indexHtmlInfo = indexData['/index.html'];
  const contentHash = indexHtmlInfo.key.replace('sha256:', '');
  const htmlEntry = await contentStore.get(`default_files_sha256_${contentHash}`);
  const html = await htmlEntry.text();

  if (entry) {
    const data = await entry.json();
    const injectedHtml = html.replace('<head>', `<head>
      <script>window.__DIVINE_SPACE_USER__ = {
        subdomain: "${subdomain}",
        pubkey: "${data.pubkey}"
      };</script>`);
    return new Response(injectedHtml, {
      headers: { 'Content-Type': 'text/html; charset=utf-8' }
    });
  }
}

Notes

  • The collectionName in KV keys is often undefined (literal string) rather than default due to how the static publisher is configured. Always check actual keys.
  • DNS changes can take up to 3 hours to propagate due to TTL settings.
  • Fastly edge cache may serve stale responses—use fastly purge --all after changes.
  • Static assets (/assets/*, .js, .css files) should be served BEFORE subdomain handling using the normal PublisherServer.
  • The wildcard TLS cert (*.domain.com) requires the ACME challenge DNS record to be correct before it will issue.

References

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

divinevideo의 스킬 모두 보기

이 스킬의 문제 신고