How to Audit Security Headers with Python: Grade Any Website A+ to F

April 3, 2026 · 10 min read

Why Security Headers Matter (and Why Most Sites Fail)

HTTP security headers are directives that your web server sends to the browser, instructing it how to behave when handling your site's content. They are a first line of defense against entire classes of attacks: cross-site scripting, clickjacking, MIME sniffing, protocol downgrade attacks, and unauthorized feature access.

The six headers that matter most are:

Despite being straightforward to configure, the majority of websites fail basic security header checks. A 2025 analysis of the Alexa top 1 million sites found that fewer than 15% had a Content-Security-Policy header at all, and fewer than 5% had one strict enough to meaningfully prevent XSS. HSTS adoption is higher but still sits below 30% for most verticals outside banking and fintech.

For DevOps teams, security engineers, and compliance officers, the problem is not just setting these headers once. It is monitoring them continuously across every domain you manage, every third-party vendor you onboard, and every subdomain that spins up. Manual checks do not scale. You need automation.

The Manual Way: Checking Headers with Python + Requests

The simplest approach is to send an HTTP request and inspect the response headers. Python's requests library makes this a few lines of code:

import requests

url = "https://stripe.com"
resp = requests.get(url, timeout=10)

headers_to_check = [
    "Strict-Transport-Security",
    "Content-Security-Policy",
    "X-Frame-Options",
    "X-Content-Type-Options",
    "Referrer-Policy",
    "Permissions-Policy",
    "X-XSS-Protection",
    "Cross-Origin-Opener-Policy",
]

print(f"Security headers for {url}\n")
for header in headers_to_check:
    value = resp.headers.get(header, "MISSING")
    status = "PASS" if value != "MISSING" else "FAIL"
    print(f"  [{status}] {header}: {value[:80]}")

This gets you a quick spot-check. But the moment you need to go beyond presence/absence, the complexity explodes:

Building a reliable scoring engine on top of raw header parsing is hundreds of lines of code. And you have to maintain it as browser behavior and best practices evolve. This works for one site. But what about 100?

The API Way: One Call, Full Security Report

The DetectZeStack /security endpoint does the parsing, validation, and grading for you. One HTTP request returns a structured report with a letter grade, a numeric score, and detailed per-header test results.

Here is what a raw curl request looks like:

curl -s "https://detectzestack.p.rapidapi.com/security?url=stripe.com" \
  -H "x-rapidapi-key: YOUR_API_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com" | python3 -m json.tool

And here is the response you get back:

{
  "url": "https://stripe.com",
  "domain": "stripe.com",
  "grade": "A",
  "score": 100,
  "max_score": 130,
  "scan_time_ms": 342,
  "cached": false,
  "tests": {
    "strict-transport-security": {
      "pass": true,
      "score_modifier": 20,
      "result": "HSTS enabled with sufficient max-age",
      "value": "max-age=63072000; includeSubDomains; preload",
      "info": "max-age is 63072000 seconds (730 days)"
    },
    "content-security-policy": {
      "pass": true,
      "score_modifier": 20,
      "result": "CSP header present",
      "value": "default-src 'none'; script-src ...",
      "info": "Policy defines default-src directive"
    },
    "x-frame-options": {
      "pass": true,
      "score_modifier": 15,
      "result": "X-Frame-Options set",
      "value": "SAMEORIGIN",
      "info": "Clickjacking protection enabled"
    },
    "x-content-type-options": {
      "pass": true,
      "score_modifier": 15,
      "result": "nosniff enabled",
      "value": "nosniff",
      "info": "MIME-type sniffing blocked"
    },
    "referrer-policy": {
      "pass": true,
      "score_modifier": 10,
      "result": "Referrer-Policy set",
      "value": "strict-origin-when-cross-origin",
      "info": "Referrer leakage controlled"
    },
    "permissions-policy": {
      "pass": true,
      "score_modifier": 10,
      "result": "Permissions-Policy set",
      "value": "camera=(), microphone=(), geolocation=()",
      "info": "Browser feature access restricted"
    },
    "x-xss-protection": {
      "pass": true,
      "score_modifier": 10,
      "result": "X-XSS-Protection enabled",
      "value": "1; mode=block",
      "info": "Legacy XSS filter enabled"
    },
    "cross-origin-opener-policy": {
      "pass": true,
      "score_modifier": 10,
      "result": "COOP header set",
      "value": "same-origin",
      "info": "Cross-origin window isolation enabled"
    }
  }
}

The grade scale works as follows: A+ requires a score of 110 or higher, A is 100-109, B is 80-99, C is 60-79, D is 40-59, and F is anything below 40. The maximum possible score is 130.

Here is how you call the same endpoint from Python:

import requests

url = "https://detectzestack.p.rapidapi.com/security"
params = {"url": "stripe.com"}
headers = {
    "x-rapidapi-key": "YOUR_API_KEY",
    "x-rapidapi-host": "detectzestack.p.rapidapi.com"
}

