# ssl.sh JSON API：证书检查、证书链、CT、CAA、HSTS 接口

> https://ssl.sh/api/v1/ 下的接口：检查 TLS 证书、获取完整证书链、查询证书透明度日志、CAA、HSTS、混合内容和证书解析。

Source: https://ssl.sh/zh/docs/api/
Last updated: 2026-11-10

ssl.sh 的 API 是 `https://ssl.sh/api/v1/` 下的一组只读 JSON 接口。用 GET 请求、把域名放在查询参数里即可，日常使用不需要注册。

## 认证和额度

- **匿名：**每个 IP 每分钟 30 次检查（令牌桶）。结果缓存 5 分钟（失败结果 1 分钟），命中缓存不计次数。
- **API Key：**请求头带 `Authorization: Bearer <key>`（或参数 `?key=<key>`），额度按套餐。Key 无效返回 `401 invalid_key`。
- **超出额度：**返回 `429 rate_limited`，`Retry-After` 头给出需要等待的秒数。
- 检查类接口加 `fresh=1` 跳过缓存（会计入次数）。

## 错误格式

所有错误都是同一个结构：

```json
{"error": "blocked_address", "message": "10.0.0.1 resolves to 10.0.0.1: private or reserved range 10.0.0.0/8"}
```

| 状态码 | `error` 取值 |
|---|---|
| 400 | `missing_host`、`invalid_host`、`invalid_port`、`port_not_allowed`、`invalid_starttls`、`private_key_rejected`、`parse_failed` |
| 401 | `invalid_key` |
| 403 | `blocked_address`（内网、回环、链路本地、100.64.0.0/10 或其他保留地址） |
| 404 | `not_found` |
| 413 | `too_large` |
| 429 | `rate_limited` |
| 502 | `dns_failed`、`connect_refused`、`connect_failed`、`timeout`、`tls_handshake_failed`、`protocol_mismatch`、`starttls_failed`、`ct_unavailable`、`fetch_failed` |

`/api/v1/check` 返回 502 时，响应体仍是完整的检查结果（含 `error`、`error_detail`、`message`），可以据此区分是 DNS 解析失败还是端口不通。`message` 目前是英文；中文说明在 `findings[].message_zh` 里。

## GET /api/v1/check

检查一个主机。参数：

| 参数 | 默认值 | 说明 |
|---|---|---|
| `host` | 必填 | 域名、`域名:端口` 或网址。 |
| `port` | 443 | 允许：443、8443、4443、9443、10443、465、587、25、993、995、143、110、21、990、636、853、5222、5432、6443、2083、2087、8883。 |
| `starttls` | 自动 | `smtp`、`imap`、`pop3`、`ftp`、`xmpp`、`postgres`。25、587、143、110、21、5222、5432 端口自动启用。 |
| `full` | 关 | `full=1` 再逐个测试 TLS 版本、HSTS、CAA 和吊销状态。 |
| `fresh` | 关 | `fresh=1` 跳过缓存。 |

```bash
curl -s 'https://ssl.sh/api/v1/check?host=example.com&full=1'
```

返回（节选）：

```json
{
  "host": "example.com",
  "port": 443,
  "ip": "203.0.113.10",
  "ok": true,
  "error": null,
  "tls_version": "TLSv1.3",
  "cipher": "TLS_AES_256_GCM_SHA384",
  "alpn": "h2",
  "protocols": {"TLSv1.0": false, "TLSv1.1": false, "TLSv1.2": true, "TLSv1.3": true},
  "chain": [
    {
      "subject_cn": "example.com",
      "sans": ["example.com", "www.example.com"],
      "issuer_cn": "R12",
      "issuer_org": "Let's Encrypt",
      "not_after": "2027-01-13T07:11:59Z",
      "days_left": 64,
      "key_type": "RSA 2048",
      "validation_level": "DV",
      "pem": "-----BEGIN CERTIFICATE-----\n…"
    }
  ],
  "chain_complete": true,
  "trusted": true,
  "hostname_match": true,
  "hsts": "max-age=31536000; includeSubDomains",
  "caa": ["0 issue \"letsencrypt.org\""],
  "revocation": "good",
  "findings": [
    {"level": "ok", "code": "valid", "message": "Certificate is valid and trusted, expires in 64 days.", "message_zh": "证书有效且受信任，64 天后过期。", "link": null}
  ],
  "grade": "A+",
  "days_left": 64,
  "fullchain_pem": "-----BEGIN CERTIFICATE-----\n…",
  "report_url": "https://ssl.sh/example.com"
}
```

