Aller au contenu
Integration

Surveiller l'expiration du certificat SSL avec une API REST

| 6 min read

Vérifiez les dates d'expiration des certificats SSL, les émetteurs et les en-têtes de sécurité pour tout domaine doté de deux points de terminaison d'API. Inclut des exemples d'alertes GitHub Actions, Node.js et Slack.

Green padlock icon on a browser address bar
Photo by Towfiqu barbhuiya on Unsplash

Un certificat SSL expiré met votre site hors ligne et affiche un avertissement de navigateur qui fait peur clients absents. Let's Encrypt certificats à renouvellement automatique, mais DNS mal configuré, tâches cron échouées, et les certificats manuels oubliés provoquent toujours des pannes. Vous avez besoin d'un moyen de surveiller l'expiration dates sur tous vos domaines.

L'API botoi fournit deux points de terminaison pour cela. On renvoie les détails du certificat (émetteur, dates de validité, jours jusqu'à expiration). L'autre vérifie la prise en charge HTTPS et analyse la sécurité en-têtes. Ensemble, ils couvrent à la fois la surveillance des expirations et les audits de posture de sécurité.

Obtenez les détails du certificat avec /v1/ssl-cert/certificate

Ce point de terminaison se connecte au domaine, lit le certificat TLS et renvoie des informations structurées. des données que vous pouvez analyser dans n’importe quelle langue.

curl -X POST https://api.botoi.com/v1/ssl-cert/certificate \\
  -H "Content-Type: application/json" \\
  -d '{"domain": "stripe.com"}'

Réponse:

{
  "success": true,
  "data": {
    "domain": "stripe.com",
    "subject": "CN=stripe.com",
    "issuer": "C=US, O=Let's Encrypt, CN=E6",
    "valid_from": "2026-02-18T00:00:00.000Z",
    "valid_to": "2026-05-19T00:00:00.000Z",
    "days_until_expiry": 51,
    "serial": "04:A3:9B:7C:2D:1E:8F:00:5A:B2:C4:D6:E8:F0:12:34",
    "fingerprint": "A1:B2:C3:D4:E5:F6:78:90:AB:CD:EF:01:23:45:67:89",
    "san": ["stripe.com", "*.stripe.com"]
  }
}

La days_until_expiry Le champ est celui autour duquel vous créerez des alertes. Le san Le tableau affiche tous les domaines couverts par le certificat, utile pour vérifier que les certificats génériques incluent les sous-domaines que vous attendez.

Vérifiez la prise en charge HTTPS et les en-têtes de sécurité avec /v1/ssl

Savoir que votre certificat est valide ne suffit pas. Vous souhaitez également confirmer que la sécurité des en-têtes comme HSTS et CSP sont en place. Le /v1/ssl le point de terminaison gère cela.

curl -X POST https://api.botoi.com/v1/ssl \\
  -H "Content-Type: application/json" \\
  -d '{"domain": "stripe.com"}'

Réponse:

{
  "success": true,
  "data": {
    "domain": "stripe.com",
    "ssl_supported": true,
    "protocol": "TLSv1.3",
    "headers": {
      "strict-transport-security": "max-age=63072000; includeSubDomains; preload",
      "content-security-policy": "default-src 'self'; script-src 'self' js.stripe.com",
      "x-frame-options": "SAMEORIGIN",
      "x-content-type-options": "nosniff",
      "referrer-policy": "strict-origin-when-cross-origin"
    }
  }
}

La ssl_supported boolean confirme que HTTPS fonctionne. Le headers les surfaces d'objet HSTS, CSP, X-Frame-Options, X-Content-Type-Options et Referrer-Policy. Les en-têtes manquants indiquent des lacunes dans votre configuration de sécurité. Un protocole TLS inférieur à 1.2 est un drapeau rouge.

Actions GitHub : vérification SSL hebdomadaire avec des problèmes créés automatiquement

Ce workflow s'exécute tous les lundis, vérifie une liste de domaines et ouvre un problème GitHub si tout certificat expire dans les 30 jours. Créer .github/workflows/ssl-check.yml:

name: SSL Expiry Check

on:
  schedule:
    # Every Monday at 9:00 UTC
    - cron: '0 9 * * 1'
  workflow_dispatch:

