Webskan for developers · reference

Finding ids

Everything audit_website, recheck_website and the PDF audit can report, by id. Use this page to decide what a CI job fails on, to tell an agent which findings are its job and which are the hosting provider's, and to look up what a fixed / new line in a re-check actually means.

How ids work

  • Shape. area.rule, optionally followed by @target: sec.header.hsts is site-wide, seo.h1.missing@/pricing/ is about one page, links.broken@https://…/file.zip about one link, a11y.color-contrast@/#dark about one axe rule on one page in one colour scheme. The part before @ is listed below; the target is whatever the finding is about.
  • Stable. The same problem produces the same id in every run, which is what makes the re-check diff (fixed / open / new / unverified) and the weekly Monitor comparison possible. Titles and details are human text and may change; ids do not.
  • Severity is one of high, medium, low, info. Some rules have a fixed severity, others pick it from a threshold (listed below). info is context, never a defect.
  • Areas: sec security · perf performance · seo technical SEO · a11y accessibility · ux usability · journey customer journeys in a real mobile browser · links broken links.
  • Unverified in a re-check means the target could not be checked this time (link blocked or timed out, page returned an error), so it is reported neither as fixed nor as new.
  • Performance ids (perf.*) move with network conditions. Gate on them with a threshold you accept, not on their mere presence; the Monitor's weekly diff excludes them for the same reason.

Security – sec.*

IdSeverityFires whenUsual fix
sec.tls.invalidhighThe TLS handshake fails or the certificate does not match the host.Issue or renew the certificate for every name the site answers to (apex and www); check the chain.
sec.tls.expiringhigh < 7 days, else medium (fires under 21 days)The certificate expires soon.Fix automatic renewal (certbot timer, host panel, CDN) – do not just renew by hand.
sec.https.no-redirecthighhttp:// does not redirect to https://.301 from port 80 to https at the server or CDN level.
sec.www.no-redirectmediumThe www or non-www variant serves the site instead of redirecting.Pick one canonical host, 301 the other.
sec.exposed-file@/pathhigh for .git, .env, SQL dumps, backup archives, wp-config copies; medium for phpinfo, logs, Adminer; low for .DS_StoreA well-known sensitive path returns 200 and the body matches the expected content signature (a “200 for everything” site does not trigger it).Delete the file from the web root, keep backups outside it, and deny these paths in the server or CDN config. Rotate any credential that was inside.
sec.header.hstsmediumNo Strict-Transport-Security on the https response.Strict-Transport-Security: max-age=31536000; includeSubDomains once https is stable everywhere.
sec.header.xfomediumNeither X-Frame-Options nor CSP frame-ancestors is set.X-Frame-Options: SAMEORIGIN or Content-Security-Policy: frame-ancestors 'self'.
sec.header.xctolowNo X-Content-Type-Options: nosniff.Add the header; one line, no side effects.
sec.header.referrerlowNo Referrer-Policy.Referrer-Policy: strict-origin-when-cross-origin.
sec.header.csplowNo Content-Security-Policy.Start in Content-Security-Policy-Report-Only, tighten, then enforce. Matters most with logins, payments and user content.
sec.header.permissionslowNo Permissions-Policy.Permissions-Policy: camera=(), microphone=(), geolocation=() unless you use them.
sec.leak.<header>lowServer, X-Powered-By or similar reveals a software version.server_tokens off (nginx), ServerTokens Prod (Apache), expose_php = Off, strip X-Powered-By.
sec.mixed-content@/pagehighAn https page loads scripts, styles, images or frames over http://.Use protocol-relative or https URLs; in a CMS, search-and-replace the old http origin in content and theme settings.
sec.form.insecure-action@/pagehighA form's action points to an http:// URL.Change the action to https (or a relative path).

Performance – perf.*

Measured with Lighthouse on an emulated mid-range phone over simulated 4G, from the audit location. Values vary between runs; gate with a margin.

IdSeverityFires whenUsual fix
perf.lighthouse.incompleteinfoThe page did not finish loading within 45 s. No other perf.* finding is produced from that run, because its metrics would be inflated.Find the resource that never resolves from outside your network (third-party script, blocked geo); check the page in PageSpeed Insights.
perf.score.mobilehigh < 50, medium 50–89Lighthouse mobile performance score below 90.Work the specific perf.* findings below; the score follows.
perf.lcp.mobilehigh > 4 s, medium > 2.5 sLargest Contentful Paint above the Google threshold.Preload the hero image or font, serve it in a modern format and the right size, cut render-blocking CSS/JS, fix slow TTFB.
perf.cls.mobilehigh > 0.25, medium > 0.1Cumulative Layout Shift above threshold.Width/height on images and embeds, reserve space for banners and ads, font-display: optional or matching fallback fonts.
perf.tbt.mobilehigh > 600 ms, medium > 300 msTotal Blocking Time above threshold (the lab proxy for INP).Defer or remove heavy scripts, split long tasks, load tag managers and chat widgets after interaction.
perf.weight.mobilemediumTotal transferred bytes above 3 MB.Compress and resize images, drop unused scripts and fonts.
perf.heavy-script@file.jsinfoTBT is above 300 ms; names the single script with the most main-thread time.Profile that file; it is usually the first thing to defer or remove.
perf.blocking-scriptsmedium ≥ 3 scripts, else lowSynchronous scripts in <head> without defer/async.Add defer, or move the script to the end of <body>.
perf.opp.<lighthouse-audit>medium ≥ 1.5 s saving, else lowA Lighthouse opportunity estimates ≥ 0.5 s or ≥ 50 KB of savings (e.g. perf.opp.render-blocking-resources, perf.opp.uses-responsive-images).Follow the Lighthouse audit of that name.
perf.images.oversized@/pagelowImages have several times more pixels than their rendered size on a phone.srcset/sizes or an image CDN; export at the displayed size.
perf.images.no-dims@/pagelowThree or more images without width/height on a page whose CLS is above 0.1.Add the attributes or aspect-ratio in CSS.
perf.slow-response@/pagemedium > 5 s, low > 2.5 sTime to first byte of the HTML above threshold during the crawl.Page cache, object cache, fewer plugins, better hosting, a CDN in front.
perf.compression.nonemediumThe HTML response is not gzip/brotli compressed.Enable compression at the server or CDN.

