DEV Community

Dev Nestio
Dev Nestio

Posted on • Originally published at devnestio.pages.dev

I Built a Browser-Only HTTP Header Analyzer — Security Scoring, Missing Header Warnings, 147 Tests

Every web developer has had this moment: you check your app's response headers, see a wall of Content-Type and Cache-Control, and wonder — is this actually secure? Which headers am I missing? What does Permissions-Policy do again?

I built HTTP Header Analyzer to answer those questions instantly. Paste any set of HTTP response headers and get a security score, missing header warnings, cache analysis, and recommended values — all in the browser, zero dependencies, zero server.


What it does

Paste headers in Key: Value format (one per line) and click Analyze Headers. The tool:

  • Scores security headers from 0–100 with a letter grade (A+ → F)
  • Flags missing critical headers with recommended values
  • Categorizes all headers — Security / Cache / Content / Other
  • Evaluates each header value — good, warn, or bad — with a plain-English explanation
  • Supports sample presets for Nginx, Express, Apache, and a "Minimal (insecure)" set

The security scoring model

Six headers are required; three more earn bonus points:

Header Points
Content-Security-Policy 20
Strict-Transport-Security 15
X-Frame-Options 10
X-Content-Type-Options 10
Referrer-Policy 10
Permissions-Policy 10
Cross-Origin-Embedder-Policy +5 bonus
Cross-Origin-Opener-Policy +5 bonus
Cross-Origin-Resource-Policy +5 bonus

A "warn" rating (e.g. HSTS with max-age under a year, or CSP with unsafe-inline) earns half points. The score caps at 100.

A+ ≥ 90   A ≥ 80   B ≥ 70   C ≥ 60   D ≥ 40   F < 40
Enter fullscreen mode Exit fullscreen mode

Nuanced per-header evaluation

Rather than just checking "present or absent," the tool evaluates the value:

HSTS

Strict-Transport-Security: max-age=86400
→ warn: max-age should be at least 31536000 (1 year)

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
→ good: Good configuration.
Enter fullscreen mode Exit fullscreen mode

CSP

Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline'
→ warn: Contains unsafe-inline. Consider removing for stronger XSS protection.

Content-Security-Policy: default-src 'self'; object-src 'none'
→ good: Policy present without unsafe directives.
Enter fullscreen mode Exit fullscreen mode

Set-Cookie

Set-Cookie: session=abc123; path=/
→ bad: Missing HttpOnly; Missing Secure; Missing SameSite.

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict
→ good: All security attributes present.
Enter fullscreen mode Exit fullscreen mode

Server / X-Powered-By — version disclosure is flagged:

Server: nginx/1.24.0
→ warn: Version disclosed. Consider removing version number.

Server: nginx
→ good: No version disclosed.
Enter fullscreen mode Exit fullscreen mode

Cache-Control — directives are decoded into plain English:

Cache-Control: public, max-age=31536000, immutable
→ public | max-age=31536000 | immutable: resource will not change
Enter fullscreen mode Exit fullscreen mode

Sample presets

Three real-world samples are one click away:

Nginx — full security headers, Brotli, ETag, HSTS with preload

Express — X-Powered-By leak, weak HSTS, CORS origin, signed cookie

Apache — version disclosure, no security headers, basic cache

Minimal (insecure) — the bad old days: PHPSESSID without HttpOnly, PHP version disclosed

Load "Minimal (insecure)" and you get an F-grade report with every critical header flagged as missing. Load "Nginx" and you score 75 with specific advice on adding COEP/COOP/CORP to push toward A+.


Implementation

Zero dependencies. Pure HTML + CSS + vanilla JavaScript in a single file.

The parser handles:

  • HTTP status lines (skipped correctly)
  • Multiple Set-Cookie headers (collected as an array)
  • Values containing colons (Location: https://example.com:443/path)
  • Case-insensitive header name matching
function parseHeaders(raw) {
  const headers = {};
  for (const line of raw.split("\n")) {
    const trimmed = line.trim();
    if (!trimmed || /^HTTP\/\d/i.test(trimmed)) continue;
    const idx = trimmed.indexOf(":");
    if (idx === -1) continue;
    const key = trimmed.slice(0, idx).trim().toLowerCase();
    const value = trimmed.slice(idx + 1).trim();
    if (key === "set-cookie") {
      if (!headers[key]) headers[key] = [];
      headers[key].push({ name: trimmed.slice(0, idx).trim(), value });
    } else {
      headers[key] = { name: trimmed.slice(0, idx).trim(), value };
    }
  }
  return headers;
}
Enter fullscreen mode Exit fullscreen mode

Scoring:

function computeScore(parsedHeaders) {
  let score = 0;
  for (const key of REQUIRED_SECURITY_HEADERS) {
    const hdr = parsedHeaders[key];
    if (!hdr) continue;
    const ev = HEADER_DB[key].evaluate(hdr.value);
    if (ev.rating === "good") score += HEADER_DB[key].points;
    else if (ev.rating === "warn") score += Math.floor(HEADER_DB[key].points * 0.5);
  }
  // bonus for COEP/COOP/CORP
  return Math.min(100, score + bonusScore);
}
Enter fullscreen mode Exit fullscreen mode

147 tests, no framework

Tests run in Node.js with just the built-in assert module.

$ node test/test.js

Results: 147 passed, 0 failed
Total: 147 tests
Enter fullscreen mode Exit fullscreen mode

Test coverage includes:

  • Parser edge cases (status line skipping, colon-in-value, multiple Set-Cookie)
  • Each header's evaluation logic across all rating outcomes
  • Score computation (correct points, half-points for warn, bonus capping at 100)
  • Grade thresholds (A+ / A / B / C / D / F)
  • Utility functions (formatSeconds, formatBytes)
  • Integration scenarios (nginx sample, minimal insecure, full perfect score)

Try it

Live tool: https://devnestio.pages.dev/http-header-analyzer/

All tools: https://devnestio.pages.dev/

The tool is part of devnestio — a collection of browser-only developer utilities with no login, no tracking, and no server round-trips.


Built with vanilla JS. 147 tests. Zero dependencies.

Top comments (0)