fastly-wildcard-subdomain-compute-edge

Par divinevideo · divine-mobile

Corrige le routage de sous-domaines wildcard pour les services Fastly Compute@Edge lorsque les sous-domaines résolvent mais retournent 500 "Domain Not Found" ou des réponses vides. À utiliser quand : (1) la souscription TLS est "issued" mais le edge sert le certificat par défaut (CN=j.sni-644-default), (2) la création via Fastly domain-v1 réussit mais le sous-domaine retourne 500, (3) Static publisher/PublisherServer retourne un contenu vide pour les requêtes de sous-domaines, (4) le CNAME wildcard DNS résout vers la mauvaise cible. Couvre la sélection d'endpoint SNI pour la configuration TLS (x.sni vs j.sni vs w.sni), les problèmes de point final dans les entrées DNS, l'activation Fastly domain-v1, et la lecture directe depuis le KV store quand PublisherServer échoue pour les sous-domaines.

npx skills add https://github.com/divinevideo/divine-mobile --skill fastly-wildcard-subdomain-compute-edge

Routage de sous-domaines wildcard Fastly pour Compute@Edge

Problème

Les sous-domaines wildcard (*.example.com) ne fonctionnent pas sur les services Fastly Compute@Edge même après :

  • La création du domaine avec fastly domain-v1 create
  • La configuration des enregistrements DNS
  • Le fonctionnement correct du static publisher pour le domaine apex

Les symptômes incluent :

  • Erreurs 500 avec "Fastly error: unknown domain: subdomain.example.com"
  • PublisherServer retournant des réponses vides (content-length: 0) pour les requêtes de sous-domaine
  • Souscriptions TLS bloquées dans l'état « pending »
  • DNS résolvant vers les mauvaises cibles

Contexte / Conditions déclencheurs

  1. Mauvais point de terminaison SNI : Le certificat TLS est « émis » mais l'edge sert le certificat par défaut (CN=j.sni-644-default.ssl.fastly.net au lieu de votre domaine).

    CRITIQUE : Le nom de la configuration TLS indique quel point de terminaison SNI utiliser :

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

    Vérifiez votre configuration TLS :

    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}'

    Vérifiez en vous connectant directement au bon point de terminaison :

    echo | openssl s_client -servername subdomain.yourdomain.com \
      -connect x.sni.global.fastly.net:443 2>/dev/null | openssl x509 -noout -subject
  2. Problème de point final DNS : Les enregistrements CNAME résolvent vers target.com.yourdomain.com au lieu de target.com parce que le fournisseur DNS ajoute le nom de la zone sans point final.

    Vérifiez avec : dig @ns-server *.yourdomain.com CNAME +short

    Mauvais : x.sni.global.fastly.net.yourdomain.com. Bon : x.sni.global.fastly.net.

  3. Domaine non activé : Fastly domain-v1 affiche "activated": false, "verified": false même après la configuration de DNS.

    Vérifiez avec :

    curl -s -H "Fastly-Key: $(fastly profile token)" \
      "https://api.fastly.com/domain-management/v1/domains/DOMAIN_ID" | jq
  4. PublisherServer retourne vide pour les sous-domaines : Le PublisherServer de @fastly/compute-js-static-publish retourne des réponses null/vides quand le hostname de la requête est un sous-domaine, même s'il fonctionne pour le domaine apex et l'URL edgecompute.app.

Solution

Partie 1 : Corriger la configuration DNS

  1. CNAME wildcard avec point final (vérifiez votre config TLS pour le bon point de terminaison SNI) :

    Name: *
    Type: CNAME
    Value: x.sni.global.fastly.net.   <- Utilisez le point de terminaison de votre config TLS (x.sni, w.sni, ou j.sni)
  2. CNAME du défi ACME avec point final (pour la validation TLS) :

    Name: _acme-challenge
    Type: CNAME
    Value: CHALLENGE_VALUE.fastly-validations.com.   <- DOIT inclure le point final
  3. Alternative : Utilisez des enregistrements A au lieu de CNAME (évite les problèmes de point final) :

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

