[{"id":"crawl.http-status","category":"crawl","title":"The audited URL answers with HTTP 200","severity":"critical","confidence":"high","summary":"The URL ends in HTTP 200 after redirects.","why_it_matters":"A page that does not answer with 200 cannot be indexed. Every other check on this page is irrelevant if the crawler never receives the document.","how_to_fix":"Fix the server response for this URL. If the page moved, update the links that point at it instead of relying on a redirect chain.","references":[{"label":"Google: Understand the HTTP response codes","url":"https://developers.google.com/search/docs/crawling-indexing/online-marketing-and-other-resources/http-response-codes"}],"docs_url":"https://abackaudit.com/checks/http-status","requires_browser":false,"informational":false},{"id":"crawl.redirect-chain","category":"crawl","title":"Redirects are short and do not loop","severity":"medium","confidence":"high","summary":"At most one redirect hop, and no redirect loops.","why_it_matters":"Every hop adds latency for the crawler and for the visitor, and long chains dilute link signals. Loops make a page unreachable.","how_to_fix":"Point links directly at the final URL. Keep one redirect at most, from the old address to the canonical one.","references":[{"label":"Google: Redirects and Google","url":"https://developers.google.com/search/docs/crawling-indexing/301-redirects"}],"docs_url":"https://abackaudit.com/checks/redirect-chain","requires_browser":false,"informational":false},{"id":"crawl.https","category":"crawl","title":"The site is served over HTTPS","severity":"high","confidence":"high","summary":"The final URL uses HTTPS and the http version redirects to it.","why_it_matters":"HTTPS is a confirmed ranking signal and browsers flag insecure pages. An http page that does not redirect leaves two addresses for the same content.","how_to_fix":"Serve the whole site over HTTPS and redirect every http request to the https version with a 301.","references":[{"label":"Google: Secure site migration","url":"https://developers.google.com/search/docs/crawling-indexing/http-https-migration"}],"docs_url":"https://abackaudit.com/checks/https","requires_browser":false,"informational":false},{"id":"crawl.host-canonical","category":"crawl","title":"One canonical host answers for every variant","severity":"medium","confidence":"high","summary":"The www and non-www variants, with and without a trailing slash, resolve to a single form.","why_it_matters":"When several addresses serve the same content, search engines have to guess which one to index and which one to keep in results.","how_to_fix":"Pick one host and one trailing slash style, then redirect every other variant to it with a 301.","references":[{"label":"Google: Site consolidation and duplicate URLs","url":"https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls"}],"docs_url":"https://abackaudit.com/checks/host-canonical","requires_browser":false,"informational":false},{"id":"crawl.robots-txt","category":"crawl","title":"robots.txt exists and is usable","severity":"high","confidence":"high","summary":"The file exists, returns 200, parses, and stays under 500 KiB.","why_it_matters":"robots.txt is how a site tells crawlers where to go. A missing or broken file removes that control and hides real crawl problems.","how_to_fix":"Publish a plain text robots.txt at the root of the site and keep it small. Test it with the robots testing tool before deploying.","references":[{"label":"Google: Introduction to robots.txt","url":"https://developers.google.com/search/docs/crawling-indexing/robots/intro"},{"label":"RFC 9309: Robots Exclusion Protocol","url":"https://www.rfc-editor.org/rfc/rfc9309"}],"docs_url":"https://abackaudit.com/checks/robots-txt","requires_browser":false,"informational":false},{"id":"crawl.robots-blocking","category":"crawl","title":"The default robots rules do not block the site","severity":"critical","confidence":"high","summary":"The wildcard rules in robots.txt do not block the audited URL or the key paths of the site.","why_it_matters":"A blanket Disallow in the wildcard group hides the whole site from every crawler that does not name itself in robots.txt. This is the single most common reason a site cannot be indexed.","how_to_fix":"Remove the blocking rule, or scope it to the paths that really should be private. Test the file against the audited URL before publishing it.","references":[{"label":"Google: How robots.txt works","url":"https://developers.google.com/search/docs/crawling-indexing/robots/intro"}],"docs_url":"https://abackaudit.com/checks/robots-blocking","requires_browser":false,"informational":false},{"id":"crawl.sitemap","category":"crawl","title":"A valid sitemap is published and declared","severity":"high","confidence":"high","summary":"The sitemap exists, parses as XML, is declared in robots.txt, and its sample urls answer with 200.","why_it_matters":"A sitemap is the clearest list of pages a site wants indexed. It speeds up discovery and makes coverage visible.","how_to_fix":"Generate a sitemap with the urls that should be indexed, publish it at /sitemap.xml and add a Sitemap line to robots.txt.","references":[{"label":"Google: Build and submit a sitemap","url":"https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap"}],"docs_url":"https://abackaudit.com/checks/sitemap","requires_browser":false,"informational":false},{"id":"crawl.noindex","category":"crawl","title":"The page is not marked as noindex","severity":"critical","confidence":"high","summary":"No noindex directive appears in the meta robots tag or in the X-Robots-Tag header.","why_it_matters":"noindex is a direct instruction to keep the page out of search results. It is often left behind by a staging deployment.","how_to_fix":"Remove the noindex directive from the meta robots tag and from the X-Robots-Tag header. Check the deployment configuration as well, not only the template.","references":[{"label":"Google: X-Robots-Tag header","url":"https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag/x-robots-tag"},{"label":"Google: Robots meta tag","url":"https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag"}],"docs_url":"https://abackaudit.com/checks/noindex","requires_browser":false,"informational":false},{"id":"crawl.canonical","category":"crawl","title":"The canonical link points at the page itself","severity":"high","confidence":"high","summary":"A rel=canonical link exists, is absolute, matches the page and does not point at a redirect or an error.","why_it_matters":"The canonical link tells search engines which address is the real one. A wrong canonical can move the ranking of a page to another address.","how_to_fix":"Set the canonical link to the absolute, final URL of the page, one per page, and make sure it does not point at a redirecting or error address.","references":[{"label":"Google: Specify a canonical URL","url":"https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls"}],"docs_url":"https://abackaudit.com/checks/canonical","requires_browser":false,"informational":false},{"id":"crawl.internal-links","category":"crawl","title":"Internal links resolve","severity":"medium","confidence":"high","summary":"Sampled internal links answer with 2xx, or with a single redirect.","why_it_matters":"Broken internal links waste crawl budget and strand visitors. They are also a strong signal that the site is not maintained.","how_to_fix":"Fix or remove the broken links. Templates that build urls from ids should be checked first, they fail the most often.","references":[{"label":"Google: Links and Google Search","url":"https://developers.google.com/search/docs/crawling-indexing/links-crawlable"}],"docs_url":"https://abackaudit.com/checks/internal-links","requires_browser":false,"informational":false},{"id":"ai.crawler-access","category":"ai","title":"AI crawlers are allowed in robots.txt","severity":"medium","confidence":"high","summary":"What robots.txt says about each known AI crawler, classified as training, search or user triggered.","why_it_matters":"Robots.txt is the only place a site states its policy towards AI crawlers. Blocking an AI search crawler removes the site from that assistant, blocking a training crawler is a legitimate choice.","how_to_fix":"Decide per crawler class. Allowing training crawlers while blocking search crawlers is a valid combination, just make it explicit in robots.txt.","references":[{"label":"OpenAI: Crawlers and user agents","url":"https://platform.openai.com/docs/bots"},{"label":"Anthropic: Crawlers and user agents","url":"https://support.anthropic.com/en/articles/8896518"}],"docs_url":"https://abackaudit.com/checks/crawler-access","requires_browser":false,"informational":false},{"id":"ai.bot-fetch-parity","category":"ai","title":"AI crawlers receive the same page as everyone else","severity":"high","confidence":"high","summary":"The page returns the same status and substantially the same content to AI crawler user agents as to a default client.","why_it_matters":"Firewalls, bot management and CDN rules often block AI crawlers before robots.txt is even read. The site then looks open to humans and invisible to assistants.","how_to_fix":"Review the WAF, CDN and rate limiting rules that match the AI crawler user agents and allow them through.","references":[{"label":"Cloudflare: Bot management","url":"https://developers.cloudflare.com/bots/"}],"docs_url":"https://abackaudit.com/checks/bot-fetch-parity","requires_browser":false,"informational":false},{"id":"ai.llms-txt","category":"ai","title":"An llms.txt file describes the site","severity":"low","confidence":"medium","summary":"The file exists, is plain text or Markdown, has a title and a summary, and its links resolve.","why_it_matters":"llms.txt is an emerging convention for giving language models a curated map of a site. It is not used by any major crawler yet, so treat it as preparation rather than as a ranking factor.","how_to_fix":"Publish a plain text llms.txt at the root of the site with an H1 title, a short summary and links to the pages that matter.","references":[{"label":"llms.txt proposal","url":"https://llmstxt.org/"}],"docs_url":"https://abackaudit.com/checks/llms-txt","requires_browser":false,"informational":false},{"id":"ai.llms-full-txt","category":"ai","title":"An llms-full.txt file is available","severity":"info","confidence":"medium","summary":"The optional llms-full.txt file exists.","why_it_matters":"The full variant carries the complete text of the documentation of a site. It is entirely optional and never affects a score.","how_to_fix":"Add llms-full.txt next to llms.txt if the site has long form documentation worth exposing to models.","references":[{"label":"llms.txt proposal","url":"https://llmstxt.org/"}],"docs_url":"https://abackaudit.com/checks/llms-full-txt","requires_browser":false,"informational":true},{"id":"ai.content-without-js","category":"ai","title":"The main content is in the initial HTML","severity":"high","confidence":"high","summary":"The main content is present before JavaScript runs, not only after the page is rendered.","why_it_matters":"Most AI crawlers read the HTML they receive and do not execute JavaScript. Content that only exists after rendering is invisible to them.","how_to_fix":"Render the main content on the server. Client side rendering should enhance the page, not create it.","references":[{"label":"Google: Understand how Google Search indexes JavaScript","url":"https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics"}],"docs_url":"https://abackaudit.com/checks/content-without-js","requires_browser":true,"informational":false},{"id":"ai.markdown-alternate","category":"ai","title":"A Markdown version of the page is offered","severity":"info","confidence":"medium","summary":"The page offers a Markdown version through an alternate link or a .md URL.","why_it_matters":"Markdown is cheaper for a model to read than HTML. Offering it is a small, well received signal for AI tools.","how_to_fix":"Add a link rel=alternate type=text/markdown element that points at the Markdown version of the page.","references":[{"label":"Markdown for agents","url":"https://www.markdownlang.org/"}],"docs_url":"https://abackaudit.com/checks/markdown-alternate","requires_browser":false,"informational":true},{"id":"ai.agent-discovery","category":"ai","title":"Agent discovery files are published","severity":"info","confidence":"medium","summary":"Machine readable discovery files such as an MCP server card, an API catalog and an OpenAPI document are available.","why_it_matters":"Discovery files tell agents what a site exposes to machines. They are optional, but they remove guesswork for any integration.","how_to_fix":"Publish an OpenAPI document and, if the site exposes an MCP server, a server card under /.well-known/.","references":[{"label":"Model Context Protocol","url":"https://modelcontextprotocol.io/"}],"docs_url":"https://abackaudit.com/checks/agent-discovery","requires_browser":false,"informational":true},{"id":"page.title","category":"page","title":"The title tag is present and well sized","severity":"high","confidence":"high","summary":"A title exists, is roughly 30 to 60 characters, and is unique across the sampled pages.","why_it_matters":"The title is the strongest on-page signal and the headline of every search result. Missing or duplicated titles make pages compete with each other.","how_to_fix":"Give every page a title that names the page, uses about 30 to 60 characters, and differs from every other page on the site.","references":[{"label":"Google: Influencing your title links in search results","url":"https://developers.google.com/search/docs/appearance/title-link"}],"docs_url":"https://abackaudit.com/checks/title","requires_browser":false,"informational":false},{"id":"page.meta-description","category":"page","title":"The meta description is present and well sized","severity":"medium","confidence":"high","summary":"A meta description exists, is roughly 70 to 160 characters, and is unique across the sampled pages.","why_it_matters":"The description is the snippet under the title. It decides whether someone clicks, and duplicated descriptions waste the space.","how_to_fix":"Write one description per page, around 70 to 160 characters, that states what the page offers.","references":[{"label":"Google: Control your snippets in search results","url":"https://developers.google.com/search/docs/appearance/snippet"}],"docs_url":"https://abackaudit.com/checks/meta-description","requires_browser":false,"informational":false},{"id":"page.h1","category":"page","title":"The page has exactly one H1","severity":"medium","confidence":"high","summary":"Exactly one non-empty H1 element exists.","why_it_matters":"The H1 tells both readers and crawlers what the page is about. Several H1s make the hierarchy ambiguous, and an empty one carries no information.","how_to_fix":"Keep one H1 that names the topic of the page, and use H2 and H3 for the structure below it.","references":[{"label":"Google: Heading elements and document outline","url":"https://developers.google.com/search/docs/appearance/heading-elements"}],"docs_url":"https://abackaudit.com/checks/h1","requires_browser":false,"informational":false},{"id":"page.heading-order","category":"page","title":"The heading outline does not skip levels","severity":"low","confidence":"high","summary":"Headings descend one level at a time and none of them is empty.","why_it_matters":"A clean outline is how a screen reader user navigates a page. Skipped levels break that navigation and hide the structure from crawlers.","how_to_fix":"Move a heading up or down one level so that the sequence never jumps from H2 to H4.","references":[{"label":"W3C: Heading order technique","url":"https://www.w3.org/WAI/WCAG22/Techniques/html/H42"}],"docs_url":"https://abackaudit.com/checks/heading-order","requires_browser":false,"informational":false},{"id":"page.lang","category":"page","title":"The document declares its language","severity":"low","confidence":"high","summary":"The html element carries a valid BCP 47 language tag.","why_it_matters":"The language tag lets search engines target the right locale and lets screen readers pick the right voice.","how_to_fix":"Add a lang attribute to the html element, for example lang=\"en\" or lang=\"de\".","references":[{"label":"W3C: Declaring language in HTML","url":"https://www.w3.org/International/questions/qa-html-language-declarations"}],"docs_url":"https://abackaudit.com/checks/lang","requires_browser":false,"informational":false},{"id":"page.hreflang","category":"page","title":"The hreflang annotations are complete","severity":"medium","confidence":"high","summary":"When hreflang is used, the codes are valid, the links are reciprocal and an x-default exists.","why_it_matters":"hreflang tells search engines which language version to show. One sided annotations are ignored, which is worse than not using them at all.","how_to_fix":"List every language version on every version of the page, include an x-default entry, and make sure the links point at each other.","references":[{"label":"Google: Tell Google which regional versions to show","url":"https://developers.google.com/search/docs/specialty/international/localized-versions"}],"docs_url":"https://abackaudit.com/checks/hreflang","requires_browser":false,"informational":false},{"id":"page.open-graph","category":"page","title":"The Open Graph tags are complete","severity":"low","confidence":"high","summary":"og:title, og:description, og:image and og:url are present and the image resolves.","why_it_matters":"Open Graph tags control the preview a post gets when it is shared on social networks and in chat applications. Missing tags produce a bare link.","how_to_fix":"Add the four Open Graph tags to the page head and point og:image at an absolute, publicly reachable image.","references":[{"label":"The Open Graph protocol","url":"https://ogp.me/"}],"docs_url":"https://abackaudit.com/checks/open-graph","requires_browser":false,"informational":false},{"id":"page.twitter-card","category":"page","title":"The Twitter card tags are complete","severity":"low","confidence":"high","summary":"twitter:card is set and the fields that match the card type are present.","why_it_matters":"Without a card type, X shows a plain link. With it, the shared preview carries the title, the description and the image.","how_to_fix":"Set twitter:card to summary or summary_large_image and add twitter:title, twitter:description and twitter:image.","references":[{"label":"X: Cards markup","url":"https://developer.x.com/en/docs/twitter-for-websites/cards/overview"}],"docs_url":"https://abackaudit.com/checks/twitter-card","requires_browser":false,"informational":false},{"id":"page.image-alt","category":"page","title":"Images carry alternative text","severity":"medium","confidence":"high","summary":"Every image that carries meaning has an alt attribute.","why_it_matters":"Alternative text is the only description a screen reader user gets of an image, and it is also how image search understands the picture.","how_to_fix":"Describe the content of every meaningful image in its alt attribute. Use alt=\"\" for purely decorative images.","references":[{"label":"W3C: Images must have alternate text","url":"https://www.w3.org/WAI/WCAG22/Techniques/html/H37"}],"docs_url":"https://abackaudit.com/checks/image-alt","requires_browser":false,"informational":false},{"id":"page.viewport","category":"page","title":"The viewport allows zooming","severity":"high","confidence":"high","summary":"A viewport meta tag is present and does not disable zoom.","why_it_matters":"Blocking zoom makes the page unusable for people who need to magnify it, and it is a well known accessibility failure.","how_to_fix":"Use a viewport tag such as width=device-width, initial-scale=1 and leave user-scalable out, or set it to a value above 1.","references":[{"label":"W3C: Viewport meta tag","url":"https://www.w3.org/WAI/WCAG22/Techniques/html/H33"}],"docs_url":"https://abackaudit.com/checks/viewport","requires_browser":false,"informational":false},{"id":"page.thin-content","category":"page","title":"The page carries enough content","severity":"low","confidence":"low","summary":"The visible word count is not very low for the page type.","why_it_matters":"Pages with almost no text rarely satisfy a search intent. This check is a heuristic, so treat the number as a prompt to look rather than as a verdict.","how_to_fix":"Make sure the page answers the question it is about. If the content lives in an image, in a video or in a script, it is invisible to a crawler.","references":[{"label":"Google: Create helpful, people-first content","url":"https://developers.google.com/search/docs/fundamentals/creating-helpful-content"}],"docs_url":"https://abackaudit.com/checks/thin-content","requires_browser":false,"informational":false},{"id":"schema.json-ld-valid","category":"schema","title":"The JSON-LD blocks are valid","severity":"high","confidence":"high","summary":"Every JSON-LD block parses as JSON and declares a context and a type.","why_it_matters":"A block with a syntax error is ignored completely. A block without a context or a type is read but cannot be matched to anything.","how_to_fix":"Validate the JSON before it ships. A block must contain @context set to https://schema.org and at least one @type.","references":[{"label":"Google: Introduction to structured data markup","url":"https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data"},{"label":"Schema.org: JSON-LD","url":"https://schema.org/docs/jsonld.html"}],"docs_url":"https://abackaudit.com/checks/json-ld-valid","requires_browser":false,"informational":false},{"id":"schema.types-detected","category":"schema","title":"The structured data types found on the page","severity":"info","confidence":"high","summary":"Lists the schema.org types the page declares.","why_it_matters":"The list makes it easy to see what a crawler can understand about the page. It never affects the score.","how_to_fix":"Use the type that matches the page. Article for posts, Product for products, LocalBusiness for a business page.","references":[{"label":"Schema.org: Full hierarchy","url":"https://schema.org"}],"docs_url":"https://abackaudit.com/checks/types-detected","requires_browser":false,"informational":true},{"id":"schema.required-properties","category":"schema","title":"The structured data carries its required properties","severity":"medium","confidence":"high","summary":"Detected types include the required and recommended properties for that type.","why_it_matters":"A rich result is only granted when the required properties are present. Missing them leaves the markup unused.","how_to_fix":"Add the missing properties to the JSON-LD block. The check lists exactly which ones are absent.","references":[{"label":"Google: Structured data guidelines","url":"https://developers.google.com/search/docs/appearance/structured-data/sd-policies"}],"docs_url":"https://abackaudit.com/checks/required-properties","requires_browser":false,"informational":false},{"id":"schema.organization","category":"schema","title":"The site identifies its organisation","severity":"medium","confidence":"high","summary":"An Organization or WebSite entity is present. An Organization also declares a logo and its sameAs profiles.","why_it_matters":"The organisation entity is what connects the pages of a site to the brand behind them, in search results and in knowledge panels.","how_to_fix":"Add an Organization entity with name, url, logo and the sameAs profiles of the brand.","references":[{"label":"Google: Organization structured data","url":"https://developers.google.com/search/docs/appearance/structured-data/organization"}],"docs_url":"https://abackaudit.com/checks/organization","requires_browser":false,"informational":false},{"id":"schema.breadcrumbs","category":"schema","title":"The page declares its breadcrumbs","severity":"low","confidence":"high","summary":"A BreadcrumbList entity is present on nested pages.","why_it_matters":"Breadcrumbs replace the raw URL in the search result with a readable path, and they describe the structure of the site.","how_to_fix":"Add a BreadcrumbList entity with one ListItem per level, pointing at the real address of that level.","references":[{"label":"Google: Breadcrumb structured data","url":"https://developers.google.com/search/docs/appearance/structured-data/breadcrumb"}],"docs_url":"https://abackaudit.com/checks/breadcrumbs","requires_browser":false,"informational":false},{"id":"perf.ttfb","category":"perf","title":"The server answers quickly","severity":"medium","confidence":"medium","summary":"Time to first byte, measured from the audit location.","why_it_matters":"Every request starts with this delay. A slow server makes every other performance number worse and is visible to the visitor before anything is drawn.","how_to_fix":"Cache the response at the edge, keep the origin close to the visitor, and make sure the server does not do slow work before it answers.","references":[{"label":"web.dev: Time to First Byte","url":"https://web.dev/articles/ttfb"}],"docs_url":"https://abackaudit.com/checks/ttfb","requires_browser":false,"informational":false},{"id":"perf.lcp","category":"perf","title":"The largest element appears quickly","severity":"high","confidence":"medium","summary":"Largest Contentful Paint on a throttled mobile profile.","why_it_matters":"Largest Contentful Paint is what a visitor perceives as the loading of the page. It is one of the Core Web Vitals and it correlates with how long people stay.","how_to_fix":"Serve the largest element early. Preload the hero image, avoid lazy loading it, and keep the server response fast.","references":[{"label":"web.dev: Largest Contentful Paint","url":"https://web.dev/articles/lcp"},{"label":"web.dev: Core Web Vitals","url":"https://web.dev/articles/vitals"}],"docs_url":"https://abackaudit.com/checks/lcp","requires_browser":true,"informational":false},{"id":"perf.cls","category":"perf","title":"The page does not shift while it loads","severity":"medium","confidence":"medium","summary":"Cumulative Layout Shift on a throttled mobile profile.","why_it_matters":"Content that jumps after it appears is the most common complaint about a slow feeling website. A stable layout reads as a faster one.","how_to_fix":"Reserve the space for images, ads and embeds with explicit width and height, and avoid inserting content above what is already on screen.","references":[{"label":"web.dev: Cumulative Layout Shift","url":"https://web.dev/articles/cls"}],"docs_url":"https://abackaudit.com/checks/cls","requires_browser":true,"informational":false},{"id":"perf.tbt","category":"perf","title":"The page responds to input quickly","severity":"medium","confidence":"medium","summary":"Total Blocking Time, the lab stand in for Interaction to Next Paint.","why_it_matters":"A page that blocks the main thread feels frozen. Long tasks are usually caused by heavy scripts running during load.","how_to_fix":"Split long tasks, defer scripts that are not needed for the first view, and reduce the amount of JavaScript the page ships.","references":[{"label":"web.dev: Total Blocking Time","url":"https://web.dev/articles/tbt"}],"docs_url":"https://abackaudit.com/checks/tbt","requires_browser":true,"informational":false},{"id":"perf.page-weight","category":"perf","title":"The page is not too heavy","severity":"low","confidence":"medium","summary":"Total transfer size of the page and its subresources.","why_it_matters":"Every kilobyte costs bandwidth and battery. On a slow connection a heavy page is the difference between a visit and a bounce.","how_to_fix":"Serve smaller images, remove unused scripts and stylesheets, and prefer modern formats such as AVIF and WebP.","references":[{"label":"web.dev: Page weight","url":"https://web.dev/articles/performance-budgets-101"}],"docs_url":"https://abackaudit.com/checks/page-weight","requires_browser":true,"informational":false},{"id":"perf.compression","category":"perf","title":"Text assets are compressed","severity":"medium","confidence":"high","summary":"Text responses are served with gzip or Brotli.","why_it_matters":"Compression cuts the bytes on the wire by roughly three quarters for HTML, CSS and JavaScript. It is the cheapest speed win there is.","how_to_fix":"Enable Brotli or gzip compression for text responses at the CDN or the origin.","references":[{"label":"web.dev: Enable compression","url":"https://web.dev/articles/optimize-lcp"}],"docs_url":"https://abackaudit.com/checks/compression","requires_browser":false,"informational":false},{"id":"perf.caching","category":"perf","title":"Static assets are cached","severity":"low","confidence":"medium","summary":"Static assets carry an effective Cache-Control header.","why_it_matters":"A visitor who comes back should not download the same files again. Long lived cache headers turn repeat visits into a near instant page.","how_to_fix":"Give fingerprinted assets a long max-age, for example one year, and revalidate HTML with a short max-age or ETag.","references":[{"label":"web.dev: Caching best practices","url":"https://web.dev/articles/http-caching"}],"docs_url":"https://abackaudit.com/checks/caching","requires_browser":false,"informational":false},{"id":"perf.image-formats","category":"perf","title":"Images use modern formats","severity":"low","confidence":"medium","summary":"Images are served in modern formats and are not grossly oversized.","why_it_matters":"Images dominate the weight of a typical page. AVIF and WebP cut the size of a photograph by half compared with JPEG, at the same quality.","how_to_fix":"Serve AVIF or WebP with a fallback, resize images to the size they are displayed at, and compress them.","references":[{"label":"web.dev: Use modern image formats","url":"https://web.dev/articles/uses-optimized-images"}],"docs_url":"https://abackaudit.com/checks/image-formats","requires_browser":false,"informational":false},{"id":"perf.render-blocking","category":"perf","title":"The page is not blocked by head resources","severity":"medium","confidence":"medium","summary":"Render blocking CSS and JavaScript in the document head.","why_it_matters":"The browser cannot paint anything until it has read the blocking resources in the head. Every one of them delays the first pixel.","how_to_fix":"Inline the small amount of critical CSS, load the rest asynchronously, and defer scripts that are not needed for the first view.","references":[{"label":"web.dev: Eliminate render blocking resources","url":"https://web.dev/articles/render-blocking-resources"}],"docs_url":"https://abackaudit.com/checks/render-blocking","requires_browser":false,"informational":false},{"id":"a11y.contrast","category":"a11y","title":"Text meets the colour contrast requirement","severity":"medium","confidence":"medium","summary":"Text contrast follows WCAG 2 AA for normal and large text.","why_it_matters":"Low contrast text is unreadable for a large share of people, especially on a phone in daylight. It is the most common accessibility failure on the web.","how_to_fix":"Darken the text or lighten the background until the pair reaches 4.5 to 1 for normal text and 3 to 1 for large text.","references":[{"label":"WCAG: Contrast minimum","url":"https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html"}],"docs_url":"https://abackaudit.com/checks/contrast","requires_browser":true,"informational":false},{"id":"a11y.form-labels","category":"a11y","title":"Form controls have labels","severity":"high","confidence":"high","summary":"Every form control carries an accessible label.","why_it_matters":"A screen reader announces an unlabelled field as an empty edit box. People cannot tell what belongs in it.","how_to_fix":"Give every field a label element linked with the for attribute, or an aria-label or aria-labelledby attribute.","references":[{"label":"W3C: Labels and form controls","url":"https://www.w3.org/WAI/WCAG22/Techniques/html/H44"}],"docs_url":"https://abackaudit.com/checks/form-labels","requires_browser":false,"informational":false},{"id":"a11y.landmarks","category":"a11y","title":"The page uses landmarks","severity":"low","confidence":"high","summary":"The page has a main landmark and a sensible landmark structure.","why_it_matters":"Landmarks let assistive technology jump between the header, the navigation and the content, instead of reading the page from the top.","how_to_fix":"Wrap the main content in a main element and use header, nav, aside and footer where they apply.","references":[{"label":"W3C: Providing landmarks","url":"https://www.w3.org/WAI/WCAG22/Techniques/html/H101"}],"docs_url":"https://abackaudit.com/checks/landmarks","requires_browser":false,"informational":false},{"id":"a11y.names","category":"a11y","title":"Interactive elements have accessible names","severity":"high","confidence":"high","summary":"Links, buttons and images expose a name to assistive technology.","why_it_matters":"A link that says only click here is unusable with a screen reader. The name is what a person hears and what they search for.","how_to_fix":"Give every link and button text that describes the destination, and give every meaningful image an alt attribute.","references":[{"label":"W3C: Name, role, value","url":"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html"}],"docs_url":"https://abackaudit.com/checks/names","requires_browser":false,"informational":false},{"id":"a11y.aria","category":"a11y","title":"The ARIA usage is valid","severity":"medium","confidence":"medium","summary":"ARIA roles and attributes are used in ways that assistive technology can act on.","why_it_matters":"Wrong ARIA is worse than no ARIA. A role that contradicts the element, or a reference to an id that does not exist, hides content instead of exposing it.","how_to_fix":"Remove ARIA that duplicates native semantics, point aria references at ids that exist, and use roles that match the element.","references":[{"label":"W3C: ARIA in HTML","url":"https://www.w3.org/TR/html-aria/"}],"docs_url":"https://abackaudit.com/checks/aria","requires_browser":false,"informational":false},{"id":"sec.tls-certificate","category":"sec","title":"The certificate is valid for the host","severity":"high","confidence":"medium","summary":"The TLS handshake succeeds, so the chain is trusted and the hostname matches.","why_it_matters":"A browser that cannot verify the certificate shows a full page warning, and every link on the site becomes a risk to click.","how_to_fix":"Serve the site with a certificate that covers the hostname and chains to a trusted root. Renew it before it expires.","references":[{"label":"Cloudflare: SSL certificates","url":"https://developers.cloudflare.com/ssl/"}],"docs_url":"https://abackaudit.com/checks/tls-certificate","requires_browser":false,"informational":false},{"id":"sec.hsts","category":"sec","title":"Strict Transport Security is set","severity":"medium","confidence":"high","summary":"The Strict-Transport-Security header is present with a max-age of at least six months.","why_it_matters":"HSTS tells browsers to use HTTPS for the site from the first visit, which removes the window where an attacker can downgrade the connection.","how_to_fix":"Send Strict-Transport-Security: max-age=31536000; includeSubDomains once HTTPS works everywhere on the domain.","references":[{"label":"MDN: Strict-Transport-Security","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security"}],"docs_url":"https://abackaudit.com/checks/hsts","requires_browser":false,"informational":false},{"id":"sec.mixed-content","category":"sec","title":"The page has no mixed content","severity":"high","confidence":"high","summary":"No http subresource is loaded by an https page.","why_it_matters":"Browsers block active mixed content and warn about passive mixed content. An attacker on the network can tamper with anything loaded over http.","how_to_fix":"Change every http reference to https, including images, stylesheets, scripts and iframes.","references":[{"label":"MDN: Mixed content","url":"https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content"}],"docs_url":"https://abackaudit.com/checks/mixed-content","requires_browser":false,"informational":false},{"id":"sec.csp","category":"sec","title":"A Content Security Policy is set","severity":"low","confidence":"high","summary":"A Content-Security-Policy header exists, and script-src does not rely on unsafe-inline.","why_it_matters":"A content security policy is the strongest defence against cross site scripting. It limits what an injected script is allowed to do.","how_to_fix":"Start with a report only policy, then tighten it. Hashes or nonces remove the need for unsafe-inline.","references":[{"label":"MDN: Content-Security-Policy","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy"}],"docs_url":"https://abackaudit.com/checks/csp","requires_browser":false,"informational":false},{"id":"sec.content-type-options","category":"sec","title":"The content type is not sniffed","severity":"low","confidence":"high","summary":"The X-Content-Type-Options header is set to nosniff.","why_it_matters":"Without nosniff a browser may interpret a file as a different type than the one the server declared, which turns an upload into an attack.","how_to_fix":"Send X-Content-Type-Options: nosniff on every response.","references":[{"label":"MDN: X-Content-Type-Options","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options"}],"docs_url":"https://abackaudit.com/checks/content-type-options","requires_browser":false,"informational":false},{"id":"sec.referrer-policy","category":"sec","title":"A referrer policy is set","severity":"low","confidence":"high","summary":"The Referrer-Policy header is present.","why_it_matters":"The referrer policy decides how much of the current URL travels with a request to another site. A strict policy keeps internal paths private.","how_to_fix":"Send Referrer-Policy: strict-origin-when-cross-origin, or stricter.","references":[{"label":"MDN: Referrer-Policy","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy"}],"docs_url":"https://abackaudit.com/checks/referrer-policy","requires_browser":false,"informational":false},{"id":"sec.clickjacking","category":"sec","title":"The page cannot be framed","severity":"low","confidence":"high","summary":"X-Frame-Options is set, or the content security policy declares frame-ancestors.","why_it_matters":"A page that can be framed can be covered by a convincing overlay in another site, which is how clickjacking attacks work.","how_to_fix":"Send X-Frame-Options: DENY, or add frame-ancestors to the content security policy.","references":[{"label":"MDN: X-Frame-Options","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options"}],"docs_url":"https://abackaudit.com/checks/clickjacking","requires_browser":false,"informational":false},{"id":"sec.cookie-flags","category":"sec","title":"Cookies are set with safe flags","severity":"low","confidence":"high","summary":"Cookies set by the response use Secure, HttpOnly where appropriate, and SameSite.","why_it_matters":"A cookie without Secure travels over plain http, one without HttpOnly is readable by scripts, and one without SameSite is sent with cross site requests.","how_to_fix":"Set Secure, HttpOnly and SameSite=Lax or Strict on every cookie that needs it.","references":[{"label":"MDN: Set-Cookie","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie"}],"docs_url":"https://abackaudit.com/checks/cookie-flags","requires_browser":false,"informational":false}]