
Two free scanners give otomoai.com.my a grade of A+ for its HTTP response headers: securityheaders.com and the Mozilla HTTP Observatory. This post explains what the two scanners actually check, lists the headers behind the grade, and says plainly what an A+ does not prove.
What the two scanners check
securityheaders.com fetches a page and reads the HTTP response headers the server sent back. It does not log in, click anything, or look at the page content. It grades what is in the headers alone.
The Mozilla HTTP Observatory does the same job with a longer checklist. Its own FAQ describes the tool as testing for "preventative measures against" cross-site scripting, man-in-the-middle attacks, cross-domain information leakage, insecure cookies, and a handful of other header-level weaknesses, then scoring the result from A+ to F. A scan of this site through the Observatory API on 29 September 2026 returned "grade": "A+", "score": 120, "tests_passed": 12 and "tests_failed": 0 out of 12 tests. The same FAQ is direct about the limit of that number: "We'd love to say that any site that gets an A+ Observatory grade is perfectly secure, but there are a lot of security considerations that we can't test", naming outdated software, SQL injection, vulnerable plugins, and weak password handling as examples of things the grade says nothing about.
| Scanner | Grade |
|---|---|
| securityheaders.com | A+ |
| Mozilla HTTP Observatory | A+ |
| Qualys SSL Labs (TLS configuration, all four server addresses) | A+ |
Internet.nl, run by the Dutch government's Internet Standards Platform, re-checks these same headers alongside TLS configuration, and otomoai.com.my scored 100% there on 15 September 2026. See how this site reached 100% on the Internet.nl website test.
The headers we send, and what each one blocks
| Header | Value | Blocks |
|---|---|---|
| X-Frame-Options | DENY | Clickjacking: another site framing this page to trick a click |
| X-Content-Type-Options | nosniff | MIME sniffing: the browser guessing a file's type and running it as script |
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | Downgrade to plain HTTP after the first visit |
| Content-Security-Policy | per-route script hashes; no 'unsafe-inline' or 'unsafe-eval' in script-src | Injected scripts running, even if an attacker gets text onto the page |
| Referrer-Policy | strict-origin-when-cross-origin | Leaking the full URL a visitor came from to another site |
| Permissions-Policy | geolocation=(), microphone=(), camera=() | A script silently asking for the camera, microphone, or location |
The Content-Security-Policy also sets frame-ancestors 'none', object-src 'none', base-uri 'self', and form-action 'self'. style-src still allows 'unsafe-inline'. That gap is in styling only, not script execution.
How the script-src policy is built
Each page on this site is built with its own Content-Security-Policy, generated at build time. The build computes a SHA-256 hash of every inline script on that specific page and writes only those hashes into the page's script-src directive. A script the browser receives that does not match one of those hashes does not run, whether it was injected by an attacker or added by mistake in a later edit. That is what lets script-src go without 'unsafe-inline' or 'unsafe-eval', the two settings that, when present, let any inline script run. The headers are set from a file built into the site itself, not from a third-party plugin, so the policy travels with the code that deploys it.
What this is not
- It is not a penetration test. Neither scanner tries to break in; both only read headers.
- It is not a certification. There is no body that certifies a site as secure, and neither scanner claims to be one.
- It protects the visitor's browser, not our servers or databases. These headers change what a browser is allowed to do with the page; they say nothing about what happens behind our API.
- It is a point-in-time result. Run the check again whenever you like, at securityheaders.com or the Mozilla HTTP Observatory.
Why a small Malaysian business should care
Your customers reach your site through their own browser, and that browser is what an attacker targets, not your server directly. Headers like these are the setting that decides what a malicious script, a framed page, or a rogue permission request is allowed to do once it reaches that browser.
There is a paperwork reason too. When a larger client or a payment provider sends a vendor security questionnaire, a scan result anyone can re-run is a faster answer than an explanation. When we build a website for a client, the same header set is available to them at no extra cost.
Sources
- Mozilla HTTP Observatory, FAQ: scoring methodology, the A+ grade example, and the "we can't test everything" caveat. https://developer.mozilla.org/en-US/observatory/docs/faq
- MDN, "Content-Security-Policy (CSP)": how hash-based script-src stops XSS without
'unsafe-inline'. https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP - MDN, "X-Frame-Options": clickjacking. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options
- MDN, "X-Content-Type-Options": MIME sniffing. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options
- MDN, "Referrer-Policy": strict-origin-when-cross-origin. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
- MDN, "Permissions-Policy": blocking camera, microphone, and geolocation by default. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy
- RFC 6797, "HTTP Strict Transport Security (HSTS)," J. Hodges, C. Jackson, A. Barth, November 2012: the max-age directive and downgrade-attack prevention. https://www.rfc-editor.org/rfc/rfc6797
- hstspreload.org: what the HSTS preload list requires and the first-visit gap it closes. https://hstspreload.org/
- OWASP Secure Headers Project: "defense in depth as a credo." https://owasp.github.io/www-project-secure-headers/
- securityheaders.com could not be fetched for this post (HTTP 403 on three attempts, checked 14 September 2026); it is linked above as a tool to re-run the check, not cited for its own explanatory content.