Monitore a expiração do certificado SSL com uma API REST
Verifique as datas de expiração do certificado SSL, emissores e cabeçalhos de segurança para qualquer domínio com dois endpoints de API. Inclui exemplos de alertas do GitHub Actions, Node.js e Slack.
Um certificado SSL expirado deixa seu site offline e mostra um aviso no navegador que assusta clientes ausentes. Vamos criptografar a renovação automática de certificados, mas DNS mal configurado, cron jobs com falha, e certificados manuais esquecidos ainda causam interrupções. Você precisa de uma maneira de monitorar a expiração datas em todos os seus domínios.
A API botoi fornece dois endpoints para isso. Um retorna detalhes do certificado (emissor, datas de validade, dias até o vencimento). O outro verifica o suporte HTTPS e verifica a segurança cabeçalhos. Juntos, eles cobrem monitoramento de expiração e auditorias de postura de segurança.
Obtenha detalhes do certificado com /v1/ssl-cert/certificate
Este endpoint se conecta ao domínio, lê o certificado TLS e retorna dados estruturados dados que você pode analisar em qualquer idioma.
curl -X POST https://api.botoi.com/v1/ssl-cert/certificate \\
-H "Content-Type: application/json" \\
-d '{"domain": "stripe.com"}'
Resposta:
{
"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"]
}
}
O days_until_expiry campo é aquele em torno do qual você criará alertas. O
san array mostra todos os domínios que o certificado cobre, útil para verificar
que os certificados curinga incluem os subdomínios que você espera.
Verifique o suporte HTTPS e os cabeçalhos de segurança com /v1/ssl
Saber que seu certificado é válido não é suficiente. Você também deseja confirmar se a segurança
cabeçalhos como HSTS e CSP estão em vigor. O /v1/ssl endpoint lida com isso.
curl -X POST https://api.botoi.com/v1/ssl \\
-H "Content-Type: application/json" \\
-d '{"domain": "stripe.com"}'
Resposta:
{
"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"
}
}
}
O ssl_supported booleano confirma que o HTTPS funciona. O headers
superfícies de objeto HSTS, CSP, X-Frame-Options, X-Content-Type-Options e Referrer-Policy.
Cabeçalhos ausentes indicam lacunas na sua configuração de segurança. Um protocolo TLS abaixo de 1.2
é uma bandeira vermelha.
GitHub Actions: verificação semanal de SSL com problemas criados automaticamente
Este fluxo de trabalho é executado todas as segundas-feiras, verifica uma lista de domínios e abre um problema no GitHub se
qualquer certificado expira em 30 dias. Criar
.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']
});
O fluxo de trabalho percorre cada domínio, consulta a API e acumula falhas. Se
qualquer certificado ficar abaixo do limite de 30 dias, ele falha no trabalho e cria um GitHub
problema marcado infrastructure e urgent. O resumo do trabalho mostra
o relatório completo para cada domínio.
Ajustar DOMAINS e THRESHOLD para corresponder à sua configuração. O grátis
o nível lida com até 100 solicitações por dia, o que abrange cerca de 14 domínios verificados semanalmente.
Node.js: monitore vários domínios em um script
Para integração em sua própria pilha de monitoramento, aqui está um script Node.js que verifica uma matriz de domínios em paralelo e sinaliza certificados próximos da expiração:
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();
Execute isso em um cronograma cron ou integre-o ao seu pipeline de verificação de integridade existente.
O Promise.all chamada verifica todos os domínios simultaneamente, então o total
o tempo de execução permanece próximo à latência de uma única chamada de API.
Alerta de webhook do Slack quando um certificado expira em breve
Combine a verificação do certificado com um webhook de entrada do Slack para notificar sua equipe quando um certificado precisa de atenção:
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();
A mensagem inclui o domínio, os dias restantes e a data de validade. Sua equipe vê o alerta no Slack e pode investigar antes que o certificado expire.
Auditoria de segurança completa: combine os dois endpoints
Para uma visão completa, execute os dois endpoints em paralelo por domínio. Isso lhe dá dados de expiração de certificado e cobertura de cabeçalho de segurança em uma única passagem:
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(", ")}\`);
}
});
Este script relata a expiração do certificado, a versão do protocolo TLS e a falta de segurança cabeçalhos para cada domínio. Adicione-o à sua revisão de segurança semanal ou conecte-o ao seu painel de resposta a incidentes.
Pontos-chave
-
/v1/ssl-cert/certificateretorna o emissor, datas de validade,days_until_expiry, número de série, impressão digital e lista SAN para qualquer certificado TLS do domínio. -
/v1/sslverifica o suporte HTTPS, relata a versão do protocolo TLS e verifica HSTS, CSP, X-Frame-Options, X-Content-Type-Options e Referrer-Policy cabeçalhos. -
Ambos os endpoints funcionam anonimamente a 5 solicitações por minuto. Passe um
X-Api-Keycabeçalho para limites mais altos. - Um cron job semanal do GitHub Actions com um limite de 30 dias detecta falhas de renovação antes que se tornem interrupções.
- Combine os dois endpoints para executar o monitoramento de expiração de certificado e o cabeçalho de segurança auditorias em um único script.
FAQ
- Como verifico a expiração do certificado SSL via API?
- Envie uma solicitação POST para https://api.botoi.com/v1/ssl-cert/certificate com um corpo JSON contendo o domínio. A resposta inclui os campos valid_from, valid_to e days_until_expiry do certificado. Não é necessária CLI do openssl ou inspeção manual do navegador.
- Qual é a diferença entre /v1/ssl-cert/certificate e /v1/ssl?
- O endpoint /v1/ssl-cert/certificate retorna detalhes do certificado: emissor, assunto, número de série, datas de validade e dias até a expiração. O endpoint /v1/ssl verifica se HTTPS é compatível e verifica cabeçalhos de segurança como HSTS, CSP, X-Frame-Options e Referrer-Policy. Use o primeiro para monitoramento de expiração e o segundo para auditorias de segurança.
- Posso verificar certificados SSL sem uma chave API?
- Sim. O acesso anônimo permite 5 solicitações por minuto e 100 solicitações por dia com limitação de taxa baseada em IP. Nenhuma inscrição ou chave de API é necessária. Para maior rendimento, os planos pagos começam em US$ 9/mês.
- Com que frequência devo verificar a expiração do certificado SSL?
- Semanalmente é uma boa base para a maioria das equipes. Os certificados Let's Encrypt são renovados a cada 90 dias com renovação automática 30 dias antes do vencimento. Uma verificação semanal com limite de aviso de 30 dias oferece tempo suficiente para corrigir falhas de renovação antes que o certificado expire.
- Isso funciona com certificados autoassinados ou internos?
- A API se conecta ao domínio pela Internet pública, portanto funciona com qualquer certificado servido na porta 443. Os certificados autoassinados retornarão detalhes do certificado, mas o campo do emissor mostrará a entidade autoassinada em vez de uma CA confiável.
Comece a construir com botoi
150+ endpoints de API para consultas, processamento de texto, geração de imagens e utilitários para desenvolvedores. Plano gratuito, sem cartão de crédito.