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
-
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.netau 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 - "HTTP/3 & TLS v1.3 + 0RTT (x.sni)" → DNS vers
-
Problème de point final DNS : Les enregistrements CNAME résolvent vers
target.com.yourdomain.comau lieu detarget.comparce que le fournisseur DNS ajoute le nom de la zone sans point final.Vérifiez avec :
dig @ns-server *.yourdomain.com CNAME +shortMauvais :
x.sni.global.fastly.net.yourdomain.com.Bon :x.sni.global.fastly.net. -
Domaine non activé : Fastly domain-v1 affiche
"activated": false, "verified": falsemê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 -
PublisherServer retourne vide pour les sous-domaines : Le PublisherServer de
@fastly/compute-js-static-publishretourne 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
-
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) -
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 -
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
collectionNamedans les clés KV est souventundefined(chaîne littérale) plutôt quedefaulten 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 --allaprè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.