# Website Security & SSL Expiry Checker — Headers, HTTPS, A–F (`securityzenkai/website-security-checker`) Actor

Grade websites A–F on security headers, TLS certificate validity & expiry, HTTPS redirect and cookie flags; returns the single most important fix per site. One normal visit per site, no scanning.

- **URL**: https://apify.com/securityzenkai/website-security-checker.md
- **Developed by:** [Joshua Pole](https://apify.com/securityzenkai) (community)
- **Categories:** Developer tools, SEO tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 site checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Website Security & SSL Expiry Checker — Headers, HTTPS, A–F

**How secure does a website look from the outside?** Paste a list of domains and get an **A–F grade** per site — security headers, TLS certificate, HTTPS redirect, cookie flags and version leaks — plus the **one fix that matters most**, ready to hand to a developer.

✅ A–F grade + 0–100 score  ·  ✅ Expiring & broken certificates  ·  ✅ One prioritised fix per site  ·  ✅ 5,000 sites per run  ·  ✅ One normal visit per site — no scanning

***

### ⚡ See it in 5 seconds

```json
// input
{ "domains": ["acme-shop.com"] }
```

```json
// output
{
  "domain": "acme-shop.com",
  "grade": "D",
  "score": 52,
  "main_fix": "Renew the TLS certificate on acme-shop.com now — it expires on 2026-10-13. Enable auto-renewal.",
  "cert_days_left": 12,
  "issues": [
    "TLS certificate expires in 12 days",
    "Missing HSTS header",
    "No clickjacking protection (X-Frame-Options / frame-ancestors)",
    "Server header leaks version: Apache/2.4.41"
  ]
}
```

A grade your client understands, and a fix your developer can paste.

***

### 🎯 Who uses this

| You are | You use it to |
|---|---|
| **Web agency / web builder** | Check every site you host or built, and fix weak spots before a client — or an auditor — finds them. |
| **MSP / IT service provider** | Monthly hygiene report across all client sites; catch certificates before they expire. |
| **Security consultant** | Instant external baseline for an assessment or a proposal, with concrete remediation. |
| **Sales / lead generation** | Find companies with an F-grade website — a specific, verifiable reason to start a conversation. |
| **SEO / marketing teams** | HTTPS redirects and valid certificates affect trust signals and browser warnings. |

***

### 🔍 What it checks

| Area | Details |
|---|---|
| **TLS certificate** | Valid & trusted · expired · self-signed · hostname mismatch · **days until expiry** · issuer · negotiated TLS version |
| **HTTPS** | Site reachable over HTTPS · **HTTP → HTTPS redirect** |
| **Security headers** | `Strict-Transport-Security` (incl. max-age) · `Content-Security-Policy` · clickjacking protection (`X-Frame-Options` or CSP `frame-ancestors`) · `X-Content-Type-Options` · `Referrer-Policy` · `Permissions-Policy` |
| **Information leakage** | `Server` header with version number · `X-Powered-By` |
| **Cookies** | Cookies set on the landing page without `Secure`, `HttpOnly` or `SameSite` |

Input is forgiving: `https://www.acme-shop.com/contact`, `ACME-SHOP.COM` and `info@acme-shop.com` all work. Duplicates are removed.

***

### 🚦 Grades

| Grade | Score | Meaning |
|---|---|---|
| 🟢 **A** | 90–100 | Strong baseline. |
| 🟢 **B** | 80–89 | Good — a few missing headers. |
| 🟡 **C** | 65–79 | Basic protection, clear gaps. |
| 🟠 **D** | 50–64 | Weak — multiple important protections missing. |
| 🔴 **F** | 0–49 | Broken certificate, no HTTPS, or almost no protection. |
| ⚪ **blocked** | – | Bot protection (403/429/503) hid the real page — certificate fields still filled, **not billed**. |
| ⚪ **error** | – | Site not reachable or invalid — **not billed**. |

#### What weighs most

| Problem | Penalty |
|---|---|
| No working HTTPS | −60 |
| Invalid / expired certificate | −50 |
| TLS 1.0 / 1.1 negotiated | −30 |
| Certificate expires within 14 days | −20 |
| No HTTP → HTTPS redirect · missing HSTS · missing CSP | −15 each |
| No clickjacking protection | −10 |
| Certificate expires within 30 days · short HSTS · nosniff · Referrer-Policy · insecure cookies | −5 each |
| Version leaks · Permissions-Policy | −3 each |

`main_fix` always picks the highest-impact problem first: HTTPS → certificate → protocol → expiry → redirect → HSTS → CSP → clickjacking → the rest.

***

### 📊 Example: a portfolio run

**Dataset → Overview view:**

| site | grade | score | cert days left | main\_fix |
|---|---|---|---|---|
| old-portal.acme.com | 🔴 F | 0 | -31 ❌ | Replace the TLS certificate (certificate has expired) — browsers show a security warning. |
| contoso-bakery.nl | 🔴 F | 32 | 85 | Redirect http://contoso-bakery.nl to https://contoso-bakery.nl with a 301. |
| fabrikam.net | 🟠 D | 52 | 12 | Renew the TLS certificate now — it expires on 2026-10-13. Enable auto-renewal. |
| northwind.io | 🟡 C | 74 | 64 | Add header — Content-Security-Policy: default-src 'self'; frame-ancestors 'self' |
| litware.com | 🟢 B | 82 | 142 | Raise HSTS to: Strict-Transport-Security: max-age=31536000; includeSubDomains |
| adatum.eu | 🟢 A | 92 | 59 | Add header — Permissions-Policy: geolocation=(), camera=(), microphone=() |
| wingtip.co | ⚪ blocked | – | 58 | bot protection returned HTTP 403 — *not billed* |

**Key-value store → `SUMMARY`** — worst first, plus every certificate expiring within 30 days:

```json
{
  "sites": 7,
  "grades": { "A": 1, "B": 1, "C": 1, "D": 1, "F": 2, "blocked": 1 },
  "fix_first": [
    { "domain": "old-portal.acme.com", "grade": "F", "score": 0, "main_fix": "Replace the TLS certificate …" },
    { "domain": "contoso-bakery.nl", "grade": "F", "score": 32, "main_fix": "Redirect http:// … to https:// … with a 301." },
    { "domain": "fabrikam.net", "grade": "D", "score": 52, "main_fix": "Renew the TLS certificate now …" }
  ]
}
```

*(Example domains are fictional; the grades and fixes are real outputs of this Actor on real websites.)*

***

### 📥 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `domains` | string\[] | — | **Required.** Domains or URLs. Max 5,000 per run. |
| `concurrency` | integer | `10` | Sites checked in parallel (1–30). |
| `timeoutSecs` | integer | `15` | Per-request timeout. |

### 📤 Output fields

| Field | Description |
|---|---|
| `domain` | Normalised host name. |
| `grade` / `score` | `A`–`F` (or `blocked` / `error`) and 0–100. |
| `main_fix` | The single most important fix. |
| `issues` | Every problem found, in plain English. |
| `final_url` / `http_status` | Where the HTTPS visit ended up, and its status code. |
| `https_redirect` | Does `http://` redirect to `https://`? |
| `tls_version` | Negotiated protocol (e.g. `TLSv1.3`). |
| `cert_valid` · `cert_error` · `cert_issuer` · `cert_expires` · `cert_days_left` | Certificate trust, problem, issuer, expiry date and days left. |
| `headers_present` / `headers_missing` | Which security headers are (not) set. |
| `server` / `x_powered_by` | Technology headers, if exposed. |
| `insecure_cookies` | Cookies missing `Secure` / `HttpOnly` / `SameSite`. |
| `error` | Why a site could not be graded. |
| `checked_at` | UTC timestamp. |

Export as **CSV, Excel, JSON, XML or HTML**, or send it to Google Sheets, Zapier, Make or n8n.

***

### 🔌 Use it via API

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("securityzenkai/website-security-checker").call(
    run_input={"domains": ["acme-shop.com", "northwind.io"]}
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f'{row["domain"]}: {row["grade"]} — {row["main_fix"]}')
```

**JavaScript**

```javascript
import { ApifyClient } from "apify-client";

const client = new ApifyClient({ token: "YOUR_API_TOKEN" });
const run = await client.actor("securityzenkai/website-security-checker").call({
  domains: ["acme-shop.com", "northwind.io"],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.filter((r) => r.cert_days_left !== null && r.cert_days_left < 30)
  .forEach((r) => console.log(`${r.domain}: certificate expires in ${r.cert_days_left} days`));
```

**Schedule it weekly** in Apify and you have a free-standing certificate-expiry monitor for all your sites.

***

### 💰 Pricing

**$5 per 1,000 sites checked** ($0.005 per site). Unreachable sites and sites hidden behind bot protection are free. No subscription, no seats.

***

### 🛡️ Non-intrusive by design

Per site, this Actor does exactly what a browser does when someone types the address: **one HTTPS request, one HTTP request** (to see whether it redirects) and the normal TLS handshake. No port scans, no directory brute-forcing, no attack payloads, no forced legacy-protocol handshakes. Safe to run on any list of public websites.

### ⚠️ What it does not do

- **Not a vulnerability scanner.** It grades the visible security configuration, not the application code (no SQLi/XSS testing).
- **Only the landing page.** Headers and cookies are read from the page the domain lands on; other pages can differ.
- **No full TLS audit.** It reports the negotiated protocol, not every supported cipher or legacy protocol.
- **Bot protection can hide the real site.** Those sites come back as `blocked` (free) rather than with a misleading grade.
- **CSP quality isn't scored.** A present CSP counts; whether it's strict enough is up to you.

***

### ❓ FAQ

**Why is a missing HSTS header so important?**
Without HSTS, a visitor's first request can be silently downgraded to plain HTTP on a hostile network (public Wi-Fi), exposing logins and cookies.

**My site redirects to HTTPS in JavaScript — why "no redirect"?**
Only a server-side redirect (301/302) protects the first request. A JavaScript redirect happens after the insecure page is already loaded.

**What counts as clickjacking protection?**
Either `X-Frame-Options: DENY/SAMEORIGIN` or a CSP with `frame-ancestors`. Without one, your pages can be framed invisibly on a malicious site.

**Can I use this to monitor certificate expiry?**
Yes — schedule it weekly and filter on `cert_days_left < 30`, or read the `SUMMARY` record, which lists every certificate expiring within 30 days.

**Do I need API keys?**
No.

***

### 🔗 Related Actors

- **[Bulk Email Spoofing Checker](https://apify.com/securityzenkai/email-spoof-checker)** — check the same domains for SPF, DMARC and DKIM: can someone send email in their name?

# Actor input Schema

## `domains` (type: `array`):

Domains or URLs to check, one per line. Only the host is used — paths and email addresses are stripped.

## `concurrency` (type: `integer`):

How many sites are checked in parallel.

## `timeoutSecs` (type: `integer`):

Give up on a slow site after this many seconds.

## Actor input object example

```json
{
  "domains": [
    "github.com",
    "example.com"
  ],
  "concurrency": 10,
  "timeoutSecs": 15
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "github.com",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("securityzenkai/website-security-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "domains": [
        "github.com",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("securityzenkai/website-security-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "github.com",
    "example.com"
  ]
}' |
apify call securityzenkai/website-security-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,securityzenkai/website-security-checker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Pjxekefweq9WaDRLB/builds/8GKjIzT6nB0faJwUz/openapi.json