Technical SEO – seo.*

IdSeverityFires whenUsual fix
seo.title.missing@/pagehigh on the home page, medium elsewhereEmpty or missing <title>.A unique title per page; in a CMS fix the template.
seo.title.longlowTitles longer than 70 characters on one or more pages (one finding listing them all).Shorten the title template; stop appending the site name to long post titles.
seo.title.duplicate@<title>mediumSeveral crawled pages share one title.Unique titles; check paginated and filtered pages.
seo.desc.missing@/pagemedium on the home page, low elsewhere (blog/tag/category/pagination pages are skipped)No meta description.Write one per commercial page; let Google pick for the rest.
seo.desc.longinfoDescriptions longer than 170 characters (one finding listing them all).Cosmetic – trim if convenient.
seo.desc.duplicate@<text>lowSeveral pages share one description.Per-page descriptions.
seo.h1.missing@/pagelowNo <h1>.One H1 per page naming what the page is.
seo.h1.multiple@/pageinfoMore than one <h1>.Demote the extra ones (logo in H1 is the usual culprit).
seo.noindex@/pagemediumA crawled page carries noindex.Fine if intentional (thank-you pages). After a launch, check it is not a staging leftover.
seo.robots.blocks-allhighrobots.txt has Disallow: / for all agents.Remove it; this is the classic staging-to-production leftover.
seo.robots.missinginfoNo robots.txt.Optional; add one pointing to the sitemap.
seo.sitemap.missinglow (medium when more than 10 pages were crawled)No sitemap found at the usual locations or in robots.txt.Generate one and reference it in robots.txt.
seo.sitemap.errormediumThe declared sitemap returns an error or no URLs.Fix the generator or the path; check it is not blocked by a bot rule.
seo.canonical.missinglowThe home page has no rel="canonical".Self-referencing canonical on every page.
seo.canonical.mismatch@/pagemediumA page's canonical points to a different URL.Intentional for duplicates; otherwise a staging domain or http canonical left in the template.
seo.og.missinglowFewer than two Open Graph tags on the home page.og:title, og:description, og:image (1200×630).
seo.sd.missinginfoNo JSON-LD structured data on the home page.Optional: Organization/LocalBusiness, Product, Article.
seo.sd.invalid@/pagelowA ld+json block is not valid JSON.Fix the JSON; validate with the Rich Results Test.
seo.lang.missinglowNo lang attribute on <html>.<html lang="en">.
seo.viewport.missinghighNo meta viewport.<meta name="viewport" content="width=device-width, initial-scale=1">.
seo.viewport.no-zoommediumuser-scalable=no or maximum-scale=1.Remove them; zoom is a WCAG requirement.
seo.img.alt@/pagelowImages without an alt attribute.Describe meaningful images; alt="" for decorative ones.
seo.page.error@/pagehigh for 5xx or no response, medium for 4xxAn internal link leads to a page that returns an error.Fix the page or the link; add a redirect if the URL changed.
seo.lighthouse.<audit>lowA Lighthouse SEO audit fails that is not already covered above (e.g. seo.lighthouse.link-text, seo.lighthouse.crawlable-anchors).Follow the Lighthouse audit of that name.

Accessibility – a11y.*

