# SSL Certificate Expiry & Security Headers Checker (`velveteen_driver/website-security-headers-checker`) Actor

Check many websites at once: SSL/TLS certificate expiry, issuer and trust, HTTP to HTTPS redirect, HSTS, CSP, X-Frame-Options and other security headers, cookie flags, with an A-F grade. Passive homepage check only, built for agencies monitoring client sites.

- **URL**: https://apify.com/velveteen_driver/website-security-headers-checker.md
- **Developed by:** [Elliott](https://apify.com/velveteen_driver) (community)
- **Categories:** Developer tools, SEO tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 website 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

## SSL Certificate Expiry & Security Headers Checker

Check the HTTPS set-up of many websites in one run. For each website's homepage you get:

- **SSL/TLS certificate**: trusted or not (and why), issuer, names covered, valid from / expires, **days left**, TLS version and cipher
- **Redirects**: whether `http://` redirects to `https://`, with the full redirect chain (up to 5 hops)
- **Security headers**: Strict-Transport-Security (max-age, includeSubDomains, preload), Content-Security-Policy (and
  report-only), CSP frame-ancestors, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy,
  Cross-Origin-Opener-Policy, plus the `Server` and `X-Powered-By` values
- **Cookie flags**: each cookie's name with its Secure, HttpOnly and SameSite flags (cookie values are never read or stored)
- **Grade A to F** and a score out of 100, with a plain list of issues

### Use it for

- **Agencies and freelancers**: schedule a weekly run over your client sites and catch certificates that expire in under
  30 days before the client notices.
- **IT and web teams**: a quick header hygiene report across all your domains.
- **Pre-launch checks**: confirm a new site redirects to HTTPS and sends HSTS and the basic security headers.

### Example input

```json
{
  "websites": ["example.com", "https://www.yourclient.co.uk"],
  "maxConcurrency": 5
}
```

### Output fields

`input`, `host`, `finalUrl`, `grade`, `score`, `issues`, `error`, `note`, `httpsWorks`, `httpRedirectsToHttps`,
`httpsRedirects`, `httpRedirects`, `statusCode`, `hsts`, `hstsMaxAge`, `hstsIncludeSubdomains`, `hstsPreload`, `csp`,
`cspReportOnly`, `cspFrameAncestors`, `xFrameOptions`, `xContentTypeOptions`, `referrerPolicy`, `permissionsPolicy`,
`crossOriginOpenerPolicy`, `server`, `poweredBy`, `cookies`, `tlsVersion`, `cipher`, `certValid`, `certError`,
`certIssuer`, `certIssuerOrg`, `certSubject`, `certNames`, `certNotBefore`, `certExpires`, `certDaysLeft`, `checkedAt`.

Inputs that point to the same host are checked and charged once: `example.com`, `EXAMPLE.com/page` and
`http://example.com:443` are one website (`www.example.com` is a different host). Each website in the output is one charged event (`site-checked`), including sites that return an `error`
(for example "the domain does not resolve").

### How the grade works

The score starts at 100 and loses points for: HTTPS not working (F), an untrusted or expired certificate, a certificate
expiring within 30 days, TLS 1.0/1.1, no HTTP to HTTPS redirect, no HSTS (or max-age under 180 days), no CSP, no
clickjacking protection, no `nosniff`, no Referrer-Policy, no Permissions-Policy, cookies without Secure, and an
`X-Powered-By` header. A = 90+, B = 75+, C = 60+, D = 45+, E = 30+, F below 30.

The grade is a header and configuration best-practice grade, based on the [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/) and [MDN's HTTP security guidance](https://developer.mozilla.org/en-US/docs/Web/Security). It is a quick hygiene indicator from what the server returns. **It is not a vulnerability scan, penetration test
or security guarantee**: a site can score A and still be insecure, and a low score doesn't mean a site has been breached.

### Exactly what the actor does per website

1. Resolves the domain and refuses private, loopback, link-local and reserved addresses. Every connection (including
   each redirect hop and robots.txt) is opened to an address that passed this check, so a DNS answer that changes
   mid-run can't redirect a request to a private network. Only ports 80 and 443 are used, environment proxies are
   ignored, and credentials in redirect URLs are stripped.
2. One TLS handshake on port 443 to read the certificate (IPv4 first; a second, unverified handshake only when the
   certificate isn't trusted, so its details can still be shown).
3. Reads `/robots.txt` once per host with its own User-Agent. If robots.txt disallows `/` for this checker or for all
   robots, the homepage is **not** fetched and the row says so (the certificate is still reported).
4. A GET of `https://host/` and of `http://host/`, following redirects by hand (max 5 hops, each hop's host also checked
   against its robots.txt for that path). Only the response headers are read; the page body is never downloaded or stored.

Nothing else: no other paths, no crawling, no port scans, no probing for files or admin pages, no payloads, no logins or
forms, no proxies. If a site answers 401/403/429/503 or shows a bot check, that answer is reported as it is (headers shown, no grade) and never worked around.
A path in your input (`https://site.com/page`) is ignored: only the homepage is checked. IP addresses are not accepted (domain names only), and redirects to a bare IP address are not followed.
A `Crawl-delay` in robots.txt is honoured between requests to the same host (up to 2 minutes; a site asking for longer gets only the first request).

User-Agent: `Mozilla/5.0 (compatible; SecurityHeadersCheck/1.0; +https://apify.com/velveteen_driver/website-security-headers-checker)`

### Limits

- Up to 1,000 websites per run; up to 20 different websites are checked at the same time (default 5). Requests to any one
  host are made one at a time, including redirect targets shared by several inputs. Not for load testing or uptime pinging.
- Headers are read from the homepage only; other pages of a site may send different headers.
- Sites that block automated visitors in robots.txt get a certificate result only.

### Data source and responsible use

- The only data source is the websites you supply: the certificate and response headers each server sends to any visitor.
  No third-party databases or APIs are used.
- Only submit websites you own, manage or have permission to assess, and comply with the law that applies to you. Use the
  results to fix or advise, not to publicly name or shame site owners.
- Any future feature that finds hosts by itself (subdomain discovery, certificate-transparency lists, bulk internet-wide
  lists) is out of scope for this actor.
- Certificates are reported as the server presents them. The actor stores no page content and no cookie values.

# Actor input Schema

## `websites` (type: `array`):

One per line: example.com or https://www.example.com. Only the homepage of each site is checked. Up to 1,000 per run. Only submit sites you own, manage or have permission to assess.

## `maxConcurrency` (type: `integer`):

How many different websites are checked in parallel. Each website gets at most a few requests, one at a time.

## Actor input object example

```json
{
  "websites": [
    "example.com"
  ],
  "maxConcurrency": 5
}
```

# Actor output Schema

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

All results in the default dataset, shown in the overview table.

# 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 = {
    "websites": [
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("velveteen_driver/website-security-headers-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 = { "websites": ["example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("velveteen_driver/website-security-headers-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 '{
  "websites": [
    "example.com"
  ]
}' |
apify call velveteen_driver/website-security-headers-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,velveteen_driver/website-security-headers-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/8EQl1syKbZZeew2qE/builds/5WnNeW1Ij9S1Giid2/openapi.json