jobs:
  check-ssl:
    runs-on: ubuntu-latest
    steps:
      - name: Check SSL certificates
        run: |
          DOMAINS=("stripe.com" "api.stripe.com" "dashboard.stripe.com")
          THRESHOLD=30
          FAILURES=""

          for DOMAIN in "\\\${DOMAINS[@]}"; do
            RESPONSE=\$(curl -s -X POST https://api.botoi.com/v1/ssl-cert/certificate \\
              -H "Content-Type: application/json" \\
              -d "{\\"domain\\": \\"\$DOMAIN\\"}")

            DAYS=\$(echo "\$RESPONSE" | jq -r '.data.days_until_expiry')
            ISSUER=\$(echo "\$RESPONSE" | jq -r '.data.issuer')
            EXPIRY=\$(echo "\$RESPONSE" | jq -r '.data.valid_to')

            echo "## \$DOMAIN" >> \$GITHUB_STEP_SUMMARY
            echo "- Expires: \$EXPIRY" >> \$GITHUB_STEP_SUMMARY
            echo "- Days left: \$DAYS" >> \$GITHUB_STEP_SUMMARY
            echo "- Issuer: \$ISSUER" >> \$GITHUB_STEP_SUMMARY
            echo "" >> \$GITHUB_STEP_SUMMARY

            if [ "\$DAYS" -lt "\$THRESHOLD" ]; then
              FAILURES="\$FAILURES\\n- \$DOMAIN expires in \$DAYS days (\$EXPIRY)"
            fi
          done

          if [ -n "\$FAILURES" ]; then
            echo "::error::Certificates expiring within \$THRESHOLD days:\$FAILURES"
            exit 1
          fi

          echo "All certificates have more than \$THRESHOLD days remaining."

      - name: Open GitHub issue on failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'SSL certificate expiring soon',
              body: 'The weekly SSL check found certificates expiring within 30 days. See the [workflow run](' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId + ') for details.',
              labels: ['infrastructure', 'urgent']
            });

Le flux de travail parcourt chaque domaine, interroge l'API et accumule les échecs. Si tout certificat tombe en dessous du seuil de 30 jours, le travail échoue et crée un GitHub problème marqué infrastructure et urgent. Le résumé du travail montre le rapport complet pour chaque domaine.

Ajuster DOMAINS et THRESHOLD pour correspondre à votre configuration. Le gratuit Le niveau traite jusqu'à 100 requêtes par jour, ce qui couvre environ 14 domaines vérifiés chaque semaine.

Node.js : surveiller plusieurs domaines dans un script

Pour l'intégration dans votre propre pile de surveillance, voici un script Node.js qui vérifie un ensemble de domaines en parallèle et signale les certificats proches de l'expiration :

const DOMAINS = [
  "stripe.com",
  "api.stripe.com",
  "dashboard.stripe.com",
  "docs.stripe.com",
];

const THRESHOLD_DAYS = 30;

async function checkCert(domain) {
  const res = await fetch("https://api.botoi.com/v1/ssl-cert/certificate", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": process.env.BOTOI_API_KEY,
    },
    body: JSON.stringify({ domain }),
  });
  const { data } = await res.json();
  return { domain, ...data };
}

