# Security Headers Checker: Website Audit for CSP & HSTS (`hiver/website-security-headers-audit`) Actor

Bulk-check website security headers: CSP, HSTS, framing, cookies, HTTPS redirect, security.txt, with a grade per site and evidence for each finding. Bench: 21/21 policy checks vs 19/21, at $2 per 1,000 sites.

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

## Pricing

from $1.40 / 1,000 site auditeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Security Headers Checker: Website Audit for CSP & HSTS

**Grade a list of websites on public security hygiene in one run.** Use it to prioritise outreach for a security or web-agency service ("your site has no CSP and no HSTS"), to check your own sites or a client portfolio, or to add a hygiene score to vendor and prospect research.

**$2 per 1,000 sites audited**; sites that do not respond are free.

*Unofficial. Not affiliated with, endorsed by or sponsored by any company or website named in the results.*

### Measured against two other security-header Actors

Our board's Lab bench gives this Actor and two other security-header Actors on the Apify Store the same 8 sites in three jobs, then checks the answers against hand-verified facts (2026-10-08, build 0.1.5):

| | This Actor | psx/security-headers-tls-audit | ninhothedev/security-headers-checker |
|---|---|---|---|
| Plain vs hardened sites (HSTS, HTTPS redirect, findings) | 5 of 5 checks | 5 of 5 | 2 of 5 |
| CSP and HSTS policy correctness (weak max-age, unsafe-inline, ignored unsafe-inline under nonce/strict-dynamic, base-uri, subdomain coverage, cookies) | **11 of 11** | 9 of 11 | 3 of 11 |
| Sites behind bot protection | 5 of 5 | 5 of 5 | 3 of 5 |
| All checks matched | **21 of 21 (100%)** | 19 of 21 (90%) | 8 of 21 (38%) |
| Fields filled | 100% | 100% | 71% |
| Price per 1,000 sites in these runs, free plan | $2.02 | $9.88 | $1.02 |

Same coverage as psx at about a fifth of its price, and the only one of the three that got every policy check right. Three jobs and 8 sites are a small sample; the bench runs again after every new build.

### What you get

One row per site:

- `grade` (A to F) and `score` (0-100) from a transparent checklist
- `issues`: plain-language list of what is missing or weak
- `headers`: Strict-Transport-Security, Content-Security-Policy, X-Frame-Options or CSP frame-ancestors, X-Content-Type-Options, Referrer-Policy, Permissions-Policy. Entries retain `value`, `present`, `usable` and `detail`. `usable` is checklist effectiveness, not a guarantee of a safe policy.
- `policyAnalysis.csp`: parsed directives, effective script element/event-handler/eval sources, nonce/hash and strict-dynamic handling, duplicate-directive first-wins behavior, fetch fallback and the intersection of enforcing policies. `allowsUnsafeInline` means arbitrary inline script elements are permitted after these checks, not merely that the keyword exists. Report-only policies are shown separately and do not protect.
- `policyAnalysis.framing`: enforcing CSP frame-ancestors overrides X-Frame-Options, including a broad wildcard that makes XFO DENY ineffective. Neither frame-ancestors nor base-uri inherits default-src.
- `policyAnalysis.hsts`: parsed max-age, includeSubDomains and preload; flags missing subdomain coverage and a max-age below the optional one-year preload minimum. `preloadHeaderEligible` checks only the header, not the preload list, certificates or subdomain HTTPS. Preloading is opt-in, not mandatory.
- `findings`: stable code, severity (`info`, `low`, `medium`, `high`), message and `evidence: {header, value}` containing the exact observed header field. Missing headers have null evidence. CSP findings identify their policy/directive; an identified weakness mitigated by another enforcing policy is informational. Wildcards and HTTP sources are configuration review findings, not proof of exploitable resource loading.
- `httpProbe`: plain-HTTP final URL, status and redirect-chain evidence; a failed probe leaves the redirect result unknown, not false.
- `auditScope`: labels HTTP error-response audits explicitly; headers on a 403 are not proof of the origin application's policy.
- `httpVersion`: the negotiated response protocol. The client offers HTTP/2 with ordinary HTTP/1.1 fallback, identifies itself as HiverSecurityAudit, and does not retry access denials with a disguised user agent or another IP.
- `cookies`: each final-homepage-response Set-Cookie field, parsed Secure/HttpOnly/SameSite flags, session lifetime and structured findings. SameSite=None without Secure and invalid cookie prefixes are flagged; session-lifetime cookies without HttpOnly are flagged without assuming they are authentication cookies. Intentional client-side state may need JavaScript access. Raw `value` and evidence include cookie values: treat datasets as potentially sensitive and do not publish them indiscriminately. Redirect-response cookies and browser cookie acceptance outside these static checks are not audited.
- `httpToHttpsRedirect`, `versionLeaks` (server software versions exposed in headers)
- `robotsTxt`, `sitemapXml`, `securityTxt` (present or not) and the security.txt expiry date

### Policy example (real response, shortened)

