# HTTP Security Headers Audit & Grade (`webintel/security-headers-audit`) Actor

Bulk-audit websites' HTTP security headers: HSTS, CSP, clickjacking, cookies, CORS, info leaks and HTTPS redirects. Get an A+ to F grade per site with prioritized, copy-paste fixes.

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

## Pricing

from $2.00 / 1,000 sites

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

## HTTP Security Headers Audit & Grade

![HTTP Security Headers Audit & Grade](https://api.apify.com/v2/key-value-stores/21SBwDwdNtlnapIdO/records/security-headers-audit.png?v=a948e344)

Audit the HTTP security headers of hundreds of websites in one run. Each site gets an **A+ to F grade**, a **0–100 score**, a pass/warn/fail result for every check, and a **prioritized list of fixes with copy-paste header examples**.

- 🔒 **Checks 20+ items**: HSTS, Content-Security-Policy (directive by directive), X-Frame-Options / CSP frame-ancestors, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, COOP / COEP / CORP, deprecated headers, information leaks, cookies, CORS, HTTP→HTTPS redirect, HTTPS availability and mixed content.
- 🧠 **Real CSP analysis**, not just "present or missing". It flags `'unsafe-inline'`, `'unsafe-eval'`, wildcard and `https:` script sources, a missing `default-src`, `object-src`, `base-uri` and `frame-ancestors`, and report-only policies that are not enforced. Nonce- and hash-based `'strict-dynamic'` policies are recognised as strict.
- 🍪 **Cookie audit**: Secure, HttpOnly and SameSite flags for every `Set-Cookie` header (including cookies set on redirects), plus `__Host-` and `__Secure-` prefix rules.
- 🛠️ **Actionable output**: every finding comes with a recommendation, and the recommendations are sorted high → medium → low with an example header value.
- 🔁 **Redirect chain** recorded hop by hop. Plain domains are tried over HTTPS first, then HTTP.
- ⚡ Fast and lightweight: HTTP requests only (no browser) and one request per site, plus one for the redirect check.
- 💸 **Failed sites are free.** Invalid URLs, DNS failures, unreachable hosts and 5xx responses are not charged.

### Use cases

- **Agencies and MSPs**: run a monthly security-header report across every client website.
- **Pentest and compliance teams** (ISO 27001, SOC 2, PCI DSS, OWASP ASVS): run quick baseline checks before a deeper assessment.
- **Vendor risk and third-party assessment**: grade the public web posture of suppliers in bulk.
- **DevSecOps**: schedule the Actor and alert when a grade drops after a deploy.
- **Sales prospecting** for security services: find sites with an F grade and include concrete fixes in your outreach.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array of strings | – | Websites to audit. Plain domains (`example.com`) or full URLs. Duplicates are removed. Invalid entries are reported as failed items and are free. |
| `followRedirects` | boolean | `true` | Follow up to 10 redirects and audit the final page. The redirect chain is always recorded. |
| `checkHttpToHttps` | boolean | `true` | Also request `http://` to verify that it redirects to HTTPS. For `http://` inputs, this also checks whether HTTPS is available. |
| `concurrency` | integer | `10` | Number of websites audited in parallel (1–50). |

```json
{ "urls": ["https://github.com", "example.com", "https://www.mozilla.org"] }
```

### Output

The Actor outputs one dataset item per website. The Console offers these views: **Grades overview**, **All checks** (one row per check), **Recommendations** and **Cookies**. This example is trimmed from a real run:

```json
{
  "url": "https://www.mozilla.org/",
  "finalUrl": "https://www.mozilla.org/en-US/",
  "statusCode": 200,
  "https": true,
  "httpsAvailable": true,
  "httpRedirectsToHttps": true,
  "redirectChain": [
    { "url": "https://www.mozilla.org/", "statusCode": 302, "location": "https://www.mozilla.org/en-US/" },
    { "url": "https://www.mozilla.org/en-US/", "statusCode": 200, "location": null }
  ],
  "grade": "B",
  "score": 78,
  "checks": [
    { "id": "hsts", "header": "Strict-Transport-Security", "present": true, "value": "max-age=31536000",
      "status": "warn", "message": "HSTS is set but without includeSubDomains.",
      "recommendation": "Add includeSubDomains once all subdomains support HTTPS.", "scoreImpact": -2 },
    { "id": "csp", "header": "Content-Security-Policy", "present": true, "value": "script-src 'self' 'unsafe-eval' 'unsafe-inline' … ; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; …",
      "status": "fail",
      "message": "CSP present but weak — script-src: 'unsafe-inline' allows inline scripts, defeating most XSS protection. script-src: 'unsafe-eval' allows eval() and similar.",
      "recommendation": "Tighten the policy: use nonces/hashes with 'strict-dynamic' instead of 'unsafe-inline'/host allowlists, and set object-src 'none' and base-uri 'none'.",
      "scoreImpact": -15 }
  ],
  "missingHeaders": ["Permissions-Policy"],
  "leakedInfo": [],
  "cookies": [],
  "mixedContent": { "count": 0, "examples": [] },
  "recommendations": [
    { "priority": "high", "header": "Content-Security-Policy", "recommendation": "Tighten the policy: …",
      "example": "Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; form-action 'self'" },
    { "priority": "low", "header": "Permissions-Policy", "recommendation": "Disable browser features the site does not use.",
      "example": "Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()" }
  ],
  "responseHeaders": { "…": "all response headers of the final page" },
  "success": true,
  "error": null
}
```

Cookie entries look like this: `{ "name": "PHPSESSID", "secure": false, "httpOnly": false, "sameSite": null, "issues": ["Missing Secure flag …", "Missing HttpOnly …", "No SameSite attribute …"] }`.

Failed items have `success: false`, an `error` such as `"Domain not found (DNS lookup failed)"`, `"Invalid URL"` or `"HTTP 503"`, and are **not charged**.

### Scoring rubric

Every site starts at **100**. The deductions below are applied, and the score is clamped to 0–100. The rubric is similar in spirit to securityheaders.com and Mozilla Observatory, but it is fully transparent: each check's `scoreImpact` shows exactly what it cost.

| Check | Condition | Points |
|---|---|---|
| HTTPS | Page served over plain HTTP | −40 (grade capped at **F**) |
| HTTP → HTTPS | `http://` does not redirect to HTTPS | −15 |
| Strict-Transport-Security | Missing, invalid or `max-age=0` | −20 |
| | `max-age` < 15552000 (180 days) | −10 |
| | No `includeSubDomains` | −2 |
| Content-Security-Policy | Missing | −25 |
| | Only `Content-Security-Policy-Report-Only` | −20 |
| | Present but weak: no script restriction −15, `'unsafe-inline'` without nonce/hash −10, wildcard/`https:`/`data:`/`http:` script sources −10, `'unsafe-eval'` −5, no `default-src` −3, `object-src` not `'none'` −3, no `base-uri` −2 | up to −25 |
| Clickjacking | No `X-Frame-Options` (DENY/SAMEORIGIN) and no CSP `frame-ancestors`, or invalid value | −20 |
| | `ALLOW-FROM` (unsupported) | −10 |
| X-Content-Type-Options | Missing or not `nosniff` | −5 |
| Referrer-Policy | `unsafe-url` / `no-referrer-when-downgrade` −5, missing −3, `origin` / `origin-when-cross-origin` −2 | |
| Permissions-Policy | Missing | −5 |
| Cross-Origin-Opener-Policy | Missing or `unsafe-none` | −2 |
| COEP / CORP | Not set | 0 (info) |
| Deprecated headers | `X-XSS-Protection` other than `0` −2, `Public-Key-Pins` −2. `Expect-CT` and `Feature-Policy` are flagged (0). | |
| Information leaks | `Server` header with a version, `X-Powered-By`, `X-AspNet-Version`, `X-AspNetMvc-Version`, `X-Generator` | −3 each, max −10 |
| Cookies | Missing Secure (on HTTPS) −5, session/auth cookie without HttpOnly −5, missing SameSite −2, invalid prefix/SameSite −3 | max −15 |
| CORS | `Access-Control-Allow-Origin: *` with credentials −10, `null` origin −5 | |
| Mixed content | `http://` scripts, stylesheets or iframes on an HTTPS page | −10 |

**Grades:** **A+** ≥ 95 with no failed check · **A** ≥ 85 · **B** ≥ 70 · **C** ≥ 55 · **D** ≥ 40 · **E** ≥ 25 · **F** < 25 or no HTTPS.

### Pricing

Pay per event: you only pay for websites that were successfully audited.

| Event | Price |
|---|---|
| Site audited | **$0.002** per site ($2 per 1,000 sites) |
| Actor start | $0.00005 per run |

Invalid URLs, unreachable hosts, DNS failures and 5xx responses are **free**. If you set a maximum cost per run, the Actor audits only as many sites as fit within it.

### FAQ

**Is it the same as securityheaders.com or Mozilla Observatory?**
No. It is an independent tool with its own rubric (documented above). Grades are comparable in spirit but not identical. This Actor is not affiliated with securityheaders.com, Snyk or Mozilla.

**Which page is audited?**
The final page after redirects (unless `followRedirects` is off). Cookies set on intermediate redirects are included.

**Why did a site get a 403 but still a grade?**
Responses with status codes below 500 still carry the site's (or its CDN's) security headers, so they are audited. Check `statusCode` if a WAF is blocking the request.

