obfus.link
Analyzers

OWASP security header scorecard with prioritized remediation

Paste any HTTP response headers and get a security grade A+ through F against the OWASP Secure Headers Project recommendations. Per-category breakdown for HSTS, CSP, CORS, X-Frame, and Permissions-Policy plus a structured CORS analyser.

The Header Inspector grades HTTP response headers A+ through F against the OWASP Secure Headers Project recommendations. The per-category breakdown covers HSTS, CSP, CORS, X-Frame-Options, Permissions-Policy, X-Content-Type-Options, and Referrer-Policy. A structured CORS analyser flags canonical misconfigurations including wildcard origin with credentials and missing max-age, with severity ratings on each finding.

1. Insight

Insight

The problem this article addresses and why it matters.

Every modern web application broadcasts its security posture in plain text through HTTP response headers. Strict-Transport-Security enforces HTTPS. X-Frame-Options (or Content-Security-Policy frame-ancestors) blocks clickjacking. X-Content-Type-Options: nosniff blocks MIME-sniffing attacks. Content-Security-Policy whittles down what the browser is allowed to load. Permissions-Policy opts the site out of features it doesn't use. Referrer-Policy controls referrer leakage. The presence, absence, and exact configuration of these headers tells you 80% of what a security auditor would say about a site in the first hour.

The OWASP Secure Headers Project maintains the canonical recommendations: which headers to set, what values they should contain, what the high-impact misconfigurations look like. Tools like securityheaders.com grade public sites against this checklist. The tool in this article does the same work, plus a structured CORS analyser and a remediation checklist sorted by impact — but runs against headers you paste in (response from an internal endpoint, headers from a curl -I you just ran, headers from a Charles / mitmproxy capture).

Why a paste-in scorecard instead of a public scanner

Public scanners scan public URLs. They can't help with internal services, staging endpoints behind auth, or pre-deploy QA where the site isn't reachable from the public internet. They also can't help with the "explain why this is a finding" question that drives remediation. A grade is a number; a remediation checklist is an action plan.

The tool produces both. The scorecard returns a letter grade and a per-category breakdown (HSTS, CSP, CORS, X-Frame, Permissions-Policy). The remediation array is ordered by impact — fix the critical findings first — with one-line "what to change" guidance per item.

What this article delivers

End-to-end walkthroughs of pasting a real response header set, reading the per-category grades, and applying the remediation checklist. We cover the CORS structured analyser (Access-Control-Allow-Origin: * with credentials is the canonical critical misconfiguration), the cases where a "missing" header is actually fine (CSP on a static site can be replaced with the meta-tag equivalent), and the headers the scorecard doesn't grade because they're application-specific.

2. Intent

Intent

What you will be able to do after reading.

By the end of this article you will be able to:

  • Inspect any HTTP response headers (paste from curl, mitmproxy, browser devtools) and get a security grade A+ through F
  • Read the per-category breakdown that grades HSTS, CSP, CORS, X-Frame-Options, and Permissions-Policy independently
  • Apply the remediation checklist — ordered by impact — to fix findings in priority order
  • Parse the structured CORS output that flags the canonical misconfigurations (wildcard with credentials, overly broad allow-methods, missing max-age)
  • Recognise the headers that are application-specific and shouldn't be graded blindly (CSP on a chrome-extension surface, X-Frame-Options on an iframe-embeddable widget)

The Examples section walks through a real production response, a development staging response, and a misconfigured CORS endpoint.

3. Examples

Examples

Annotated code and worked scenarios.

Before / after: paste a real response, read the scorecard

A curl -I against a midsize production API:

Before — raw headers:

HTTP/2 200
content-type: application/json; charset=utf-8
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
access-control-allow-origin: https://app.example.com
access-control-allow-methods: GET, POST, PUT, DELETE
access-control-allow-credentials: true
cache-control: no-store

After:

headerInspector({
  headersRaw: rawHeaderBlock,
  scorecard:  true,
});

// security: {
//   grade: 'B+',
//   score: 84,
//   categories: [
//     { name: 'HSTS',                status: 'present', grade: 'A', finding: 'preload, 2-year max-age, includeSubDomains — strongest config', recommendation: '' },
//     { name: 'CSP',                 status: 'missing', grade: 'D', finding: 'No Content-Security-Policy header. Best-practice direction is to ship one even for JSON APIs as defense in depth.', recommendation: 'Add Content-Security-Policy: default-src \'self\'' },
//     { name: 'CORS',                status: 'present', grade: 'A', finding: 'Explicit origin (not *), credentials enabled, method list scoped. No misconfiguration detected.', recommendation: '' },
//     { name: 'X-Frame',             status: 'present', grade: 'A', finding: 'DENY — strongest config', recommendation: '' },
//     { name: 'Permissions-Policy',  status: 'present', grade: 'A', finding: 'Opted out of camera, microphone, geolocation', recommendation: 'Consider opting out of payment, usb, accelerometer too' },
//   ],
//   remediationChecklist: [
//     '1. Add Content-Security-Policy header. Even JSON APIs benefit from default-src \'self\' as defense in depth (CSP-relevant attacks like reflected XSS in error responses).',
//     '2. Extend Permissions-Policy to opt out of payment, usb, accelerometer if the API never needs these.',
//   ],
// }