async function checkAll() {
  const results = await Promise.all(DOMAINS.map(checkCert));
  const expiring = results.filter(
    (r) => r.days_until_expiry < THRESHOLD_DAYS
  );

  console.log("SSL Certificate Report");
  console.log("=".repeat(50));

  for (const r of results) {
    const status =
      r.days_until_expiry < THRESHOLD_DAYS ? "WARNING" : "OK";
    console.log(
      \`[\\\${status}] \\\${r.domain} - \\\${r.days_until_expiry} days left (expires \\\${r.valid_to})\`
    );
  }

  if (expiring.length > 0) {
    console.log(
      \`\\n\\\${expiring.length} certificate(s) expiring within \\\${THRESHOLD_DAYS} days.\`
    );
  }

  return { results, expiring };
}

checkAll();

Exécutez-le selon une planification cron ou intégrez-le à votre pipeline de contrôle de santé existant. Le Promise.all l'appel vérifie tous les domaines simultanément, donc le total le temps d'exécution reste proche de la latence d'un seul appel d'API.

Alerte webhook Slack lorsqu'un certificat expire bientôt

Associez la vérification du certificat à un webhook entrant Slack pour avertir votre équipe lorsqu'un le certificat nécessite une attention particulière :

async function sendSlackAlert(domain, daysLeft, validTo) {
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;

  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: \`:rotating_light: SSL certificate for \\\${domain} expires in \\\${daysLeft} days\`,
      blocks: [
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: [
              "*SSL Certificate Expiry Warning*",
              \`Domain: \\\`\\\${domain}\\\`\`,
              \`Days remaining: *\\\${daysLeft}*\`,
              \`Expires: \\\${validTo}\`,
            ].join("\\n"),
          },
        },
      ],
    }),
  });
}

// After running the certificate check
async function checkAndAlert() {
  const DOMAINS = ["stripe.com", "api.stripe.com"];

  for (const domain of DOMAINS) {
    const res = await fetch(
      "https://api.botoi.com/v1/ssl-cert/certificate",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Api-Key": process.env.BOTOI_API_KEY,
        },
        body: JSON.stringify({ domain }),
      }
    );
    const { data } = await res.json();

    if (data.days_until_expiry < 30) {
      await sendSlackAlert(domain, data.days_until_expiry, data.valid_to);
    }
  }
}

checkAndAlert();

Le message inclut le domaine, les jours restants et la date d'expiration. Votre équipe voit le alerte dans Slack et peut enquêter avant l’expiration du certificat.

Audit de sécurité complet : combinez les deux points de terminaison

Pour une image complète, exécutez les deux points de terminaison en parallèle par domaine. Cela vous donne données d’expiration du certificat et couverture de l’en-tête de sécurité en un seul passage :

async function auditSsl(domain) {
  const [cert, ssl] = await Promise.all([
    fetch("https://api.botoi.com/v1/ssl-cert/certificate", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ domain }),
    }).then((r) => r.json()),

    fetch("https://api.botoi.com/v1/ssl", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ domain }),
    }).then((r) => r.json()),
  ]);

  const issues = [];

  // Certificate checks
  if (cert.data.days_until_expiry < 30) {
    issues.push(\`Certificate expires in \\\${cert.data.days_until_expiry} days\`);
  }

  // Security header checks
  const headers = ssl.data.headers || {};
  if (!headers["strict-transport-security"]) {
    issues.push("Missing HSTS header");
  }
  if (!headers["content-security-policy"]) {
    issues.push("Missing Content-Security-Policy header");
  }
  if (!headers["x-frame-options"]) {
    issues.push("Missing X-Frame-Options header");
  }

  return { domain, cert: cert.data, ssl: ssl.data, issues };
}

// Run audit across all domains
const domains = ["stripe.com", "api.stripe.com"];
Promise.all(domains.map(auditSsl)).then((results) => {
  for (const r of results) {
    console.log(\`\\n\\\${r.domain}:\`);
    console.log(\`  Certificate: \\\${r.cert.days_until_expiry} days left\`);
    console.log(\`  TLS: \\\${r.ssl.protocol}\`);
    console.log(\`  Issues: \\\${r.issues.length === 0 ? "None" : r.issues.join(", ")}\`);
  }
});

Ce script signale l'expiration du certificat, la version du protocole TLS et la sécurité manquante en-têtes pour chaque domaine. Ajoutez-le à votre examen de sécurité hebdomadaire ou connectez-le à votre tableau de bord de réponse aux incidents.

Points clés

  • /v1/ssl-cert/certificate renvoie l'émetteur, les dates de validité, days_until_expiry, numéro de série, empreinte digitale et liste SAN pour tout le certificat TLS du domaine.
  • /v1/ssl vérifie la prise en charge HTTPS, signale la version du protocole TLS et analyse HSTS, CSP, X-Frame-Options, X-Content-Type-Options et Referrer-Policy en-têtes.
  • Les deux points de terminaison fonctionnent de manière anonyme à 5 requêtes par minute. Passer un X-Api-Key en-tête pour des limites plus élevées.
  • Une tâche cron hebdomadaire GitHub Actions avec un seuil de 30 jours détecte les échecs de renouvellement avant qu'ils ne deviennent des pannes.
  • Combinez les deux points de terminaison pour exécuter la surveillance de l'expiration des certificats et l'en-tête de sécurité audits dans un seul script.

FAQ

Comment vérifier l'expiration du certificat SSL via l'API ?
Envoyez une requête POST à ​​https://api.botoi.com/v1/ssl-cert/certificate avec un corps JSON contenant le domaine. La réponse inclut les champs valid_from, valid_to etdays_until_expiry du certificat. Aucune CLI openssl ou inspection manuelle du navigateur n'est nécessaire.
Quelle est la différence entre /v1/ssl-cert/certificate et /v1/ssl ?
Le point de terminaison /v1/ssl-cert/certificate renvoie les détails du certificat : émetteur, sujet, numéro de série, dates de validité et jours jusqu'à l'expiration. Le point de terminaison /v1/ssl vérifie si HTTPS est pris en charge et analyse les en-têtes de sécurité tels que HSTS, CSP, X-Frame-Options et Referrer-Policy. Utilisez le premier pour la surveillance des expirations et le second pour les audits de sécurité.
Puis-je vérifier les certificats SSL sans clé API ?
Oui. L'accès anonyme autorise 5 requêtes par minute et 100 requêtes par jour avec une limitation de débit basée sur IP. Aucune inscription ni clé API requise. Pour un débit plus élevé, les forfaits payants commencent à 9 $/mois.
À quelle fréquence dois-je vérifier l’expiration du certificat SSL ?
Chaque semaine est une bonne base de référence pour la plupart des équipes. Les certificats Let's Encrypt se renouvellent tous les 90 jours avec renouvellement automatique 30 jours avant l'expiration. Une vérification hebdomadaire avec un seuil d'avertissement de 30 jours vous laisse suffisamment de temps pour corriger les échecs de renouvellement avant l'expiration du certificat.
Cela fonctionne-t-il avec des certificats auto-signés ou internes ?
L'API se connecte au domaine via l'Internet public et fonctionne donc avec n'importe quel certificat servi sur le port 443. Les certificats auto-signés renverront les détails du certificat, mais le champ de l'émetteur affichera l'entité auto-signée au lieu d'une autorité de certification de confiance.

Commencez a construire avec botoi

150+ endpoints API pour la recherche, le traitement de texte, la generation d'images et les utilitaires pour developpeurs. Offre gratuite, sans carte bancaire.