response = requests.get(url, headers=headers, params=params)
data = response.json()

print(f"Domain: {data['domain']}")
print(f"Grade:  {data['grade']} ({data['score']}/{data['max_score']})")
print(f"Scan:   {data['scan_time_ms']}ms\n")

for header, result in data["tests"].items():
    status = "PASS" if result["pass"] else "FAIL"
    print(f"  [{status}] {header}")
    print(f"         {result['result']}")
    print(f"         {result['info']}\n")

Free tier includes 100 requests/month. Get your API key on RapidAPI — no credit card required.

Scanning Multiple Sites in Bulk

The real power of an API-based approach shows when you need to audit dozens or hundreds of domains. Here is a complete Python script that reads domains from a list, calls the /security endpoint for each, and outputs a graded report:

import requests
import time

API_URL = "https://detectzestack.p.rapidapi.com/security"
HEADERS = {
    "x-rapidapi-key": "YOUR_API_KEY",
    "x-rapidapi-host": "detectzestack.p.rapidapi.com"
}

domains = [
    "stripe.com",
    "github.com",
    "shopify.com",
    "wordpress.org",
    "example.com",
]

print(f"{'Domain':<25} {'Grade':<6} {'Score':<8} {'Missing Headers'}")
print("-" * 75)

for domain in domains:
    resp = requests.get(API_URL, headers=HEADERS, params={"url": domain})
    data = resp.json()

    missing = [
        name for name, test in data["tests"].items()
        if not test["pass"]
    ]
    missing_str = ", ".join(missing) if missing else "none"

    print(f"{data['domain']:<25} {data['grade']:<6} "
          f"{data['score']}/{data['max_score']:<5} {missing_str}")

    time.sleep(0.5)  # be respectful of rate limits

Sample output:

Domain                    Grade  Score    Missing Headers
---------------------------------------------------------------------------
stripe.com                A      100/130  none
github.com                A      105/130  none
shopify.com               B      85/130   permissions-policy, cross-origin-opener-policy
wordpress.org             C      65/130   content-security-policy, permissions-policy, cross-origin-opener-policy
example.com               F      20/130   strict-transport-security, content-security-policy, x-frame-options, permissions-policy, cross-origin-opener-policy

You can extend this script to write results to a CSV, send Slack alerts for any domain scoring below a threshold, or integrate it into a CI/CD pipeline that blocks deploys when headers regress.

Try the live demo at detectzestack.com/security-headers — scan any domain, no API key needed.

Understanding the Results: What Each Header Test Means

The API tests eight security headers. Here is what each one does, how the API determines a pass, and what you risk if it is missing:

Header Purpose Pass Criteria Risk if Missing
Strict-Transport-Security Enforces HTTPS for all requests Present with max-age ≥ 1 year SSL stripping, man-in-the-middle
Content-Security-Policy Controls allowed resource origins Header present with a defined policy XSS, data injection, code execution
X-Frame-Options Prevents iframe embedding Set to DENY or SAMEORIGIN Clickjacking attacks
X-Content-Type-Options Blocks MIME-type sniffing Set to nosniff MIME confusion, drive-by downloads
Referrer-Policy Controls referrer information leakage Header present with a valid policy URL/token leakage to third parties
Permissions-Policy Restricts browser feature access Header present Unauthorized camera/mic/geo access
X-XSS-Protection Legacy browser XSS filter Set to 1; mode=block XSS in older browsers
Cross-Origin-Opener-Policy Isolates browsing context from cross-origin windows Header present Cross-origin data leaks via window references

Each test contributes a score_modifier to the total. HSTS and CSP are weighted highest at 20 points each because they address the most critical attack vectors. The remaining headers contribute 10-15 points each. For a deeper dive into each header's security implications, see the OWASP Secure Headers Project.

Real-World Use Cases

Automated security header scanning fits into several workflows beyond one-off audits:

Getting Started

The DetectZeStack security headers API is available on RapidAPI with a free tier of 100 requests per month. No credit card is required to get started.

  1. Get your free API key on RapidAPI
  2. Call GET /security?url=yourdomain.com with your key
  3. Get a grade, score, and per-header breakdown in the response

You can also scan any domain interactively on the Security Headers landing page — no API key needed, no signup required.

If you are building a broader security audit workflow, the DetectZeStack API also offers technology detection and CPE-based vulnerability matching, so you can combine header grades with dependency risk in a single pipeline.

For quick one-off checks while browsing, the DetectZeStack Chrome extension shows technology and security data for any page you visit.

Related Reading

Try DetectZeStack Free

100 requests per month, no credit card required. Grade security headers on any website from A+ to F with a single API call.

Get Your Free API Key

Get API updates and tech detection tips

Join the mailing list. No spam, unsubscribe anytime.