字段说明：

- `chain` 是服务器实际发送的顺序；`aia_fetched` 是服务器没发、由 ssl.sh 按 AIA 下载的中间证书，它不为空时 `chain_complete` 为 `false`。
- `findings[].code` 供程序判断：`expired`、`not_yet_valid`、`expires_soon`、`hostname_mismatch`、`self_signed`、`untrusted_root`、`incomplete_chain`、`revoked`、`weak_key`、`sha1`、`tls10_enabled`、`tls11_enabled`、`weak_cipher`、`legacy_only`、`no_tls13`、`no_hsts`、`hsts_short`、`caa_missing`、`client_auth_eku`。`link` 是对应的解决办法页面（中文页面在前面加 `/zh`）。
- `grade` 为 `A+`、`A`、`B`、`C`、`F` 或 `T`（不受信任）；握手都没成功时为 `null`。
- `protocols`、`hsts`、`caa`、`revocation` 只在 `full=1` 时有值；`key_exchange_group` 取不到时为 `null`。

## GET /api/v1/chain

返回补齐后的证书链：`{host, port, trusted, chain_complete, root, pem_fullchain, certs: [...]}`。`pem_fullchain` 按验证路径排列（站点证书在前，中间证书在后，不含根证书）；`certs` 每项字段同上，另有 `source`：`server`（服务器发送）或 `aia`（补齐下载）。

```bash
curl -s 'https://ssl.sh/api/v1/chain?host=example.com' | jq -r .pem_fullchain > fullchain.pem
```

## GET /api/v1/ct

查询证书透明度日志，先查 crt.sh，失败时改查 Cert Spotter，结果缓存 6 小时。可以用来发现别人用你的域名申请的证书。

| 参数 | 默认值 | 说明 |
|---|---|---|
| `domain` | 必填 | 域名。 |
| `include_subdomains` | 1 | `0` 只查这个域名本身。 |
| `include_expired` | 0 | `1` 包含已过期的证书。 |

每条记录：`{id, issuer, not_before, not_after, dns_names, serial, source}`，按 `not_before` 倒序。

## GET /api/v1/caa

`?domain=example.com` 返回生效的 CAA 记录：按 RFC 8659 从该域名逐级往上找，第一个有记录的名字生效。返回 `{domain, found_at, records, issue, issuewild, iodef, any_ca_allowed}`。阿里云、腾讯云 DNSPod 的解析控制台都支持添加 CAA 记录。

## GET /api/v1/hsts

`?host=example.com` 请求 `https://example.com/`，返回 `{host, port, http_status, header, max_age, max_age_days, include_subdomains, preload_directive, preload_list}`。`preload_list` 是 hstspreload.org 接口给出的预加载状态（`preloaded`、`pending`、`unknown` 等），该服务无响应时为 `null`。

## GET /api/v1/mixed-content

`?url=https://example.com/page`（或 `?host=`）下载页面（最多 2 MB，跟随跳转），列出用 `http://` 加载的子资源：`{url, final_url, status, page_is_https, count, active, passive, insecure_forms, items: [{tag, attr, url, type}]}`。`active`（脚本、样式表、iframe）会被浏览器直接拦截，`passive`（图片、音视频）会让地址栏显示不安全。详见[混合内容警告](https://ssl.sh/zh/errors/mixed-content-warning/)。

## POST /api/v1/decode

在请求体里发送证书或 CSR，PEM 或 base64 DER 都可以（纯文本、JSON `{"pem": "…"}` 或表单字段 `pem`），最大 256 KB。只要内容里出现 `PRIVATE KEY` 就返回 `400 private_key_rejected`，不做任何处理。私钥不要发给任何在线工具。

```bash
curl -s https://ssl.sh/api/v1/decode --data-binary @cert.pem
```

证书返回 `{"type": "certificate", "count": n, "certificates": [...]}`，字段同 `chain`；CSR 返回 `{"type": "csr", "csr": {subject, subject_cn, sans, key_type, sig_alg, signature_valid}}`。