IdSeverityFires whenUsual fix
a11y.<axe-rule>@/page (the home page is tested in both colour schemes, so its ids end in #light or #dark)high for axe impact critical, medium for serious, low for moderate and minoraxe-core reports a WCAG 2.1 AA violation on a crawled page, e.g. a11y.color-contrast@/#dark, a11y.label@/contact/, a11y.link-name@/, a11y.image-alt@/about/. The finding carries the axe helpUrl and sample nodes.Follow the axe rule page linked in helpUrl; contrast, form labels and link names cover most of the volume.
a11y.score.mobilehigh < 70, medium 70–89Lighthouse accessibility score below 90.Work the a11y.* rule findings; automated tests find roughly a third of barriers, so a 100 is not “accessible”.

Usability – ux.*

Measured in a real mobile browser (390 px) on the crawled pages.

IdSeverityFires whenUsual fix
ux.overflow@/pagemediumThe page is more than 8 px wider than the phone screen (horizontal scrollbar).Find the element with a fixed width or a non-wrapping table/code block; max-width: 100%, overflow-wrap.
ux.small-text@/pagelowVisible paragraph text under 13 px, or more than 30 % of the paragraphs are under 14 px. Text that is off-screen, clipped or in a closed widget is not counted.Body text ≥ 16 px on mobile.
ux.js-errors@/pagemedium ≥ 3 errors, else lowUncaught errors in the console while loading.Open the console on a phone-sized window; the first error usually explains the rest.
ux.failed-requests@/pagemedium ≥ 3, else lowResources fail to load (404 images, blocked scripts, missing fonts).Fix or remove the references.
ux.cookie.blockingmediumA consent dialog covers most of the first screen on mobile.A bottom bar instead of a full-screen modal.
ux.cookie.no-rejectmediumThe consent dialog has no reject option at the same level as accept.“Reject” or “Necessary only” next to “Accept” (EDPB guidance).
ux.phone.not-clickable@/pagemediumA phone number appears as text and the page has no tel: link.<a href="tel:+48…">.
ux.email.not-clickable@/pagemedium on the contact page, low elsewhereAn e-mail address appears as text without a mailto: link.<a href="mailto:…"> or a form.
ux.no-contacthighNo contact form and no clickable phone or e-mail on the key pages.A contact page with a short form and a tel:/mailto: link.
ux.contact.form-missinglowContact is only an e-mail address written as text.A mailto: link or a three-field form.
ux.form.unlabelled_fields@/pagemediumVisible form fields without an associated <label>, aria-label or title (placeholder only). Hidden fields and honeypot traps (off-screen, aria-hidden, tabindex=-1) are ignored; the field names are listed in the finding.<label for> or aria-label on every field.
ux.form.too_many_fields@/pagemediumThe contact form has more than 6 fields.Name, e-mail or phone, message; everything else optional or removed.
ux.form.no_required@/pagelowNo field is marked required.required on the fields you need.
ux.form.no_spam_protection@/pagelowNo CAPTCHA, Turnstile, hCaptcha or honeypot field (a hidden text input such as website/url) detected in the contact form.A honeypot field first; Turnstile if spam continues.
ux.form.no_consent@/pagelowA form collecting e-mail has no privacy notice or consent checkbox.One sentence with a link to the privacy policy under the submit button.

Customer journeys – journey.*

The audit walks the site like a customer on a phone: opens the menu, searches for a word from the page, finds the contact form, adds a product to the cart and reaches checkout (nothing is submitted or paid), and requests a non-existent address. Each finding carries a screenshot.

IdSeverityFires whenUsual fix
journey.nav.failhighTapping the mobile menu button reveals no links.Usually a JavaScript error or a CSS breakpoint mismatch; see ux.js-errors.
journey.nav.not_foundmediumNo menu or menu button is visible on the first mobile screen.A visible hamburger button or inline links above the fold.
journey.search.failmediumSite search ends on an error page.Fix the search endpoint; test with a plain word.
journey.search.warnlowSearching for a word taken from the page's own heading returns nothing.Reindex, or check the search ignores accents and word forms.
journey.404.soft404mediumA random address returns 200.Return a real 404 status with the error page.
journey.404.blankhighThe error page is empty.A 404 page with menu, search and a link home.
journey.404.no_way_outmediumThe error page has no navigation.Same as above.
journey.404.server_errorhighA non-existent address returns 5xx.Fix the router or rewrite rule that crashes on unknown paths.
journey.shop.failhighProduct → cart → checkout does not reach the checkout page; the reason is in the title.Reproduce on a phone with a fresh session.
journey.shop.login_requiredhighCheckout demands an account.Enable guest checkout.
journey.shop.no_payment_methods_visiblemediumThe checkout page shows no payment methods.Show accepted methods before the customer types anything.
journey.shop.many_fieldsmediumCheckout has more than 14 fields.Hide optional fields, enable autofill, drop the account-creation block.
journey.shop.add_button_disabled_(out_of_stock_or_variant)lowThe tested product could not be added (out of stock or variant required).Check the customer gets a clear message; not a defect by itself.

What to gate on

  • Always fail: any high. They are deterministic (redirects, exposed files, mixed content, robots, viewport, broken journeys) or far past a threshold.
  • Fail on new medium findings in a re-check, not on all medium findings – legacy debt should not block today's deploy, regressions should.
  • Do not gate on info, on perf.* without a margin, or on links.broken@ for external hosts you do not control (watch them in the Monitor instead).
  • The CI script and the CLAUDE.md block that implement this are in Make your coding agent verify every deploy.

Run it on your site

claude mcp add --transport http webskan https://webskan.com/mcp

Then ask the agent to audit https://your-site and fix every high finding. Free: 2 whole-site audits per IP per day. Tools, limits and client configs · Developer plan for 30 audits a day and re-checks without the per-host window.