**Is this a penetration test?**
No. The Actor makes ordinary, polite GET requests to public pages, similar to a browser visit. It does not attack or probe anything.

**What does mixed content detection cover?**
It is a quick HTML scan for `http://` scripts, stylesheets, iframes and objects on HTTPS pages. Content that JavaScript loads dynamically is not covered.

### Changelog

- **0.1**: Initial release. Grades, CSP/HSTS/cookie/CORS analysis, prioritized recommendations, redirect chain and HTTP→HTTPS check.

# Actor input Schema

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

Websites to audit, one per line. Plain domains like "example.com" (HTTPS is tried first) or full URLs. Duplicates are removed.

## `followRedirects` (type: `boolean`):

Follow up to 10 redirects and audit the headers of the final page. The full redirect chain is recorded either way.

## `checkHttpToHttps` (type: `boolean`):

Also request the plain http:// version of each site to verify it redirects to HTTPS (and whether HTTPS is available for http:// inputs).

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

How many websites are audited in parallel.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "github.com",
    "example.com"
  ],
  "followRedirects": true,
  "checkHttpToHttps": true,
  "concurrency": 10
}
```

# Actor output Schema

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

All audited websites (JSON): url, finalUrl, statusCode, https, redirectChain\[], grade, score, checks\[], missingHeaders\[], leakedInfo\[], cookies\[], recommendations\[], success, error.

## `grades` (type: `string`):

Grade, score, HTTPS status, missing headers and leaked info per website.

## `checks` (type: `string`):

One row per check per website (header, status, message, recommendation).

## `recommendations` (type: `string`):

Prioritized fixes per website, with example header values.

## `cookies` (type: `string`):

One row per cookie with Secure/HttpOnly/SameSite flags and issues.

# 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": [
        "https://apify.com",
        "github.com",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("webintel/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": [
        "https://apify.com",
        "github.com",
        "example.com",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webintel/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/CJ9RZieZpgasYf04l/builds/V0RjSBLB0dh8PLy4i/openapi.json
