A website vulnerability scan checks what an attacker can reach without logging in: the TLS configuration and certificate, the HTTP security headers, cookie flags, mixed content and outdated JavaScript in the page, files that should never be public, and the DNS records that stop email spoofing. Each area has a specific test, a specific failure mode and a specific fix, and this guide walks through all of them.
What an external scan can see, and where its edge is
An external scan is reconnaissance done politely. It connects to the site as any visitor would and reads what the server volunteers: the handshake, the headers, the HTML, the DNS. From that, a surprising amount follows. A missing header is a missing defence. A version string in the Server header is a search term for exploits. A .git directory that answers is the source code. None of this requires sending anything hostile, which is why it is safe to run against a production site and lawful to run against one you do not own.
The edge of what a scan can see is the login page. Behind it live the application's real logic: who may read which record, whether a price can be edited client-side, whether an ID in a URL is checked against the session. Those are the flaws that cause the largest breaches, and no external scanner detects them, because from outside they look like pages loading. The guide to vulnerability scanning lays out the types of scan and the difference between a scan and a penetration test, and the passive versus active guide draws the line that Scan.now stays behind: it observes responses and never sends crafted payloads, guesses credentials or fuzzes inputs. If you own the site and want active testing, the comparison of open-source scanners explains what ZAP, Nuclei and OpenVAS each add and when to use them.
The Website Vulnerability Scanner runs every area below in one pass and grades the result. The rest of this guide takes the areas in the order an attacker would, which is also, not by coincidence, the order in which you should fix them.
Transport: TLS, certificates and HSTS
Everything else assumes the connection is private and unmodified, so transport comes first. Three things are tested.
Protocol versions and cipher suites
TLS 1.0 and 1.1 were formally deprecated by RFC 8996 in 2021 and every current browser refuses them, but many servers still accept them for the sake of clients that no longer exist. A server that does is exposed to downgrade attacks and to the cipher weaknesses those versions permit. The deprecated TLS check attempts a handshake at each old version and reports which succeed; the weak cipher check does the same for cipher families such as RC4, 3DES and export-grade suites. TLS 1.3 support is reported as a positive because it removes those choices entirely. The SSL and TLS guide explains the handshake, what a certificate proves and what a cipher suite is, in enough depth to read a testssl.sh report without a reference open.
Certificates
The scanner validates the chain up to a trusted root, checks that the hostname matches, and reports expiry, key size and signature algorithm. Most certificate findings are operational rather than exotic: a certificate that expired on a weekend, an intermediate not sent by the server so that some clients fail, a wildcard that does not cover the bare domain. The certificate errors guide matches each browser warning to its server-side cause and says plainly which errors a visitor may safely click through (almost none). You can reproduce the check locally:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
HSTS
A valid certificate does not stop the first request from going over plain HTTP, and an attacker on the network can intercept that first request and keep the victim on HTTP indefinitely: SSL stripping. Strict-Transport-Security (RFC 6797) tells the browser to remember that this host is HTTPS-only for a stated period, so the plain request is never sent again. The HSTS check looks for the header and evaluates its max-age; the preload check tells you whether the value qualifies for the browser preload list, which closes the very first visit too. The HSTS guide covers the attack, the header, includeSubDomains and the preload process, and warns about the one way HSTS bites: a long max-age on a host that later needs plain HTTP for some subdomain.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload.Headers: instructions the server gives the browser
Security headers are the highest return per minute in web security. Each is one line of server configuration, and each switches on a protection the browser already implements. The Security Headers Checker grades the full set; the security headers guide covers every header with the attack it stops and a copy-ready value. The ones that matter most:
| Header | Stops | Recommended value | Check |
|---|---|---|---|
| Content-Security-Policy | Injected scripts (XSS), data exfiltration, framing | default-src 'self'; script-src 'self' 'nonce-…'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' | missing / weak |
| X-Frame-Options | Clickjacking on older browsers | DENY (or SAMEORIGIN) | x-frame-options |
| X-Content-Type-Options | MIME sniffing that turns a text upload into a script | nosniff | nosniff |
| Referrer-Policy | Leaking full URLs to third parties | strict-origin-when-cross-origin | referrer-policy |
| Permissions-Policy | Embedded content using camera, microphone, location | camera=(), microphone=(), geolocation=() | permissions-policy |
| Cross-Origin-Opener-Policy | Cross-window attacks; enables isolation | same-origin | coop |
Two headers appear in reports as things to remove. X-XSS-Protection is a retired filter that modern browsers ignore and that could be abused in older ones; the check flags it as informational. Server and X-Powered-By values with version numbers are gifts to an attacker choosing an exploit, and the server disclosure and X-Powered-By checks report them.
Content-Security-Policy deserves its own section
CSP is the one header that is both the most powerful and the most often deployed in a form that does nothing. A policy that includes 'unsafe-inline' in script-src permits exactly the injected inline scripts that XSS produces, and a policy with a wildcard source allows an attacker to load their script from anywhere. The weak CSP check looks for precisely those patterns. A working policy uses a per-response nonce or a hash for the inline scripts the site genuinely needs, sets object-src 'none' and base-uri 'self', and is rolled out with Content-Security-Policy-Report-Only first so that violations are logged instead of breaking the site. The CSP guide takes a real page from no policy to a strict one in stages, and the clickjacking guide explains the framing attack that frame-ancestors and X-Frame-Options both exist to stop, and the few cases where a site must allow framing and how to do it narrowly.
A related misconfiguration lives in CORS rather than CSP: an Access-Control-Allow-Origin header that reflects any requesting origin together with Access-Control-Allow-Credentials: true lets any site read authenticated responses from yours. The CORS check tests for that combination and rates it high, because it is an account-data leak with no user interaction.
Cookies: the session is the prize
For most sites, the session cookie is the most valuable thing a visitor holds. Whoever has it is logged in as that user, no password required. Three attributes decide how hard it is to steal. Secure means the browser never sends the cookie over plain HTTP, so a network attacker cannot read it. HttpOnly means script cannot read it, so an XSS bug cannot exfiltrate it. SameSite controls whether the cookie accompanies requests that other sites initiate, which is the mechanism behind cross-site request forgery. The Secure, HttpOnly and SameSite checks read every Set-Cookie line the site sends and report each missing flag. A complete session cookie looks like this:
Set-Cookie: __Host-session=eyJhbGciOi...; Path=/; Secure; HttpOnly; SameSite=Lax
The __Host- prefix is the detail people miss: a browser refuses to accept a cookie with that prefix unless it is Secure, has Path=/ and has no Domain attribute, which means a compromised subdomain cannot overwrite the parent site's session. The cookie security flags guide explains each attribute, both prefixes and how to set them in the common frameworks, where the default is frequently wrong.
Page content: what the HTML pulls in
After the response headers comes the page itself, and three content findings carry real weight.
Mixed content
An HTTPS page that loads a script, stylesheet or iframe over plain HTTP has handed a network attacker the ability to rewrite the page, because that one resource can be replaced in transit. Browsers now block active mixed content outright and upgrade or block passive content such as images, but a page that depends on a blocked resource is broken, and a form whose action is an HTTP URL sends whatever the user types in the clear. The mixed content and insecure form action checks find both. The mixed content guide explains the active/passive distinction, what each browser does and the upgrade-insecure-requests CSP directive that fixes most cases in one line.
Third-party scripts and Subresource Integrity
Every script loaded from another domain runs with the full authority of your page. If the CDN or the vendor is compromised, so are your users; the payment-skimming campaigns of recent years worked exactly that way. Subresource Integrity pins a script to a hash so the browser refuses to run a changed file:
<script src="https://cdn.example/lib/4.2.0/lib.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
The SRI check lists cross-origin scripts without an integrity attribute, and the script inventory shows you every third party your page trusts, which is often the more sobering list. The SRI guide covers generating hashes, why crossorigin is required and the limit SRI cannot cross: a script that legitimately changes on every deploy.
Outdated JavaScript libraries
A page that ships jQuery 1.12 or AngularJS 1.5 ships every vulnerability published against them since, and both are still common. Detection works the way Retire.js does: identify the library and version from file names, comments and code signatures, then match against a database of known-vulnerable version ranges. The outdated library check reports each match with its CVEs and the fixing version. The outdated JavaScript guide lists the libraries most often found, what the vulnerabilities actually allow (usually XSS through a sink the library was supposed to sanitise) and how to upgrade without breaking the site. The scanner does not judge; a dependency two years old in a marketing page is a different risk from the same dependency in a checkout page, and the report gives you the facts to decide.
Exposed files and information disclosure
Some of the most damaging findings need no vulnerability at all, only a file that should never have been reachable. The scanner requests a small set of well-known paths and reports which answer:
/.git/HEAD ref: refs/heads/main -> full source tree recoverable
/.env DB_PASSWORD=... -> credentials in plain text
/backup.zip, /site.tar.gz 200 OK, 480 MB -> database and code
/phpinfo.php PHP Version 8.1.2 -> paths, modules, environment
/server-status Apache Server Status -> live client IPs and URLs
/.DS_Store binary -> directory listing of the deploy
An exposed .git directory or .env file is rated critical and should be treated as an incident in progress: remove the file, rotate every secret it contained, and search the access logs for who fetched it before you did. Backups, phpinfo, directory listings and status pages follow. The exposed files guide explains how each ends up public, usually a deploy that copied the whole working directory, and the web-server rules that close the category rather than one path at a time. The same section reports two positives: a security.txt at /.well-known/security.txt per RFC 9116, so researchers know where to report, and a sane robots.txt that does not list the admin paths it is trying to hide.
WordPress in particular
WordPress runs a large share of the web and gets attacked in proportion, and it has its own disclosure habits: the version in a meta generator tag and in asset query strings, xmlrpc.php left enabled and usable for credential stuffing through system.multicall, and the REST API listing authors at /wp-json/wp/v2/users, which hands an attacker the login names. The version, xmlrpc and user enumeration checks cover those. The WordPress scanning guide explains what an attacker looks for first and how to close each item, and is clear that outdated plugins, which an external scan can only partly see, are where most WordPress compromises actually begin.
DNS and email: can a stranger send mail as you?
Email spoofing belongs in a website scan because the domain is the same and the trust damage is the same. Without the three authentication records, anyone can send a message with your domain in the From line, and receivers have no basis to reject it. SPF (RFC 7208) lists which servers may send for the domain. DKIM (RFC 6376) signs each message so tampering and forgery are detectable. DMARC (RFC 7489) tells receivers what to do when both fail and where to send reports. The Email Security Checker resolves all three plus MX, DNSSEC and CAA; the SPF, permissive SPF, DMARC and DMARC p=none checks are the ones that fail most often. A domain that is protected publishes records like these:
example.com. TXT "v=spf1 include:_spf.google.com include:sendgrid.net -all"
_dmarc.example.com. TXT "v=DMARC1; p=reject; pct=100; rua=mailto:[email protected]"
s1._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
Two mistakes dominate. An SPF record ending in +all or ?all authorises the whole internet and is worse than no record; ~all is a soft fail that most receivers treat gently. And a DMARC record at p=none is monitoring only: useful for the first weeks while you find every legitimate sender, useless as a protection if it stays there, which it usually does. The SPF, DKIM and DMARC guide walks through writing the records, reading the aggregate reports and moving to p=quarantine and then p=reject without dropping real mail. The DNSSEC and CAA checks are lower priority but each closes a distinct impersonation route: forged DNS answers and mis-issued certificates.
The application layer a scanner cannot reach
Everything above is configuration. The OWASP Top 10:2021 is mostly not: broken access control, injection, insecure design, authentication failures. The OWASP Top 10 guide goes through each category with a concrete example and is explicit about which a passive scan can detect (security misconfiguration, vulnerable and outdated components, parts of cryptographic failures) and which need code review or authorised active testing (the rest).
Two categories deserve to be understood even by people who will never fix them, because they explain why the header and cookie findings matter. Cross-site scripting is the injection of attacker-controlled script into a page other users see; the XSS guide covers reflected, stored and DOM-based variants and shows why CSP, HttpOnly cookies and output encoding are layers rather than alternatives. SQL injection turns user input into database commands; the SQL injection guide shows the mechanism, explains why an external passive scan will almost never prove it exists, and gives the one fix that works everywhere: parameterised queries, with no exceptions for "trusted" input.
Turning findings into a fix list
A first scan of a site that has never been hardened commonly returns twenty to forty findings, and the way to avoid paralysis is to know how severities are assigned and to work by exposure removed per hour rather than strictly by colour. Named vulnerabilities carry a CVSS score from their CVE; the CVE and CVSS guide explains what the score measures and why a CVSS 9.8 in a library you load but never call may rank below a CVSS 6.1 on the code path that handles login. Configuration findings carry a default severity from the check registry, adjusted for what the scanner observed.
- Exposed secrets and source:
.git,.env, backups. Remove, rotate, audit logs. - Transport: disable TLS 1.0 and 1.1, fix the chain, add HSTS.
- Session cookies:
Secure,HttpOnly,SameSite, and the__Host-prefix if you can. - Known-vulnerable libraries on pages that handle input or money.
- Headers:
nosniff,X-Frame-Options,Referrer-Policy,Permissions-Policyin one change; then a report-only CSP that you tighten over weeks. - Email: SPF with
-all, DKIM on every sender, DMARC top=rejectvia a monitored ramp. - Disclosure and hygiene: hide versions, publish
security.txt, reviewrobots.txt.
The guide to reading a scan report covers the two remaining skills: recognising false positives, which cluster around CDNs and load balancers that rewrite headers, and confirming a fix by rescanning rather than by belief. Rescan after every deployment. Headers get dropped in a config refactor, a new marketing script arrives without SRI, a certificate renewal fails quietly; the scan exists to catch the regression before someone else does.