本文へ移動
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

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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-mobile2662026年10月13日 更新

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-mobile2662026年10月13日 更新

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-mobile2662026年10月13日 更新

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-mobile2662026年10月13日 更新

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-mobile2662026年10月13日 更新

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-mobile2662026年10月13日 更新

divinevideo のスキルをすべて見る

このスキルの問題を報告する