Security headers, and why they belong in your RFP
Before a website sends a single word of its page, it sends the browser a few lines of instructions. Nine of them decide whether a man in the middle can rewrite the page, whether someone else’s JavaScript can run on it, and whether a sign-in or checkout form can be quietly pointed somewhere else. They cost a vendor almost nothing to add, anyone can check them from outside, and a vendor who skips them has told you something about the rest of their engineering.
The 95 tools graded here right now
- 6 tools graded A+
- 16 tools graded A
- 20 tools graded B
- 11 tools graded C
- 7 tools graded D
- 35 tools graded F
What attackers do when headers are missing
These attacks chain. A man in the middle injects a script; the script points the checkout form at the attacker; the customer pays, and their card goes with it. Each header closes one link in that chain.
-
Man-in-the-middle attacks
Someone between the user and the site (public Wi-Fi, a compromised router, a hostile network) reads and rewrites the traffic. If the browser ever speaks plain HTTP to the site, even for the first request, the attacker can strip the upgrade to HTTPS, serve their own copy of the page, read passwords, session cookies and card numbers, and inject their own JavaScript into what the user sees.
Stopped by: Strict-Transport-Security.
-
Malicious JavaScript
Script the vendor never wrote runs on their page: injected through a bug (cross-site scripting), through a compromised third-party tag such as analytics, a chat widget or an ad, through a poisoned package in the build, or through an uploaded file the browser mistakes for script. Script on the page can do anything the user can: read the screen, capture every keystroke, and make requests as them.
Stopped by: Content-Security-Policy and X-Content-Type-Options.
-
Redirected forms
Injected script or markup quietly changes where a sign-in, checkout or contact form submits. The page looks the same and the user presses the same button, but their password, card or personal details go to the attacker, often followed by a bounce back to the real site so nothing seems wrong. The CSP form-action rule lists the only places forms may submit to.
Stopped by: Content-Security-Policy and Strict-Transport-Security.
-
Hijacked payments
Card skimming, often called Magecart: a few lines of script on a checkout page copy the card number, expiry date and security code as they are typed and send them to the attacker, while the real payment goes through as normal, so neither the customer nor the vendor notices. In 2018 attackers altered one script on British Airways' website and skimmed the payment details of hundreds of thousands of customers over about two weeks; the UK regulator fined the airline £20 million. A strict CSP limits which scripts can run on the checkout and where the page can send data, HSTS stops the checkout being swapped in transit, and frame protection stops another site framing the pay button.
Stopped by: Content-Security-Policy, Strict-Transport-Security and Frame protection.
-
Clickjacking and tab-nabbing
The user is steered into acting without knowing it: another site frames the product invisibly and lines its real buttons up under a decoy, or a page the product opened (or that opened it) swaps the original tab for a look-alike sign-in page.
Stopped by: Frame protection and Cross-Origin-Opener-Policy.
The nine headers, and what goes wrong without each
Heaviest first. The points are what each one is worth in the grade.
-
Content-Security-Policy 25 points
Tells the browser which scripts may run on the page, where its forms may submit, and where it may send data.
Without it: This is the main defence against malicious JavaScript. If an attacker gets any script onto the page (through a bug, a compromised analytics tag, chat widget or ad, or a poisoned package), the browser runs it with the signed-in user's full access. It can capture what they type, quietly point a sign-in or checkout form at the attacker's server, and skim card numbers as they are entered. A strict policy blocks scripts it does not list, and its form-action and connect-src rules leave stolen data nowhere to go.
-
Strict-Transport-Security 20 points
Tells browsers to use HTTPS for this domain, every time, without trying plain HTTP first.
Without it: The site is open to man-in-the-middle attacks. The first request a user makes (typing the domain, following an old http:// link) can go out unencrypted, and anyone on the same network, such as café, hotel or airport Wi-Fi, can intercept it before the redirect to HTTPS. From there they can read passwords, session cookies and card numbers, serve a fake sign-in page, or inject their own JavaScript into the real one.
-
X-Content-Type-Options 10 points
Stops the browser guessing a file's type instead of trusting the type the server declared.
Without it: A file uploaded as an image or plain text can be reinterpreted as HTML or script and run inside the product's own origin: malicious JavaScript delivered through a harmless-looking upload feature.
-
Frame protection 10 points
X-Frame-Options or the CSP frame-ancestors directive: decides which sites may show this one inside a frame.
Without it: Clickjacking. Another site loads the product invisibly in a frame and lines its real buttons up under something the user wants to click, so a signed-in user approves a payment, changes their account email or grants access without seeing it.
-
Referrer-Policy 10 points
Limits how much of the current URL is passed to other sites when a user follows a link or the page loads a third-party resource.
Without it: Full URLs, including paths and query strings that can carry document IDs, search terms, customer names or reset tokens, can leak to every outside script, image host and outbound link. Recent browsers default to a safer policy; older ones and embedded webviews do not.
-
Permissions-Policy 10 points
Switches off browser features the product does not use, such as camera, microphone, location and payments, for the page and anything embedded in it.
Without it: Any script or iframe on the page, including third-party ads and chat widgets, can ask the user for camera, microphone or location access under the vendor's name.
-
Cross-Origin-Opener-Policy 5 points
Cuts the link between the product's window and windows opened by, or opening, other sites.
Without it: A page that opens the product keeps a handle on its window and can later redirect it to a look-alike sign-in or payment page (tab-nabbing) while the user thinks they are still on the real site. It is also part of the isolation browsers need to defend against Spectre-style attacks.
-
Cross-Origin-Resource-Policy 5 points
Tells browsers which sites may load this site's resources.
Without it: Other sites can pull the product's responses into their own pages, which makes side-channel leaks of what a signed-in user can see easier.
-
No version disclosure 5 points
Keeps the Server and X-Powered-By headers from announcing exact software versions.
Without it: A header like "nginx/1.18.0" or "PHP/7.4" tells an attacker which known vulnerabilities to try, and flags possibly unpatched software to every automated scanner on the internet.
How the grade is worked out
We request the tool’s website over HTTPS, follow up to five redirects, and read the response headers of the page we land on. Each header present earns its points; the total out of 100 becomes a letter. If the final page is served over plain HTTP, or the site cannot be reached over HTTPS at all, there is nothing to grade.
| Grade | Score |
|---|---|
| A+ | 95 to 100 |
| A | 85 to 94 |
| B | 70 to 84 |
| C | 55 to 69 |
| D | 40 to 54 |
| F | 0 to 39 |
Every listed tool is checked again every 7 days, so a vendor who adds a header moves up at the next check. A check that fails because the site was unreachable never replaces a grade; the tool keeps its last one until a check succeeds.
What the grade does not tell you. It is one narrow, automated signal. It checks whether each header is sent, not how well it is configured: a Content-Security-Policy that allows everything, or that leaves out form-action, still counts as present. That is why the clause below spells out what the policy has to restrict. It looks at the website listed here, which is sometimes a marketing site rather than the app you sign in to. It is not a penetration test, and it says nothing about how the vendor stores your data.
Security posture in an RFP
An RFP (request for proposal) is where a buyer writes down what a vendor has to meet before they are considered. If the tool will handle your staff’s sign-ins or your customers’ payments, the attacks above are your risk as much as the vendor’s. Most RFP security sections are questionnaires the vendor fills in about itself. Security headers are different: you can check them yourself, from outside, in seconds, before the first sales call, and hold every bidder to exactly the same test.
That makes them a useful first filter:
- Cheap to fix, so telling when missing. Most headers are one line of server or CDN configuration. A vendor without them has either not looked or not prioritised it.
- Verifiable. No self-attestation. What the headers say today is what the headers say.
- Comparable. Every bidder gets the same score on the same scale, which is hard to argue with in an evaluation panel.
- Continuous. You can check again at signature, at renewal, or any day in between.
How to run it:
- Put a minimum in the RFP. The clause below sets B (70 out of 100); raise it to A for anything that will hold sensitive data.
- Check each bidder’s product domain, the one your people will sign in to, not only the marketing site. Tools listed here already have a grade on their page. For anything else,
curl -sI https://app.example.comprints the headers. - Ask any bidder below the minimum for a dated fix for each missing header, rather than a general promise.
- Check again before you sign, and at every renewal.
It does not replace a SOC 2 report, a penetration test or a data-processing review. It is the check you can run on a shortlist of ten before you spend time on any of those.
A clause you can copy
Built from the same nine checks the grade uses. Adjust the minimum and the remediation window to your own procurement rules.