Grade B+ with a clear remediation path. The recommendation list is sorted by impact: CSP first (one-letter-grade impact when added), Permissions-Policy expansion second (cosmetic).

Before / after: a CORS misconfiguration

A development staging endpoint accidentally shipping a wide-open CORS:

access-control-allow-origin: *
access-control-allow-methods: *
access-control-allow-headers: *
access-control-allow-credentials: true
headerInspector({
  headersRaw,
  scorecard: true,
});

// cors: {
//   allowOrigin:  '*',
//   allowMethods: ['*'],
//   allowHeaders: ['*'],
//   credentials:  true,
//   maxAge:       null,
//   issues: [
//     'CRITICAL: Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true is rejected by all modern browsers. Either remove the credentials flag or specify an explicit origin.',
//     'WARNING: Access-Control-Allow-Methods: * — browsers accept this but it disables method-specific preflight caching. List explicit methods (GET, POST, etc.) instead.',
//     'WARNING: Access-Control-Allow-Headers: * — same caveat as methods. List explicit headers.',
//     'INFO: No Access-Control-Max-Age. Browsers default to 5 seconds — every CORS-preflighted request re-fires the preflight. Set max-age to 86400 (24h) for stable APIs.',
//   ],
// }
// security: { grade: 'D', ... }

The CRITICAL finding is the real one: wildcard origin + credentials is a security-and-functionality bug. Browsers reject the response, the API appears broken from the consumer side, and the team spends hours debugging "CORS is broken in Chrome" when the actual fix is "specify the explicit origin."

Before / after: the case where a "missing" header is actually fine

A chrome-extension manifest endpoint deliberately omits CSP because the extension framework injects its own:

content-type: application/json