Partie 2 : Recréer le domaine Fastly (si bloqué)

Si le domaine affiche activated: false même après que le DNS soit correct :

# 1. Supprimer d'abord la souscription TLS (si elle existe)
fastly tls-subscription delete --id SUBSCRIPTION_ID --force

# 2. Supprimer le domaine
fastly domain-v1 delete --domain-id DOMAIN_ID

# 3. Recréer le domaine
fastly domain-v1 create --fqdn "*.yourdomain.com" --service-id SERVICE_ID

# 4. Créer une nouvelle souscription TLS
fastly tls-subscription create --domain "*.yourdomain.com"

# 5. Attendre 1-5 minutes pour la vérification et l'émission du TLS

Partie 3 : Corriger PublisherServer pour les sous-domaines

Le PublisherServer de @fastly/compute-js-static-publish ne sert pas le contenu pour les hostnames de sous-domaine. Vous devez lire directement depuis le KV store :

if (subdomain) {
  // Ouvrir le KV store de contenu
  const contentStore = new KVStore('your-content-store-name');

  // Lire le fichier index - NOTE : le nom de la collection peut être 'undefined' pas 'default'
  const indexEntry = await contentStore.get('default_index_undefined');
  const indexData = await indexEntry.json();

  // Obtenir l'entrée index.html - la structure est { '/path': { key: 'sha256:HASH', ... } }
  const indexHtmlInfo = indexData['/index.html'];

  // Lire le contenu réel
  const contentHash = indexHtmlInfo.key.replace('sha256:', '');
  const contentKey = `default_files_sha256_${contentHash}`;
  const htmlEntry = await contentStore.get(contentKey);
  const html = await htmlEntry.text();

  // Injecter les données spécifiques au sous-domaine et retourner
  const modifiedHtml = html.replace('<head>', `<head><script>window.SUBDOMAIN_DATA = {...}</script>`);
  return new Response(modifiedHtml, {
    headers: { 'Content-Type': 'text/html; charset=utf-8' }
  });
}

Partie 4 : Découverte du format de clé

Le static publisher utilise ces formats de clé KV :

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

Pour découvrir vos noms de clé réels :

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

Vérification

# 1. Vérifier que le DNS est correct
dig @8.8.8.8 subdomain.yourdomain.com A +short
# Devrait retourner les IPs Fastly

# 2. Vérifier que le domaine est activé
curl -s -H "Fastly-Key: $(fastly profile token)" \
  "https://api.fastly.com/domain-management/v1/domains/DOMAIN_ID" | jq '{activated, verified}'
# Les deux devraient être true

# 3. Vérifier que le TLS est émis
fastly tls-subscription list | grep yourdomain
# L'état devrait être "issued"

# 4. Tester le sous-domaine
curl -sI "https://subdomain.yourdomain.com"
# Devrait retourner 200 avec du contenu

Exemple

Correction complète pour le routage de sous-domaine *.divine.space :

// Dans le handler Compute@Edge
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

  • Le collectionName dans les clés KV est souvent undefined (chaîne littérale) plutôt que default en raison de la configuration du static publisher. Vérifiez toujours les clés réelles.
  • Les modifications DNS peuvent prendre jusqu'à 3 heures pour se propager en raison des paramètres TTL.
  • Le cache edge Fastly peut servir des réponses obsolètes — utilisez fastly purge --all après les modifications.
  • Les ressources statiques (/assets/*, fichiers .js, .css) doivent être servies AVANT le traitement des sous-domaines en utilisant le PublisherServer normal.
  • Le certificat TLS wildcard (*.domain.com) nécessite que l'enregistrement DNS du défi ACME soit correct avant son émission.

Références

Skills similaires