# Website Security Monitor (`vizellie/website-security-monitor`) Actor

Checks TLS, HTTPS redirects, security headers, mixed content, cookie flags and security.txt on public websites. One row per site with a score and a plain fix for each finding. $0.005 per site, and $0.02 for an optional client report. Not a penetration test or a security certification.

- **URL**: https://apify.com/vizellie/website-security-monitor.md
- **Developed by:** [Vi Zellie](https://apify.com/vizellie) (community)
- **Categories:** Developer tools, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $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 Monitor

Check the transport and header setup of public websites. Each site gets one row: a score from 0 to 100, a letter grade, and a short list of fixes in plain language.

Not legal advice; general information only.

This flags likely issues. It is not a security certification or a penetration test.

### What you get

For each public website:

- TLS certificate: who issued it, how many days are left, whether the name matches, whether the chain is trusted, and which TLS versions the server accepts.
- Whether HTTP sends visitors to HTTPS.
- Security headers: HSTS (including max-age and preload), Content-Security-Policy, framing protection, `X-Content-Type-Options`, `Referrer-Policy`, and `Permissions-Policy`.
- Whether the home page loads scripts, frames, images or media over plain HTTP.
- Cookie flags only: `Secure`, `HttpOnly`, and `SameSite`, listed by cookie name. Cookies set by JavaScript after the page loads are not seen, because this check does not run a browser.
- Whether `/.well-known/security.txt` is present, and whether its expiry date has passed.

A certificate that expires within **Certificate expiry warning** days is called out, so a weekly schedule still leaves time to renew. The default is 21 days.

Unreachable sites are stored with no score and are not charged. Private addresses, internal names, and URLs on a port other than 80 or 443 are skipped and are not charged. The summary counts every URL you supplied, how many were accepted, and how many of those unsafe URLs were skipped. A run whose addresses are all private still finishes, with nothing checked. A public site that redirects to a private address is stored with that finding and is not charged.

### What is not collected

Cookie values are never saved. Page text and HTML are not saved. IP addresses are not saved. Query strings and fragments are not saved. `security.txt` contact addresses are not saved. Header checks keep the graded facts, not the raw policy text.

### How to use it

1. Open the Actor and paste one or more public website URLs under **Website URLs**. Use a full URL such as `https://example.com/`.
2. Leave **Certificate expiry warning** at 21 for a weekly run, or raise it if you want earlier notice.
3. Set **Max sites** if you want a hard cap on the bill.
4. Start the run. Open the dataset for one row per site, or the **REPORT** record in the key-value store for the same findings as a page.

### Client-ready report

Use this when you want one file to send to a client. It is off by default. Off does not add a file and does not add a fee.

1. Paste the websites you want to check.
2. Set **Client-ready report** to **HTML**.
3. Run the Actor.
4. Open the Output tab. **Client report (HTML)** is `REPORT.html` in the key-value store. **Client report summary** lists that key.

The page has a cover with the site, the date and an overall grade, a short summary in plain language, and a table of findings. Each finding says what it means and how to fix it. A run with more than one site has a section per site. Pass, warning and fail stay readable in black and white. The footer says Generated with Website Security Monitor.

The report fee is charged once per run, and only after the file is saved. A failed save is not charged. The dataset and the existing `REPORT` summary stay included in the site fee. This Actor saves HTML. It does not save a PDF.

#### Weekly monitor

In the Apify console, create a schedule for this Actor (for example every Monday) and save the input with your site list. Each run charges the start fee plus one site fee for every site that answered. A site that is down is listed and is free, so the schedule can show an outage without a site fee.

### Pricing

You pay a small fee when the run starts, plus a fee for each public website that could be checked.

| Event | Price |
| --- | --- |
| Run start | $0.00005 |
| Site checked | $0.005 |
| Client-ready report | $0.02 |

The site fee covers the certificate, the header check, the home page and `security.txt`. Unreachable sites are not charged. Private addresses are not charged. The client-ready report is charged once per run, only when that file is saved. Leave **Client-ready report** on Off and that fee is not charged. Add $0.02 to any example below when the report is saved.

**Examples**

- 1 site: $0.00505
- 10 sites: $0.05005
- A run where every site is down or private: $0.00005

### Input

| Field | Default | What it does |
| --- | --- | --- |
| Website URLs | empty | Public sites to check. One row each. |
| Certificate expiry warning (days) | 21 | Flag certificates that expire soon. |
| Max sites | 10 | Cap how many sites one run will check. |
| Concurrency | 2 | How many sites to check at the same time. |

### Output

The dataset has one item per site. The overview table shows the score, the grade, days until the certificate expires, whether HTTP redirects to HTTPS, whether HSTS is set, and whether `security.txt` was found. The item also lists the fixes, highest impact first.

The key-value store record `REPORT` is an HTML page of the same findings. `OUTPUT` is the summary used to build that page.

### Limits

Up to 50 websites in one run. Each request times out, redirects are capped, and response bodies are capped. Only `http` and `https` URLs on ports 80 and 443 are accepted.

### Disclaimer

These findings flag likely issues. This is not a security certification or a penetration test. A high score does not mean the site is safe against every attack.

### Next additions

- Agency white-label logo
- Dutch report

# Actor input Schema

## `startUrls` (type: `array`):

Public websites to check, for example https://example.com/. One result is returned per website. Private and internal addresses are skipped and are not charged.

## `expiryWarningDays` (type: `integer`):

Flag a certificate that expires within this many days. The default of 21 suits a weekly schedule, so a renewal still has time after the next run.

## `maxSites` (type: `integer`):

Maximum number of websites to check in one run. Keeps the cost predictable.

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

How many websites to check at the same time. The default is 2.

## `report` (type: `string`):

Save an HTML report you can hand to a client. Off is the default: the run, the dataset and the price stay as they are today. The report fee is charged once per run, and only after the file is saved.

## Actor input object example

```json
{
  "startUrls": [
    "https://example.com/"
  ],
  "expiryWarningDays": 21,
  "maxSites": 10,
  "concurrency": 2,
  "report": "off"
}
```

# Actor output Schema

## `sites` (type: `string`):

One dataset row per site.

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

The OUTPUT record in the default key-value store.

## `report` (type: `string`):

The REPORT record in the default key-value store.

## `clientReport` (type: `string`):

The REPORT.html record. Saved when Client-ready report is HTML.

## `clientReportSummary` (type: `string`):

The CLIENT_REPORT record, with the report key.

# 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 = {
    "startUrls": [
        "https://example.com"
    ],
    "expiryWarningDays": 21,
    "maxSites": 10,
    "concurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("vizellie/website-security-monitor").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 = {
    "startUrls": ["https://example.com"],
    "expiryWarningDays": 21,
    "maxSites": 10,
    "concurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("vizellie/website-security-monitor").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 '{
  "startUrls": [
    "https://example.com"
  ],
  "expiryWarningDays": 21,
  "maxSites": 10,
  "concurrency": 2
}' |
apify call vizellie/website-security-monitor --silent --output-dataset

```

## MCP server setup

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

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/LjFSkK1FDLkvi5zx9/builds/DTzb24DfcjqjfgypY/openapi.json