(That's the whole header set — minimal because the endpoint is invoked only by the extension itself.)

headerInspector({
  headersRaw,
  scorecard: true,
});

// security: {
//   grade: 'F',
//   score: 12,
//   categories: [...]  // most flagged as missing
// }

Grade F. The right answer here is "the grade is wrong for this endpoint's context." The tool's scorecard doesn't know the endpoint is only ever called by an extension that injects its own headers. This is the kind of finding a human reviewer overrides — and the tool surfaces the per-category breakdown specifically so a reviewer can confirm the override is intentional.

The tool can't infer context. It can give you the data to override the grade explicitly.

Before / after: pre-deploy CI gate

A CI step runs the inspector against the staging endpoint:

# In CI script:
curl -sI https://staging.example.com/api/v1/health > headers.txt
RESULT=$(curl -X POST https://obfus.link/api/v1/header_inspector \
  -d "$(jq -Rs '{headersRaw: ., scorecard: true}' < headers.txt)")
GRADE=$(echo "$RESULT" | jq -r '.security.grade')
[ "$GRADE" = "A+" ] || [ "$GRADE" = "A" ] || [ "$GRADE" = "A-" ] || [ "$GRADE" = "B+" ] || \
  { echo "Security grade $GRADE below B+ — failing build"; exit 1; }

Now a security regression on the staging endpoint fails the build before the next prod deploy. The grade threshold is the team's policy decision; the inspector provides the deterministic input.

When humans use this

The most common workflow is the ad-hoc audit: a developer wants to verify the security of an endpoint they shipped, runs curl -I, pastes the result into the web UI, sees the grade and remediation list. Second most common is pre-launch verification: before flipping a feature live, run the inspector against the production preview URL.

When agents use this

Two production patterns:

  • Continuous compliance checks. A scheduled agent runs header_inspector against every public endpoint in the service map weekly. Grades and remediation lists feed a Slack digest. Critical findings (CORS misconfiguration, missing HSTS) open an alert immediately rather than waiting for the digest.
  • Deploy-gate enforcement. A pre-merge CI agent inspects the staging response against the main-branch baseline. Any grade decrease fails the merge. The structured remediationChecklist becomes the PR comment.

Edge cases

Multiple headers with the same name

HTTP allows multiple Set-Cookie headers, multiple Vary headers, etc. The tool parses all of them; the structured output's relevant fields contain arrays where the spec permits. For headers with single-value semantics that the input has multiple of (a common misconfiguration), the tool surfaces the duplication as a WARNING and uses the last value (matching browser behaviour for most headers).

Case sensitivity

HTTP header names are case-insensitive. The tool normalises to lowercase internally and emits the canonical mixed-case form in the output (Strict-Transport-Security, not strict-transport-security). Pasted headers in any case are handled identically.

Reverse proxy artefacts

Headers from behind a reverse proxy may include X-Forwarded-*, Via, and origin-server identifiers. These pass through; they're not graded but they're not stripped. If the inspection is for the application's own posture, paste headers captured at the application (not behind the proxy).

Per-route vs site-wide headers

Content-Security-Policy and X-Frame-Options differ legitimately per route — a public marketing page may have a stricter CSP than an authenticated dashboard. The scorecard grades the headers you provide; run it per representative route rather than expecting one grade to characterise the whole site.

4. Documentation

Documentation

Reference signatures, edge cases, and lookup tables.

Input parameters

Field

Type

Required

Default

Description

headersRaw

string

Raw response headers, one per line. Accepts curl -I output verbatim.

scorecard

boolean

false

Run the OWASP security scorecard against the parsed headers

Output shape

{
  parsed: Record<string, string>;     // normalised key (canonical case) → value
  security?: {                         // when scorecard: true
    grade:  'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' | 'C+' | 'C' | 'C-' | 'D' | 'F';
    score:  number;                    // 0-100
    categories: Array<{
      name:           'HSTS' | 'CSP' | 'CORS' | 'X-Frame' | 'Permissions-Policy';
      status:         'present' | 'missing' | 'misconfigured';
      grade:          'A' | 'B' | 'C' | 'D' | 'F';
      finding:        string;
      recommendation: string;
    }>;
    remediationChecklist: string[];    // ordered by impact
  };
  cors?: {                             // present when any CORS header is set
    allowOrigin:  string;
    allowMethods: string[];
    allowHeaders: string[];
    credentials:  boolean;
    maxAge:       number | null;
    issues:       string[];            // CRITICAL / WARNING / INFO prefixed
  };
}

Scorecard categories — what each one weighs

Category

Weight

What "A" looks like

HSTS

20

max-age >= 31536000, includeSubDomains, preload, served over HTTPS

CSP

25

Present with at minimum default-src 'self' and script-src directives

CORS

20

Explicit origin (not *), credentials only with explicit origin, scoped methods, max-age >= 86400

X-Frame-Options

15

DENY or SAMEORIGIN, OR CSP frame-ancestors directive present

Permissions-Policy

10

Opts out of unused features (camera, microphone, geolocation, payment, usb)

X-Content-Type-Options

5

nosniff

Referrer-Policy

5

strict-origin-when-cross-origin or stricter

Error codes

Code

When it fires

Recovery

INPUT_EMPTY

headersRaw empty

Paste at least one header line

INPUT_MALFORMED

None of the input lines parse as headers (no colons, garbage)

Verify the input is HTTP response headers, not request body

When NOT to use this tool

For HTTP/2 server-side audits (HTTP/2-specific concerns like header table size, frame priorities), this tool's surface is the wrong layer — those are wire-protocol concerns the headers don't express. Use h2spec or a dedicated HTTP/2 conformance tester.

For mutual-TLS configurations and certificate-pinning audits, the headers don't expose enough. Use a TLS configuration analyser (testssl.sh, Mozilla SSL Configuration Generator, SSL Labs) against the endpoint.

For application-level vulnerability scanning (SQL injection, XSS payload reflection), this tool only audits the headers as configured — it doesn't probe the application's behaviour. Use a DAST scanner (OWASP ZAP, Burp Suite) for behaviour-level audits.

Performance notes

Typical execution: under 5ms for inputs under 5KB. The OWASP scorecard runs a fixed checklist; the per-category grade is computed in O(1) per category. The tool is deterministic — same input always produces the same grade — so REST responses are Edge-Cache eligible.

The scorecard's grading thresholds track the OWASP Secure Headers Project recommendations as of mid-2026. Recommendations update periodically; grade values may shift between tool releases. Pin the version in CI workflows where grade stability matters across builds.

Try it now

Header Inspector

OWASP-graded HTTP security headers scorecard with CORS issue detection

FAQ

Frequently asked questions

How does the grade differ from securityheaders.com?

Same general methodology (OWASP Secure Headers Project), different surface. securityheaders.com scans public URLs. This tool works on paste-in headers from internal endpoints, staging environments, mitmproxy captures — anywhere a public scanner can't reach.

Should I add CSP to a JSON API?

Yes, as defense in depth. Even APIs that only return JSON benefit from default-src 'self' — it covers attack surfaces like reflected XSS in error responses, browser-served JSON viewer behaviour, and SVG content type mismatches. The grade reflects this best-practice guidance.

What's the CORS critical finding about credentials?

Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true is rejected by all modern browsers. The browser-level error is generic ("CORS policy"), the actual fix is "use an explicit origin, not *." The tool flags this immediately so the team doesn't spend hours debugging the wrong layer.

Can I lower the grade threshold for a CI gate?

Yes. The grade value is the deterministic input; your team picks the threshold. Common policies: A or better for public endpoints, B+ or better for internal services, no threshold for development. Match the policy to your threat model.