跳转到内容
Integration

使用 REST API 监控 SSL 证书过期

| 6 min read

检查具有两个 API 端点的任何域的 SSL 证书到期日期、颁发者和安全标头。 包括 GitHub Actions、Node.js 和 Slack 警报示例。

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

过期的 SSL 证书会使您的网站脱机,并显示令人恐惧的浏览器警告 远去的顾客。 Let's Encrypt 证书自动续订,但 DNS 配置错误,cron 作业失败, 忘记手动证书仍然会导致中断。 您需要一种方法来监控过期时间 您所有域的日期。

botoi API 为此提供了两个端点。 一个返回证书详细信息(颁发者、 有效期、到期日)。 另一个检查 HTTPS 支持并扫描安全性 标头。 它们共同涵盖过期监控和安全态势审计。

使用 /v1/ssl-cert/certificate 获取证书详细信息

此端点连接到域,读取 TLS 证书,并返回结构化的 您可以用任何语言解析的数据。

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

回复:

{
  "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"]
  }
}

days_until_expiry 字段是您将围绕其构建警报的字段。 这 san 数组显示证书涵盖的所有域,对于验证很有用 通配符证书包含您期望的子域。

使用 /v1/ssl 检查 HTTPS 支持和安全标头

仅仅知道您的证书有效还不够。 您还想确认安全性 HSTS 和 CSP 等标头已就位。 这 /v1/ssl 端点处理这个问题。

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

回复:

{
  "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"
    }
  }
}

ssl_supported 布尔值确认 HTTPS 有效。 这 headers 对象表面 HSTS、CSP、X-Frame-Options、X-Content-Type-Options 和 Referrer-Policy。 缺少标头表明您的安全配置存在缺陷。 1.2 以下的 TLS 协议 是一个危险信号。

GitHub Actions:每周检查自动创建的问题

此工作流程每周一运行,检查域列表,并在以下情况下打开 GitHub 问题: 任何证书都会在 30 天内过期。 创造 .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']
            });

工作流循环遍历每个域、查询 API 并累积失败。 如果 任何证书低于 30 天阈值,作业都会失败并创建 GitHub 问题已标记 infrastructureurgent。 工作总结显示 每个域的完整报告。

调整 DOMAINSTHRESHOLD 以匹配您的设置。 自由的 层每天最多处理 100 个请求,涵盖每周检查的约 14 个域。

Node.js:监控脚本中的多个域

为了集成到您自己的监控堆栈中,这里有一个 Node.js 脚本,用于检查 并行的域数组并标记证书即将到期:

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();

按 cron 计划运行它或将其集成到现有的运行状况检查管道中。 这 Promise.all 调用同时检查所有域,因此总计 执行时间接近单个 API 调用的延迟。

证书即将过期时 Slack Webhook 发出警报

将证书检查与 Slack 传入 Webhook 配对,以便在出现问题时通知您的团队 证书需要注意:

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();

该消息包括域、剩余天数和到期日期。 您的团队看到 Slack 中发出警报,可以在证书过期之前进行调查。

全面的安全审计:结合两个端点

为了获得完整的画面,请按域并行运行两个端点。 这给你 一次传递证书过期数据和安全标头覆盖范围:

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(", ")}\`);
  }
});

此脚本报告证书到期、TLS 协议版本和缺失的安全性 每个域的标头。 将其添加到您的每周安全审查中或将其连接到您的 事件响应仪表板。

要点

  • /v1/ssl-cert/certificate 返回发行人、有效期、 days_until_expiry、序列号、指纹和任何 SAN 列表 域的 TLS 证书。
  • /v1/ssl 检查 HTTPS 支持,报告 TLS 协议版本,以及 扫描 HSTS、CSP、X-Frame-Options、X-Content-Type-Options 和 Referrer-Policy 标头。
  • 两个端点均以每分钟 5 个请求的速度匿名工作。 通过一个 X-Api-Key 更高限制的标头。
  • 具有 30 天阈值的每周 GitHub Actions cron 作业会捕获续订失败 在停电之前。
  • 组合两个端点以运行证书过期监控和安全标头 在单个脚本中进行审核。

FAQ

如何通过 API 检查 SSL 证书是否过期?
使用包含域的 JSON 正文向 https://api.botoi.com/v1/ssl-cert/certificate 发送 POST 请求。 响应包括证书的 valid_from、valid_to 和 days_until_expiry 字段。 无需 openssl CLI 或手动浏览器检查。
/v1/ssl-cert/certificate 和 /v1/ssl 有什么区别?
/v1/ssl-cert/certificate 端点返回证书详细信息:颁发者、主题、序列号、有效期和到期天数。 /v1/ssl 端点检查是否支持 HTTPS 并扫描 HSTS、CSP、X-Frame-Options 和 Referrer-Policy 等安全标头。 使用第一个进行过期监控,第二个用于安全审核。
我可以在没有 API 密钥的情况下检查 SSL 证书吗?
是的。 匿名访问允许每分钟 5 个请求和每天 100 个请求,并具有基于 IP 的速率限制。 无需注册或 API 密钥。 如需更高吞吐量,付费计划起价为 9 美元/月。
我应该多久检查一次 SSL 证书过期情况?
对于大多数团队来说,每周是一个很好的基准。 Let's Encrypt 证书每 90 天更新一次,并在到期前 30 天自动更新。 每周检查一次,警告阈值为 30 天,让您有足够的时间在证书过期之前修复续订失败。
这适用于自签名或内部证书吗?
API 通过公共互联网连接到域,因此它可以与端口 443 上提供的任何证书配合使用。自签名证书将返回证书详细信息,但颁发者字段将显示自签名实体而不是受信任的 CA。

开始使用 botoi 构建

150+ 个 API 端点,涵盖查询、文本处理、图片生成和开发者工具。免费套餐,无需信用卡。