What the audit is
Crocky Audit is an automated, external audit of a public website. It reads the site from the internet the way a visitor's browser, a search engine and a mail server see it, and reports each finding with the evidence behind it. It covers performance, search, accessibility, security, hosting, DNS and mail, the technologies in use, and an estimate of whether the site was built with AI tools.
It does not log in (except with the password of a protected preview that the site owner supplies), does not test the application's logic and does not try to exploit what it finds. A report describes the site at the moment of the audit.
One audit, step by step
An audit runs these stages in order:
- First request to the address entered, following up to 10 redirects, with a 20-second limit. If the site does not answer, the audit stops with that finding.
- Opt-out and access: robots.txt is read for a CrockyAudit opt-out (next section), and a bot check or a password gate is recognised before anything else runs.
- Infrastructure: TLS, DNS, mail servers, response headers and the sensitive-path sweep, over plain HTTP, DNS and SMTP connections.
- Platform checks: hosting panel, WordPress and WHMCS, each only when the site runs it.
- Browser measurements in Chromium: Lighthouse (mobile, then desktop), links, accessibility, screenshots and the AI check.
- Results: the carbon estimate, the scores, the report, and its PDF in English and Romanian.
A standard audit reads the page entered, usually the homepage. A deeper audit also crawls the site: the sitemap first, then links, up to 100 pages and three levels deep within 60 seconds, three pages at a time, leaving out what robots.txt disallows. Lighthouse, screenshots, TLS and DNS stay on the first page; links are checked on every crawled page and accessibility on the first 10.
An audit has 12 minutes, or 25 for a deeper audit. Each stage also has its own limit: 20 seconds for the first request, 180 seconds per Lighthouse run, 25 seconds of accessibility checks per page. A stage that runs out of time is reported as not run, and the rest of the audit continues.
When the queue is long, a free audit may run in a shorter form: Lighthouse on mobile only and screenshots at two widths. The list of checks in the report marks what was left out.
How the scanner identifies itself
Every request carries a User-Agent with the token CrockyAudit and the address https://audit.crocky.host/bot, which explains what the scanner does. Requests made by the browser add the token to a standard Chrome User-Agent.
A site opts out with a robots.txt group that names CrockyAudit and disallows /: the audit then stops after its first request. A group for every user agent (User-agent: *) does not stop an audit someone asked for, but the deeper crawl respects its Disallow rules.
Security
The security checks look at what any visitor's browser receives from the site and at a fixed list of addresses that should not be public. They do not test the application's logic and do not try to exploit what they find.
Requests the scanner makes, and the ones it never makes
Almost every request is a GET or a HEAD, the same requests a browser or a link checker sends. There are two POST requests, both read-only: on a WordPress site, one XML-RPC call to system.listMethods, which lists the methods the endpoint offers and runs none of them; and, only when the site owner typed the password of a Shopify storefront into the audit form, that password sent to the storefront's own password page.
The scanner never:
- tries a login or guesses a password: no brute force and no credential lists;
- sends attack payloads: no SQL injection, cross-site scripting, path traversal or parameter fuzzing;
- submits forms, uploads files, or creates, changes or deletes anything on the site;
- solves a CAPTCHA or works around a bot check. When a bot check answers instead of the site, the scanner waits up to 15 seconds for it to clear; if it does not, the checks behind it are reported as not run.
Response bodies are read only as far as a check needs: the first 64 KB to confirm a sensitive file, at most 512 KB of a public phpinfo() page. Files are never downloaded whole. A secret the scanner meets is not copied into the report: on a public phpinfo() page the report names the variables that look like credentials, never their values. WordPress usernames, cookie values, and the server address, host name and paths from a phpinfo() page are shown in full only to the account that ran the audit; anyone else with the link, and every PDF, sees them masked.
Load on the audited server
The sensitive-path sweep sends at most 10 requests at a time; 6 when the site's first byte takes 0.5 to 1.2 seconds, and 4 when it takes longer. Each request waits three times the site's own first-byte time, never less than 1.5 seconds and never more than 5. The sweep stops after 45 seconds, and paths it did not reach are reported as not checked. If the site answers 429 Too Many Requests, the scanner pauses 3 seconds and asks those paths once more, two at a time; anything still refused is reported as not checked, never as absent. Other parallel checks (links, hosting-panel addresses) run at most 6 at a time.
HTTPS, redirects and response headers
The audit starts from the address entered (with https:// added when the scheme is left out) and follows up to 10 redirects. If the page it ends on is not served over HTTPS, that is a critical finding. A chain of more than two redirects is reported as a performance cost (medium). The headers below are read from the response of that final page.
| Header | What is checked | Severity when missing |
|---|---|---|
Strict-Transport-Security | present (max-age is not graded); a separate low-severity finding when the domain is not on the HSTS preload list | high |
Content-Security-Policy | present, then graded A to F (next section) | medium |
X-Content-Type-Options | present | low |
X-Frame-Options | present, unless an enforced CSP sets frame-ancestors, which replaces it | low |
Referrer-Policy | present | info |
Permissions-Policy | present | info |
Content-Security-Policy grade
The grade reads what the browser enforces for scripts: script-src, or default-src when script-src is absent. 'unsafe-inline' in style-src does not lower it.
- A: nonces, hashes or
'strict-dynamic', no unsafe sources,object-src 'none'and a restrictedbase-uri. No finding. - B: a reasonable policy without nonces, hashes or
'strict-dynamic'. Low. - C: report-only mode, or neither
script-srcnordefault-src. Medium. - D: one of: inline scripts allowed (
'unsafe-inline'without a nonce, a hash or'strict-dynamic', any of which makes browsers ignore it),'unsafe-eval', or script sources open to almost any host (*,https:,http:,data:). Low. - F: inline scripts and
evalboth allowed. Medium, the same as no policy at all.
A weak policy is reported as a missing layer of protection, not as a vulnerability. Two more findings come from the policy: neither frame-ancestors nor X-Frame-Options (medium), and object-src not set to 'none' (low).
Cookies
Each cookie in the Set-Cookie headers of the page's response is checked for Secure, HttpOnly and SameSite. One finding lists the gaps: medium when a cookie lacks Secure, low otherwise. Cookies that scripts set later, including third-party ones, are counted in the browser pass (see Visual).
Page content
- Mixed content: a script, stylesheet, image, frame or media file that an HTTPS page loads over
http://. A link to another site does not count. High. - Subresource Integrity: the page loads more than two scripts and stylesheets from other hosts and fewer than 30% carry an
integrityattribute. Medium. - Directory listing: an "Index of /" page at the site root or at
/icons/. High. - security.txt: no file at
/.well-known/security.txt. Low.
TLS
One TLS handshake with the site's host, asking for OCSP stapling and offering HTTP/2. Reported: the negotiated protocol and cipher, forward secrecy, the certificate's expiry (high under 14 days, critical once expired), the chain, the signature algorithm and the key size (weak below 2048-bit RSA or 224-bit EC), and the OCSP staple when the certificate names an OCSP server. Two more handshakes, 3 seconds each, check whether TLS 1.0 and TLS 1.1 are still accepted.
Sensitive paths
The scanner asks for 77 fixed paths. Each is a file or page that gives an attacker something usable when a web server serves it (credentials, source code, a database, an administration screen, the server's configuration), and each is a common deployment mistake: a file that should have stayed on a developer's machine or behind the server's configuration. The list is fixed so that two audits of the same site ask the same questions. Each path carries a fixed severity that reflects what that kind of file holds.
| Group | Examples | Severity |
|---|---|---|
| Environment files (6) | /.env, /.env.production, /.env.local | critical |
| Database dumps and backup archives (6) | /database.sql, /dump.sql, /backup.zip, /backup.tar.gz | critical |
| Configuration backups (5) | /wp-config.php.bak, /.htaccess.bak, /web.config.bak | critical |
| Version-control metadata (6) | /.git/config, /.git/HEAD, /.svn/entries, /.hg/store/00manifest.i | critical |
| Dependency manifests and lockfiles (4) | /composer.json, /composer.lock, /package-lock.json, /yarn.lock | medium |
| Server status and PHP information pages (7) | /server-status, /nginx_status, /phpinfo.php, /info.php | high; /test.php and /php.php medium |
| Database administration tools (5) | /phpmyadmin/, /pma/, /adminer.php | high |
| Admin and installer pages (5) | /admin/, /administrator/, /wp-admin/, /install.php, /setup.php | medium |
| API documentation (5) | /swagger/, /api-docs, /redoc/, /graphiql | medium |
| Editor and operating-system files (4) | /.DS_Store, /.idea/, /.vscode/, /Thumbs.db | medium; /Thumbs.db low |
| Working files of AI coding assistants (15) | /.aider.chat.history.md, /.claude/settings.local.json, /.mcp.json, /CLAUDE.md, /.cursorrules | high for chat histories and local settings, medium for project notes, low for instruction files |
| WordPress (6) | /wp-content/debug.log, /wp-admin/setup-config.php, /wp-cron.php | debug.log critical, setup-config.php high, the rest medium or low |
.well-known (3) | /.well-known/security.txt, /.well-known/openid-configuration | security.txt is reported when missing; the other two low |
What a positive result means
A path counts only when the server answers it with a 2xx status. Redirects are not followed, and 401, 403 and 404 never count. When the site answers addresses that do not exist with something other than a 404 (many single-page apps answer 200 to everything), or when the path is a data file (.env, .sql, .zip, .git/), the scanner also fetches the first 64 KB and keeps the hit only if it differs from the site's answer to two random addresses that cannot exist and, for a data file, is not an HTML page. A positive result therefore means the server served content at that address. Apart from a public phpinfo() page, the content is not parsed or kept. A path the sweep could not ask, or that a bot shield answered, is listed as not checked, not as clean.
Requests made only on some platforms
- WordPress, when the site runs it:
/xmlrpc.php(a GET, then the onesystem.listMethodscall),/wp-json/wp/v2/users(the first 4 KB),/?author=1to/?author=6(the redirect's Location header is read, not followed),readme.htmlwhen the page does not state the version, 8 backup names ofwp-config.phpand thereadme.txtof 38 common plugins (HEAD). - WHMCS, when the site runs it: 20 of its public pages and well-known leftovers (the installer,
configuration.php, SQL dumps,/attachments/,/downloads/). - Hosting panel: 16 addresses where control panels answer (
/cpanel/,/plesk-stat/,/webuzo/and others). A panel is named only when the answer names it: a redirect to the panel's own port or subdomain, or a login page that carries the panel's name. A status code alone is not evidence.
Known-vulnerable software
Versions come from what the site itself publishes (generator tags, asset paths, readme files). WordPress, Drupal and Joomla older than 6.5, 10 and 5 are reported as outdated (high). WordPress core is checked against the WordPress.org release API; WordPress plugins and themes against the Wordfence vulnerability database, whose CVSS rating sets the severity; JavaScript libraries against the Retire.js database. A version the site does not disclose cannot be checked, and the report says so.
Password-protected sites
Before the other checks, the scanner looks at what answered: HTTP basic authentication, a Vercel or Netlify protected deployment, Cloudflare Access, a Shopify storefront password, a WordPress maintenance page or another login page. The first five can be passed with the password the owner enters under Advanced options; the password is stored encrypted and deleted as soon as the audit starts. A maintenance page or an unknown login cannot be passed from outside, so the audit stops there and says why, instead of reporting the login page as the site.
Performance
Lighthouse 12 runs on the first page twice, one run after the other: mobile (a 412 x 823 screen, 4x CPU slowdown, 150 ms round trip, 1.6 Mbit/s) and desktop (1350 x 940, no CPU slowdown, 40 ms, 10 Mbit/s). These are Lighthouse's standard profiles. The performance score is the mobile score; the desktop score takes its place only when the mobile run fails. A run measured while the scanner's own processor was busy is repeated once.
Every Lighthouse audit that scores below 0.9 becomes a finding, with Lighthouse's own explanation. The severity follows the audit's score: Largest Contentful Paint, for example, is high below 0.5 and medium otherwise.
The good and poor bands next to each metric are the thresholds Google publishes: Largest Contentful Paint 2.5 and 4 s, Cumulative Layout Shift 0.1 and 0.25, First Contentful Paint 1.8 and 3 s, Total Blocking Time 200 and 600 ms (Lighthouse's own), and for field data Interaction to Next Paint 200 and 500 ms and Time to First Byte 0.8 and 1.8 s.
When Google's Chrome UX Report holds field data for the site's origin (28 days of real Chrome visits, phone and desktop), the report shows it next to the lab numbers. It is not part of the score.
One more request and one browser navigation measure how long the server takes to deliver the page and whether the page keeps the browser busy. Slower than 1.2 seconds is a hosting finding (medium), slower than 2.5 seconds high. A page heavier than 5 MB (high), text sent without compression (medium) and gzip where Brotli would do better (info) are reported as well.
Search (SEO)
Read from the page's HTML and, when the page builds its content with JavaScript, from the rendered page as well:
- the title (missing: high; under 10 or over 70 characters: low) and the meta description (missing: medium; under 50 or over 200 characters: info);
- the canonical link, Open Graph (title and image) and
twitter:card; - headings (no H1, or more than one) and images without alt text (when more than 20% lack it; reported under accessibility);
- structured data: whether JSON-LD is present, then its required fields and the fields Google uses for rich results, checked against about 40 schema.org types;
- robots.txt and the sitemap, including up to 5 child sitemaps;
- broken links: internal links (more than 5 broken: high) and a sample of up to 50 external ones (low). An answer that means "not now" (429, 408, 503, a bot check, a firewall refusing every request) is reported as not checked, not as broken;
- in a deeper audit, hreflang pairs: each alternate page has to exist and link back.
The SEO score is Lighthouse's SEO score; the checks above are findings and do not change it.
Accessibility
axe-core 4.11 runs the rules tagged for WCAG 2.0, 2.1 and 2.2 at levels A and AA, plus the target-size rule, on each audited page: the first page in a standard audit, up to the first 10 crawled pages in a deeper one, 25 seconds per page. A rule that needs a person's judgement (contrast over an image, for example) is listed as needing review, not as passed or failed.
The accessibility score starts from Lighthouse's accessibility score and deducts half a severity weight for each axe finding (see Severity). Automated rules cover only part of what WCAG asks, so a clean result here is not a statement of conformance.
Hosting
- server response time (see Performance) and whether the server speaks HTTP/2; HTTP/3 is recorded when the server advertises it (Alt-Svc), not tested;
- IPv6: whether the domain publishes an AAAA record;
- hosting panels and billing systems (12 panels such as cPanel, Plesk and DirectAdmin; WHMCS, Blesta, HostBill and ClientExec), from headers, cookies, the page and the addresses listed under Sensitive paths;
- where the server is: country and network, from a local IP database;
- the domain's registration and expiry, from RDAP (for .ro, from the registry's WHOIS, where the expiry is estimated from the registration date).
DNS and mail
Records read: A, AAAA, NS, MX, TXT, CAA, SPF, DMARC, DKIM for 7 common selectors (default, google, selector1, selector2, k1, mail, dkim), BIMI, TLS-RPT and MTA-STS (the record and the policy file). DNSSEC is checked through a public DNS-over-HTTPS resolver.
Reported: SPF that lets any server send or needs more than 10 DNS lookups, DMARC that only monitors (p=none) or uses relaxed alignment, DKIM keys of 1024 bits or less, DNSSEC off, no CAA record, and whether the domain is on the HSTS preload list (hstspreload.org). Most of these count toward the security score.
For up to 6 mail servers, by priority: one connection to port 25 that reads the greeting, sends EHLO and checks that STARTTLS is offered, without completing a TLS session or sending mail. Each server is also looked up in 6 public blocklists (Spamhaus ZEN, Barracuda, SpamCop, UCEPROTECT levels 1 to 3).
Technologies
Technologies are recognised from the page, its headers and its asset paths, with a signature list derived from the open-source Wappalyzer forks and the build markers of Next.js, Nuxt, SvelteKit, Remix, Gatsby, Astro, Angular, Eleventy, Hugo and Jekyll. Versions past their end of life are flagged with data from endoflife.date. Known-vulnerable versions are covered under Security.
Visual
In Chromium, the first page is captured at 360, 768, 1280 and 1920 pixels wide (the visible area, not the full page). From the 1280 pass come the eight dominant colours, the web-font families the stylesheets declare, browser console errors and uncaught exceptions during the load, the cookies set during the load (first- and third-party) and requests to 22 known tracking domains.
Carbon
Grams of CO2 per visit, from the page weight Lighthouse measured, with version 4 of the Sustainable Web Design model in CO2.js (Green Web Foundation). Whether the host runs on green energy comes from the Green Web Foundation's check, as a yes or a no; an unknown answer counts as no. The estimate is for a first visit. There is none when Lighthouse could not run.
AI check
Unless the box is unticked, the audit also looks for signs that the page was generated or assembled by an AI builder or a coding agent: builder fingerprints, 49 named page elements with quoted evidence, and how the CSS was put together. The verdict is one of five:
- Confirmed: a build artefact names the tool (a generator tag, a builder script, a served CLAUDE.md).
- Likely: two strong stack signals, or a cluster of measured component tells with few authored signals.
- Possibly: stock components were found; professionals use them too, so no percentage is shown.
- No evidence found: nothing indicating a generated build. Absence of evidence is not evidence of absence: it came up on 9% of the AI-built sites we measured and on about a third of those made with a coding agent.
- Not measured: the page could not be fetched or rendered, or it has too little content to measure (a canvas app, an empty shell).
On sites whose origin we know, a Confirmed or Likely verdict was right 98% of the time and caught 86% of AI-built sites, but only about a third of the sites whose makers used a coding agent. Sites built by designers on the same component kit land in Possibly by design.
Automated estimate from public signals, not a statement of fact about a site or its authors.
Severity
Each check has a fixed severity in the scanner's code, chosen for what the problem lets someone do or what it costs a visitor. A few depend on the evidence: an exposed path takes the severity of its kind of file, a cookie gap is medium only when Secure is missing, the CSP finding follows the grade, and a known vulnerability takes the rating its database gives it. The scale is the scanner's own; it is not CVSS.
| Level | Used for | Examples |
|---|---|---|
| critical | secrets, source code or data open to anyone, or no working HTTPS | a served .env or database dump, a readable .git/, an expired certificate, a site not on HTTPS |
| high | a weakness attackers routinely look for, or a broken basic | no HSTS, mixed content, a directory listing, a reachable phpMyAdmin or server-status page, an outdated CMS, a certificate expiring within 14 days, a page over 5 MB |
| medium | a missing layer of protection, or a leak that helps an attacker plan | no CSP or a grade-F CSP, no Subresource Integrity, public dependency manifests, DNSSEC off, a server slower than 1.2 seconds |
| low | hardening and hygiene | no X-Content-Type-Options, no security.txt, not on the HSTS preload list, cookies without HttpOnly or SameSite |
| info | context, nothing to fix on its own | detected technologies, no Referrer-Policy or Permissions-Policy, the carbon estimate |
How the scores are computed
- Performance and SEO: Lighthouse's mobile scores.
- Accessibility: Lighthouse's accessibility score, minus half the weight of each axe finding.
- Security and hosting: 100, minus a weight for each finding in that category: info 1, low 3, medium 8, high 18, critical 35. One finding takes off at most 25 points, and low and info findings together at most 15.
- Security also earns back up to 30 points for measured good practice (TLS 1.3 +6, forward secrecy +4, a strong key +3, HTTP/2 +3, OCSP stapling +2, TLS 1.0 and 1.1 refused +1 each, MTA-STS +5 in enforce mode or +2 in testing, DMARC +5 at quarantine or reject or +2 at none, DNSSEC +2, CAA +2, TLS-RPT +2), and does not drop below 30 unless the site has no HTTPS, an expired certificate or a failed TLS handshake.
- Overall: the average of the five scores that were measured.
Every check is listed
The report lists every check, including those that found nothing, each in one of five states: found something, passed, needs review, did not apply (with the reason, such as "no mail server"), or could not run (with the reason, such as a timeout, a bot check or a rate limit). The scanner has 107 named checks of its own, plus one row for each probed path, Lighthouse audit, axe rule and AI-check element.
A slow or refusing site is never read as a clean one. Waits stretch with the site's own response time (see Load on the audited server), an answer that means "not now" is retried once and then marked not checked, and if the site stops answering partway (this is checked before Lighthouse, the link check and the browser checks), the remaining stages are skipped and the report says the site stopped answering.
What an automated external audit cannot find
- Anything behind a login: account areas, admin panels, checkout. The scanner sees what an anonymous visitor sees.
- Flaws in the application's logic, access control or input handling (injection, broken authorisation, insecure direct object references). Finding them takes a person testing with written permission.
- The inside of the server: its patch level, file permissions, configuration that does not show in a response, other sites on the same machine.
- Vulnerable software that does not reveal its version, and vulnerabilities not yet in a public database.
- Pages that were not audited: a standard audit reads one page, a deeper audit up to 100.
- Changes after the audit: a report describes the site at one moment.
No findings therefore means that none of these checks found a problem, not that the site is secure. A finding records a condition and what was observed; on its own it does not prove that the condition can be exploited.
What is kept, and for how long
- Reports (the address, scores, findings and evidence) stay at their link while the service runs. The link holds 32 random hexadecimal characters, about 3.4 x 1038 possibilities. Reports are not listed or published, and search engines are asked not to index them.
- A report's PDFs, screenshots and HTML copy are deleted after 30 days; a PDF is printed again when someone asks for it.
- The password of a protected site is stored encrypted and deleted when the audit starts, or after 60 minutes if it never does.
- After 12 months, the personal data found on the audited site leaves the report: contact addresses and phone numbers become counts, the street address and map point are removed, and WordPress names and cookie values stay masked for good.
- The IP address and browser of whoever asked for the audit are kept 30 days; the email address for the "report ready" message is removed once the message is sent, or after one day.
- The full terms are in the Privacy policy.
Questions about the scanner: audit@crocky.host