/ Learning Hub / Security Audit Guide

Website Security Audit: HTTP Headers, TLS, CMS, Mail DNS & CVEs

A passive security audit checks the publicly observable security posture of any website โ€” without sending malicious payloads or probing for vulnerabilities. This guide walks through every check our Security Audit tool runs, why each one matters, and exactly how to fix any that fail. Pair this guide with the example report to see a real audit output.

What is a passive security audit?

A passive audit only inspects information your site already exposes to any visitor: HTTP response headers, the TLS handshake, public DNS records, and the rendered HTML. Nothing is sent that could be considered an attack. That makes it safe to run against any domain you have permission to scan, including production, and means it will not trigger your WAF or intrusion detection.

This audit covers 23 individual checks across 9 families. Each check is tagged with the relevant OWASP Top 10 category and CWE identifier so findings map cleanly onto the standard vulnerability taxonomy used by security teams.

HTTP Security HeadersSix headers that defend against XSS, clickjacking, MIME sniffing, and information leakage.
TLS / SSL ConfigurationCertificate validity, expiry window, and modern protocol version (TLS 1.2+).
Cookie Security FlagsSecure, HttpOnly and SameSite attributes on every Set-Cookie response.
Mixed ContentHTTPS pages must not load any http:// scripts, stylesheets, images or iframes.
Server FingerprintingServer and X-Powered-By headers should not advertise exact versions.
Known CVEs (NVD)Detected software versions cross-referenced against the National Vulnerability Database.
CMS DetectionWordPress, Drupal and Joomla detected via generator meta tags and content paths.
Mail Security DNSSPF, DMARC and DKIM records โ€” even non-email sites are spoofed.
robots.txt / security.txtRFC 9116 vulnerability disclosure contact and standard crawler directives.

HTTP Security Headers

Six response headers do most of the heavy lifting for browser-side protection. They cost nothing to deploy, work in every modern browser, and stop entire classes of attack.

Content-Security-Policy (CSP)

CSP tells the browser which sources are allowed to load scripts, stylesheets, fonts, frames and other resources. A well-tuned CSP is the single strongest defence against cross-site scripting (XSS), which remains one of the most common web vulnerabilities. Start strict and loosen as needed:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'
๐Ÿ’ก Use frame-ancestors in CSP instead of (or in addition to) X-Frame-Options. CSP frame-ancestors is the modern replacement and supports finer-grained control.

Strict-Transport-Security (HSTS)

HSTS tells browsers to refuse to connect to your site over plain HTTP, even if the user types http:// in the address bar. This closes the small window where a man-in-the-middle attacker could downgrade the first request before the redirect to HTTPS.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
โš ๏ธ Only add preload after testing โ€” it cannot easily be removed once you submit your domain to the HSTS preload list.

X-Frame-Options / CSP frame-ancestors

Stops other sites embedding your pages in an iframe. The classic clickjacking defence โ€” without it, an attacker can overlay your login form inside a malicious page and steal credentials. Either header passes the check:

X-Frame-Options: SAMEORIGIN

Or, preferred:

Content-Security-Policy: frame-ancestors 'self'

X-Content-Type-Options

Stops browsers second-guessing the Content-Type header through MIME sniffing. Without it, a file you serve as text/plain could be interpreted as JavaScript and executed. One line, one config option:

X-Content-Type-Options: nosniff

Referrer-Policy

Controls how much of the originating URL is sent in the Referer header when a user clicks a link to another site. The right setting protects user privacy and prevents leaking session-bearing URLs:

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy

Disables browser features that your site does not need. If you do not use the camera, microphone or geolocation, deny them and remove an entire class of supply-chain risk from compromised third-party scripts:

Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()

TLS / SSL Configuration

The TLS check opens an actual handshake to port 443 and inspects the certificate plus the negotiated protocol. Three things must be true:

CheckPass condition
Certificate validSigned by a trusted CA, not expired, not revoked
Not expiring soonValid for at least 30 more days
Modern protocolServer negotiates TLS 1.2 or TLS 1.3 โ€” never TLS 1.0 or 1.1
๐Ÿ’ก Let's Encrypt certificates auto-renew through certbot or ACME. There is no good reason to ever let a certificate expire in 2026.

Cookie Security Flags

Every Set-Cookie response should set three attributes. If a site sets cookies on the homepage, the audit checks all of them:

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/

Mixed Content

If your page is served over HTTPS but loads any http:// resources, browsers will either block them (modern behaviour) or downgrade the connection (older behaviour). Either way, you lose. The audit scans the homepage HTML for any script, link, img, iframe or source tag pointing at an unencrypted URL.

โš ๏ธ Common offenders are old hardcoded image URLs, embedded YouTube/Vimeo players using http://, and analytics or chat widgets configured pre-HTTPS migration.

Server Fingerprinting

Two response headers commonly leak version information that makes an attacker's job easier:

Server: nginx/1.18.0
X-Powered-By: PHP/7.4.0

An attacker now knows your nginx and PHP versions before lifting a finger. Strip them. In nginx:

server_tokens off;
fastcgi_hide_header X-Powered-By;
proxy_hide_header X-Powered-By;

In Apache:

ServerTokens Prod
ServerSignature Off
Header unset X-Powered-By

In PHP, also set expose_php = Off in php.ini.

Known CVEs (NVD)

When the audit identifies a software version from the Server or X-Powered-By header, or a CMS generator meta tag, it builds a CPE 2.3 identifier and queries the National Vulnerability Database for matching CVEs.

DetectedCPE vendor:product
nginx/X.Y.Zf5:nginx
Apache/X.Y.Zapache:http_server
PHP/X.Y.Zphp:php
Microsoft-IIS/X.Ymicrosoft:internet_information_services
OpenSSL/X.Y.Zopenssl:openssl
WordPress X.Y.Zwordpress:wordpress
Drupal X.Y.Zdrupal:drupal
Joomla X.Y.Zjoomla:joomla!

Results are cached for 24 hours per CPE to keep scans fast and avoid hammering NVD. If your site does not expose any version strings, this check passes by default โ€” no fingerprint, nothing to look up. That is the desired outcome.

CMS Detection

The audit looks for three signals on the homepage:

Detection itself is not a fail. The fail is when the generator tag exposes the exact version, because that turns CMS detection into a one-step CVE lookup for an attacker. Remove the generator meta:

// WordPress functions.php
remove_action('wp_head', 'wp_generator');

Mail Security DNS

Even sites that do not send email get impersonated. Without SPF, DKIM and DMARC records, anyone can send mail from @yourdomain.com and have it land in inboxes. The audit checks three TXT records on the apex of your domain (the www. prefix is stripped automatically):

# SPF (TXT on apex)
v=spf1 include:_spf.google.com -all

# DMARC (TXT on _dmarc.yourdomain.com)
v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com

# DKIM (TXT on <selector>._domainkey.yourdomain.com)
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GN...

DKIM uses per-vendor selectors so the audit probes the most common ones (default, google, selector1, selector2, k1, s1, s2, mail). If your provider uses a different selector the check may show no result even though DKIM is configured โ€” check your email provider's documentation.

robots.txt & security.txt

Two standard well-known files round out the audit.

robots.txt

Crawlers expect /robots.txt to exist. Even an empty file is preferable to a 404, which floods server logs with crawler errors and signals an under-maintained site.

security.txt (RFC 9116)

Defines how security researchers should report vulnerabilities they find on your site. Publish it at /.well-known/security.txt:

Contact: mailto:security@yourdomain.com
Expires: 2027-12-31T23:59:59.000Z
Policy: https://yourdomain.com/security-policy.html
Preferred-Languages: en

Required fields are Contact and Expires. Everything else is optional but useful. Without security.txt a researcher who finds a bug has no obvious place to report it, and may resort to public disclosure.

How the score is calculated

Each check has a weight reflecting its impact. TLS validity and protocol weight heaviest (10 and 8 points respectively), header checks are mid-weight (4โ€“8), and informational checks like robots.txt presence are light (2). Warnings (for example a certificate expiring in 30 days, or a CMS exposing its version) score half-credit rather than zero. The total weight is 109 points, normalised to a score out of 100.

๐Ÿ’ก A score above 80 indicates a strong baseline. 50โ€“79 is workable but has notable gaps. Below 50 means several foundational protections are missing and should be addressed immediately.

Frequently Asked Questions

Is this audit safe to run on production?
Yes. Every check is passive โ€” it only inspects publicly observable information. No payloads are sent, no ports are scanned beyond port 443 for the TLS handshake, and the HTTP requests are identical to those made by a normal browser. It will not trigger your WAF or IDS.
How long does a scan take?
Typically 10โ€“20 seconds. The audit runs the homepage fetch, robots.txt fetch, security.txt fetch, TLS handshake and DNS lookups in parallel, then queries NVD for any detected version fingerprints. Cached CVE lookups complete in milliseconds.
What CMSes are detected?
WordPress, Drupal and Joomla. These three account for the overwhelming majority of CMS-powered sites and have the highest CVE volumes in NVD. Other platforms can be added in a future release.
Why is my SPF/DMARC showing as missing?
The audit checks DNS records on the apex domain (yourdomain.com), not on the www. subdomain. If you typed www.yourdomain.com the audit automatically strips the www and queries the apex. If records still show missing, the records genuinely are not there โ€” confirm with dig TXT yourdomain.com.
Does this replace a full penetration test?
No. A passive audit catches the misconfigurations that an attacker would notice in their first five minutes of reconnaissance. It does not replace a manual penetration test or a dynamic scanner like OWASP ZAP, which probe for application-layer flaws like SQL injection, authentication bypass and business logic abuse.

๐Ÿ›ก Run a free security audit on your site

Scan your domain across 23 passive security checks. Get OWASP and CWE-tagged findings with specific fix instructions for everything that fails.

Run free security audit โ†’
Related Guides: How to Fix Audit Findings  ยท  Beginner Tutorial  ยท  AI Agent Readiness Guide  ยท  Robots & Sitemap Guide
๐Ÿ’ฌ Got a problem?