One GET to https://www.bbc.co.uk/ with the Actor's unchanged identity on 2026-10-07 returned HTTP/2 200. Its script-src includes a nonce, strict-dynamic and unsafe-inline. The effective analysis correctly distinguishes an ignored keyword from permission to run arbitrary inline scripts:

```json
{
  "statusCode": 200,
  "httpVersion": "HTTP/2",
  "policyAnalysis": {
    "csp": {
      "allowsUnsafeInline": false,
      "unsafeInlineIgnored": true
    }
  }
}
```

This is a developer diagnostic, not a competitor benchmark. Results vary by response and location.

### Price

Pay per event: **$2 per 1,000 sites audited** (`site-audited`, $0.002). Sites that do not respond are not charged.

### Use it as a CI gate (free GitHub Actions template)

[hiver-data/security-headers-ci](https://github.com/hiver-data/security-headers-ci) runs this Actor with your own Apify token after each push to `main` and every Monday. It writes a grade table to the Actions job summary and fails the job when a site drops below your minimum grade or loses a header you require, such as HSTS or CSP. One to five sites per run: five sites are 5 × $0.002 = $0.01 in `site-audited` events on the Free plan. The repo's tests replay real rows of this Actor through a local mock API, so you can try the gate without spending anything.

### What this is, and is not

- It reads the response headers and cookies of the home page, and requests `/`, plain HTTP `/`, `/robots.txt`, `/sitemap.xml` and `/.well-known/security.txt`. That is all: no scanning of ports or paths, no attack payloads, no login.
- The score is a simple header checklist (HSTS 20, CSP 25, framing 10, MIME sniffing 10, referrer 10, permissions 5, HTTPS redirect 5, minus points for weak cookies, short HSTS or a missing redirect). It is **not a penetration test or vulnerability scan** and says nothing about flaws in the application itself.
- Headers can differ by page, country or bot detection. Sites that block automated requests may show a thin result. A missing header is a finding to review, not proof of a vulnerability.
- Policy checks are static response-header analysis, not full browser-policy validation: they do not inspect HTML/meta CSP, validate nonce randomness, simulate every source URL intersection, test CSP bypasses or audit TLS configuration. Missing object-src is flagged only when no default-src fallback exists. The presence of a base/object restriction does not mean its allowlist is narrow.
- Semantics references: [CSP3](https://www.w3.org/TR/CSP3/), [Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie), [HSTS preload header requirements](https://hstspreload.org/).
- The Actor does not read the contact address inside security.txt; it only reports whether the file exists and when it expires.

### Input

- `urls`: domains or URLs, e.g. `github.com`
- `maxConcurrency` (default 5), `timeoutSecs` (default 15)

### Pricing table & example

Pay per event. Prices per 1,000 events, by Apify plan (higher plans get a discount automatically):

| Event | Free | Starter (Bronze) | Scale (Silver) | Business (Gold) |
|---|---|---|---|---|
| Site audited (`site-audited`) | $2.00 | $1.80 | $1.60 | $1.40 |

**Worked example:** 5,000 billable `site-audited` events on the Free plan cost 5,000 × $0.002 = **$10.00**; on Gold, 5,000 × $0.0014 = $7.00. Rows that the Price section lists as free cost nothing. Apify platform usage is included in these prices.

### Input example

```json
{
  "urls": [
    "github.com",
    "example.com"
  ]
}
```

### FAQ

**Is this a vulnerability scan?**\
No. It checks public HTTP response headers and hygiene files only. It does not probe for vulnerabilities.

**How is the grade calculated?**\
A fixed weighted checklist, described in this README; it is a hygiene score, not a security guarantee.

**Which pages are requested?**\
The homepage plus robots.txt, sitemap.xml and security.txt.

**Is this official?**\
No. Unofficial and not affiliated with, endorsed by or sponsored by any company or site named in the results or this README.

**Something looks wrong or you need a field added?**\
Open an issue from the Actor's Issues tab with the input and run link.

# Actor input Schema

## `urls` (type: `array`):

Domains or URLs, for example `github.com` or `https://example.org`. Only the home page and three well-known files are requested.

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

Sites processed at the same time (1-20).

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

Per-request timeout.

## Actor input object example

```json
{
  "urls": [
    "github.com",
    "stripe.com",
    "example.com",
    "wordpress.org"
  ],
  "maxConcurrency": 5,
  "timeoutSecs": 15
}
```

# Actor output Schema

## `results` (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 = {
    "urls": [
        "github.com",
        "stripe.com",
        "example.com",
        "wordpress.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hiver/website-security-headers-audit").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 = { "urls": [
        "github.com",
        "stripe.com",
        "example.com",
        "wordpress.org",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("hiver/website-security-headers-audit").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 '{
  "urls": [
    "github.com",
    "stripe.com",
    "example.com",
    "wordpress.org"
  ]
}' |
apify call hiver/website-security-headers-audit --silent --output-dataset

```

## MCP server setup

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

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/6asaTf4f45wfjfjFJ/builds/kJccFzAcLTiIUNMec/openapi.json
