Skip to main content

Features

Every check Arachne runs, with its source and fix

110 checks in 5 categories. 77 follow a published source; the other 33 are heuristics, labelled as such and never critical. 29 of them look across the whole site rather than one page.

SEO

76 checks · 10 groups

Titles, social cards, indexing, canonicals, links, sitemaps, hreflang, structured data, and JavaScript rendering.

Titles and descriptions

8 checks

The title and description each page shows in search results.

  • MISSING_TITLE

    critical

    DocumentedEvery page

    Page has no non-empty title

    Fix: Add a unique <title> in the head that names the page's content, for example <title>Pricing · Acme</title>.

    Source: developers.google.com(opens in a new tab)
  • DUPLICATE_TITLE

    warning

    DocumentedAcross the site

    Fix: Give each indexable page its own title; when pages share a template, build the title from the page's own subject (product name, article headline).

    Source: developers.google.com(opens in a new tab)
  • TITLE_TOO_SHORT

    info

    HeuristicEvery page

    Title is 5 Unicode characters; target range is 15–60

    Fix: Make the title describe the page specifically, such as the product name plus what it is, instead of a single word or the brand alone.

    Source: developers.google.com(opens in a new tab)
  • TITLE_TOO_LONG

    info

    HeuristicEvery page

    Title is 855 px wide in Google's result font; about 600 px fit, so it shows as "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT…"

    Fix: Move the page-specific words to the start and trim the rest; Google cuts titles by pixel width, so anything after the cut point is not shown.

    Source: developers.google.com(opens in a new tab)
  • MISSING_META_DESC

    info

    DocumentedEvery page

    Page has no non-empty meta description

    Fix: Add <meta name="description" content="…"> with an accurate one- or two-sentence summary of the page.

    Source: developers.google.com(opens in a new tab)
  • META_DESC_TOO_SHORT

    info

    HeuristicEvery page

    Meta description is 16 Unicode characters; target range is 50–160

    Fix: Expand the description into a full sentence that says what the visitor will find on the page.

    Source: developers.google.com(opens in a new tab)
  • META_DESC_TOO_LONG

    info

    HeuristicEvery page

    Meta description is 2022 px wide in Google's result font; about 920 px fit, so it shows as "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD…"

    Fix: Put the key information in the first sentence; snippets are cut by pixel width, so the end may not be shown.

    Source: developers.google.com(opens in a new tab)
  • DUPLICATE_META_DESC

    info

    DocumentedAcross the site

    Fix: Write a description for each important page, or leave it out so Google builds a snippet from the page; don't repeat one description site-wide.

    Source: developers.google.com(opens in a new tab)

Open Graph and social cards

13 checks

How links to the site look when shared on social networks and in chat apps.

  • OG_URL_MISMATCH

    warning

    HeuristicEvery page

    Open Graph URL does not match the rendered page

    Fix: Set og:url to the same URL as the canonical, so shares are counted against one address.

    Source: ogp.me(opens in a new tab)
  • INVALID_OG_URL

    warning

    DocumentedEvery page

    Open Graph URL is not an absolute HTTP(S) URL

    Fix: Use an absolute https:// URL in og:url.

    Source: ogp.me(opens in a new tab)
  • LOCAL_OG_URL

    warning

    DocumentedEvery page

    Open Graph URL points to a local address

    Fix: Replace the localhost or private address in og:url with the public URL; it is usually a build-time environment variable left at its default.

    Source: ogp.me(opens in a new tab)
  • INVALID_OG_IMAGE_URL

    warning

    DocumentedEvery page

    Open Graph image is not an absolute HTTP(S) URL

    Fix: Use an absolute https:// URL in og:image; relative paths are not resolved by most link-preview crawlers.

    Source: ogp.me(opens in a new tab)
  • LOCAL_OG_IMAGE_URL

    warning

    DocumentedEvery page

    Open Graph image points to a local address

    Fix: Replace the localhost or private address in og:image with the public image URL.

    Source: ogp.me(opens in a new tab)
  • INVALID_X_IMAGE_URL

    warning

    DocumentedEvery page

    X Card image URL is invalid

    Fix: Use an absolute https:// URL for twitter:image.

    Source: developer.x.com(opens in a new tab)
  • LOCAL_X_IMAGE_URL

    warning

    DocumentedEvery page

    X Card image points to a local address

    Fix: Replace the localhost or private address in twitter:image with the public image URL.

    Source: developer.x.com(opens in a new tab)
  • MISSING_OG_TITLE

    info

    HeuristicEvery page

    Page has no Open Graph title

    Fix: Add <meta property="og:title" content="…"> so shared links show a proper title.

    Source: ogp.me(opens in a new tab)
  • MISSING_OG_DESCRIPTION

    info

    HeuristicEvery page

    Page has no Open Graph description

    Fix: Add <meta property="og:description" content="…"> with a one-sentence summary for link previews.

    Source: ogp.me(opens in a new tab)
  • MISSING_OG_IMAGE

    info

    HeuristicEvery page

    Page has no Open Graph image

    Fix: Add <meta property="og:image" content="https://…"> with an absolute URL to an image of at least 1200×630.

    Source: ogp.me(opens in a new tab)
  • MISSING_OG_URL

    info

    HeuristicEvery page

    Page has no Open Graph URL

    Fix: Add <meta property="og:url" content="…"> with the page's canonical URL.

    Source: ogp.me(opens in a new tab)
  • MISSING_X_CARD

    info

    HeuristicEvery page

    Page has no twitter:card declaration

    Fix: Add <meta name="twitter:card" content="summary_large_image">; X falls back to Open Graph for the title, description, and image.

    Source: developer.x.com(opens in a new tab)
  • INCOMPLETE_X_CARD

    info

    HeuristicEvery page

    X Card metadata is incomplete

    Fix: Add the missing card fields, or the Open Graph equivalents X falls back to.

    Source: developer.x.com(opens in a new tab)

Indexing and canonicals

16 checks

Whether search engines may index each page, and which URL they treat as the original.

10 checks

Broken pages, redirects, and how pages link to each other.

Sitemap

4 checks

Whether the XML sitemap lists the live, canonical pages and only those.

URL structure

3 checks

URLs that create duplicates or endless variations for crawlers.

Languages (hreflang)

5 checks

The links between language and region versions of a page.

Structured data

4 checks

JSON-LD markup and its eligibility for rich results.

  • MALFORMED_STRUCTURED_DATA

    critical

    DocumentedEvery page

    Malformed JSON-LD: EOF while parsing

    Fix: Fix the JSON syntax in the application/ld+json block; a single trailing comma or unescaped quote makes the whole block unreadable.

    Source: developers.google.com(opens in a new tab)
  • INVALID_STRUCTURED_DATA

    warning

    DocumentedEvery page

    JSON-LD is missing @type

    Fix: Add @context: "https://schema.org" and an @type to each JSON-LD item.

    Source: developers.google.com(opens in a new tab)
  • STRUCTURED_DATA_MISSING_REQUIRED

    warning

    DocumentedEvery page

    Product structured data is missing required property: offers or review or aggregateRating

    Fix: Add the required properties listed in the evidence; without them the item is not eligible for its rich result.

    Source: developers.google.com(opens in a new tab)
  • DEPRECATED_STRUCTURED_DATA

    info

    DocumentedEvery page

    Page uses HowTo structured data; HowTo rich results are no longer shown

    Fix: HowTo rich results were removed and FAQ results only show for authoritative government and health sites; keep the markup only if it serves another purpose.

    Source: developers.google.com(opens in a new tab)

JavaScript rendering

5 checks

Differences between the HTML the server sends and the page after JavaScript runs.

  • NOINDEX_REMOVED_BY_JS

    critical

    DocumentedEvery page

    The server HTML has a noindex meta robots tag that JavaScript removes

    Fix: Remove noindex from the server HTML itself; when Google sees noindex before rendering, it may skip rendering and never see JavaScript remove it.

    Source: developers.google.com(opens in a new tab)
  • RENDERED_ONLY_LINKS

    warning

    DocumentedEvery page

    10 of 10 internal links exist only after JavaScript runs

    Fix: Render navigation and important internal links as <a href> in the server HTML, so crawlers find them without running JavaScript.

    Source: developers.google.com(opens in a new tab)
  • CANONICAL_CHANGED_BY_JS

    warning

    DocumentedEvery page

    JavaScript changes the canonical from /original to /

    Fix: Set the canonical once in the server HTML; don't let JavaScript change it to a different URL.

    Source: developers.google.com(opens in a new tab)
  • NOINDEX_ADDED_BY_JS

    warning

    DocumentedEvery page

    JavaScript adds a noindex meta robots tag the server HTML doesn't have

    Fix: If the page should not be indexed, put noindex in the server HTML; if it should be, find the script that adds it.

    Source: developers.google.com(opens in a new tab)
  • RENDERED_ONLY_CONTENT

    info

    DocumentedEvery page

    The server HTML has 20 words; the rendered page has 500

    Fix: Server-render or pre-render the main content; Google renders JavaScript later than it crawls, and other crawlers often don't render it at all.

    Source: developers.google.com(opens in a new tab)

Mobile and app (PWA)

7 checks

The mobile viewport, icons, and the web app manifest used to install the site.

  • MISSING_VIEWPORT

    warning

    DocumentedEvery page

    Page has no viewport meta tag

    Fix: Add <meta name="viewport" content="width=device-width, initial-scale=1"> to the head.

    Source: developers.google.com(opens in a new tab)
  • INVALID_WEB_MANIFEST

    warning

    DocumentedEvery page

    The web app manifest /manifest.webmanifest has no start_url, display

    Fix: Serve the linked manifest with a 200 status as valid JSON, with name (or short_name), icons, start_url, and display.

    Source: developer.mozilla.org(opens in a new tab)
  • MANIFEST_ICONS_TOO_SMALL

    warning

    DocumentedEvery page

    The web app manifest /manifest.webmanifest has no 512×512 icon; the largest regular icon is 192px

    Fix: Add 192×192 and 512×512 PNG icons to the manifest's icons, each with a matching sizes value; Android uses them for the home screen and the splash screen.

    Source: web.dev(opens in a new tab)
  • MISSING_FAVICON

    warning

    DocumentedEvery page

    The page links no icon and /favicon.ico is not served

    Fix: Add <link rel="icon" href="/favicon.svg"> (or a square PNG of at least 48×48) to the head; Google shows it next to the site's results, and browsers show it in tabs and bookmarks.

    Source: developers.google.com(opens in a new tab)
  • MISSING_WEB_MANIFEST

    info

    DocumentedEvery pageHidden by default

    The page links no web app manifest

    Fix: Only if the site should be installable: add <link rel="manifest" href="/manifest.webmanifest"> pointing at a manifest with name, icons, start_url, and display.

    Source: web.dev(opens in a new tab)
  • MISSING_THEME_COLOR

    info

    DocumentedEvery page

    The page has a web app manifest but no theme-color meta tag; the manifest's theme_color is #0b5fff

    Fix: Add <meta name="theme-color" content="#…"> with the manifest's theme_color, so the browser toolbar and the installed app use the brand color.

    Source: developer.mozilla.org(opens in a new tab)
  • MISSING_APPLE_TOUCH_ICON

    info

    DocumentedEvery page

    The page links no apple-touch-icon and /apple-touch-icon.png is not served

    Fix: Add <link rel="apple-touch-icon" href="/apple-touch-icon.png"> with a 180×180 PNG; iOS shows it when the site is added to the home screen.

    Source: developer.apple.com(opens in a new tab)

Performance

18 checks · 2 groups

Core Web Vitals measured in a real browser, page weight, requests, and images.

Speed and Core Web Vitals

11 checks

Lab timings, layout shifts, and the weight of what each page loads.

  • SLOW_LCP

    critical

    DocumentedEvery page

    Synthetic lab Largest Contentful Paint is 3000ms (threshold: 2500ms)

    Fix: Find the LCP element in the evidence. If it is an image, don't lazy-load it, give it fetchpriority="high", and serve it at its displayed size; if it is text, remove render-blocking resources ahead of it.

    Source: web.dev(opens in a new tab)
  • SLOW_TTFB

    warning

    DocumentedEvery page

    Synthetic lab Time to First Byte is 1000ms (threshold: 800ms)

    Fix: Cache the HTML at the edge or in the app, remove slow work before the first byte (database calls, server-side API waterfalls), and check the server region against your visitors.

    Source: web.dev(opens in a new tab)
  • HIGH_CLS

    warning

    DocumentedEvery page

    Synthetic lab Cumulative Layout Shift is 0.200 (good: at most 0.1)

    Fix: Give images, videos, iframes, and ad slots explicit width and height (or aspect-ratio), and don't insert content above existing content after load.

    Source: web.dev(opens in a new tab)
  • HIGH_TBT

    warning

    DocumentedEvery page

    Synthetic lab Total Blocking Time is 300ms (threshold: 200ms)

    Fix: Split long JavaScript tasks, defer or remove unused scripts, and move heavy work off the main thread or after interaction.

    Source: web.dev(opens in a new tab)
  • LARGE_JS

    warning

    HeuristicEvery page

    Synthetic lab JavaScript transfer total is 2.0MB (threshold: 1MB)

    Fix: Code-split by route, remove unused dependencies, and load third-party scripts only where they are needed.

    Source: web.dev(opens in a new tab)
  • TOO_MANY_THIRD_PARTY_REQUESTS

    warning

    HeuristicEvery page

    Synthetic lab load observed 20 third-party requests (threshold: 10)

    Fix: Audit third-party tags, remove unused ones, and load the rest after the main content (for example through a tag manager trigger on interaction).

    Source: web.dev(opens in a new tab)
  • SLOW_FCP

    info

    DocumentedEvery page

    Synthetic lab First Contentful Paint is 2000ms (threshold: 1800ms)

    Fix: Inline critical CSS, defer non-critical scripts with defer or async, and preload the main web font with font-display: swap.

    Source: web.dev(opens in a new tab)
  • SLOW_LOAD

    info

    HeuristicEvery page

    Synthetic lab page load time is 4000ms (threshold: 3000ms)

    Fix: Defer resources that aren't needed for the first view: third-party tags, below-the-fold images (loading="lazy"), and non-critical scripts.

    Source: developer.mozilla.org(opens in a new tab)
  • RENDER_BLOCKING_RESOURCES

    info

    HeuristicEvery page

    5 render-blocking resource(s) observed in the synthetic lab load

    Fix: Add defer to scripts in the head, inline the CSS needed for the first view, and load the rest with media or after load.

    Source: developer.chrome.com(opens in a new tab)
  • LARGE_PAGE

    info

    HeuristicEvery page

    Synthetic lab transfer total is 4.0MB (threshold: 3MB)

    Fix: Compress and resize images, serve AVIF or WebP, remove unused JavaScript and CSS, and enable Brotli or gzip.

    Source: developer.chrome.com(opens in a new tab)
  • TOO_MANY_REQUESTS

    info

    HeuristicEvery page

    Synthetic lab load observed 100 resource requests (threshold: 80)

    Fix: Bundle small scripts and styles, inline tiny icons as SVG, and remove tags and widgets the page doesn't need.

    Source: developer.chrome.com(opens in a new tab)

Images

9 checks

Image sizes, formats, loading, and alternative text.

  • LAZY_LCP_IMAGE

    warning

    DocumentedEvery page

    The largest paint is a lazy-loaded image (img.hero, hero.webp), so it starts loading late

    Fix: Remove loading="lazy" from the image that is the largest paint; lazy-load only images below the first screen.

    Source: web.dev(opens in a new tab)
  • IMAGES_WITHOUT_ALT

    warning

    DocumentedEvery page

    3 image(s) have no alt attribute

    Fix: Add alt text describing each informative image; use alt="" for decorative images so screen readers skip them.

    Source: w3.org(opens in a new tab)
  • OVERSIZED_IMAGES

    info

    HeuristicEvery page

    1 image(s) are much larger than displayed at this viewport's 1.00x pixel ratio: hero.jpg (3000px wide, shown at 300px)

    Fix: Serve each image near its displayed size times the device pixel ratio, with srcset and sizes so the browser picks the right file.

    Source: developer.chrome.com(opens in a new tab)
  • IMAGES_WITHOUT_DIMENSIONS

    info

    DocumentedEvery page

    1 image(s) have no width and height or aspect ratio: photo.webp

    Fix: Add width and height attributes (or a CSS aspect-ratio) to every image so the browser reserves its space before it loads.

    Source: web.dev(opens in a new tab)
  • LCP_IMAGE_NOT_PRIORITIZED

    info

    DocumentedEvery page

    The largest paint is an image (img.hero, hero.webp) without fetchpriority="high" or a preload

    Fix: Add fetchpriority="high" to the LCP image, or <link rel="preload" as="image" href="…"> when it is a CSS background.

    Source: web.dev(opens in a new tab)
  • LEGACY_IMAGE_FORMAT

    info

    HeuristicEvery page

    1 large image(s) use an older format; WebP would save about 25 KB: photo.jpg (image/jpeg, 400 KB)

    Fix: Serve large photos as AVIF or WebP, with <picture> fallbacks if you support old browsers.

    Source: developer.chrome.com(opens in a new tab)
  • RESPONSIVE_IMAGE_TOO_LARGE

    info

    DocumentedEvery page

    1 image(s) download a srcset file far larger than their slot; the right candidates would save about 282 KB: hero-1600.webp picks the 1600w file for a 100px slot where the 400w file would do (no sizes attribut…

    Fix: Give the image a sizes attribute that matches its real slot, such as sizes="(max-width: 600px) 100vw, 300px"; without it the browser assumes the image fills the viewport.

    Source: developer.mozilla.org(opens in a new tab)
  • OFFSCREEN_IMAGES_NOT_LAZY

    info

    DocumentedEvery page

    1 image(s) below the first screen load immediately; loading="lazy" would defer about 80 KB: gallery.webp (2400px down, 80 KB)

    Fix: Add loading="lazy" to images below the first screen, and keep it off the hero image and anything visible on load.

    Source: web.dev(opens in a new tab)
  • POOR_ALT_TEXT

    info

    HeuristicEvery page

    1 image(s) have alt text that doesn't describe them: "IMG_2041.jpg"

    Fix: Describe what the image shows or does, instead of a file name or a word like "image"; use alt="" for decorative images.

    Source: w3.org(opens in a new tab)

Accessibility

7 checks · 2 groups

Color contrast, document language, headings, form labels, and alt text.

Color contrast

1 check

Text that is too faint against its background to read comfortably.

  • LOW_TEXT_CONTRAST

    warning

    DocumentedEvery page

    3 text element(s) are below the WCAG contrast minimum: "Free shipping on orders over $50" #999999 on #ffffff (2.84:1, needs 4.5:1)

    Fix: Darken the text or lighten its background until the ratio reaches 4.5:1, or 3:1 for text of at least 24px (18.66px bold); the evidence lists each color pair and its ratio.

    Source: w3.org(opens in a new tab)

Structure and forms

4 checks

Headings, page language, and labels that assistive technology relies on.

  • MISSING_LANG

    warning

    DocumentedEvery page

    HTML element has no lang attribute

    Fix: Set the page language on the root element, for example <html lang="en">.

    Source: w3.org(opens in a new tab)
  • INPUTS_WITHOUT_LABELS

    warning

    DocumentedEvery page

    2 input(s) missing associated labels

    Fix: Associate each control with a <label for="…">, wrap it in a label, or give it aria-label.

    Source: w3.org(opens in a new tab)
  • MISSING_H1

    info

    HeuristicEvery page

    Page has no H1 heading

    Fix: Add one <h1> naming the page's main content, or give the existing top heading an accessible name.

    Source: dequeuniversity.com(opens in a new tab)
  • HEADING_LEVEL_SKIP

    info

    HeuristicEvery page

    Heading outline skips one or more levels

    Fix: Use the next heading level down (H2 after H1, H3 after H2); style headings with CSS instead of choosing levels for their size.

    Source: dequeuniversity.com(opens in a new tab)

Content

5 checks · 1 group

Thin, placeholder, duplicate, and near-duplicate content across the site.

Content

6 checks

Placeholder text, duplicated content, and pages that differ from their siblings.

  • PLACEHOLDER_CONTENT

    warning

    HeuristicEvery page

    Page shows placeholder text: default title "React App", "lorem ipsum" filler text

    Fix: Replace the placeholder with real copy, and check the template or data source that produced it; a framework's default title usually means the head was never set.

    Source: developers.google.com(opens in a new tab)
  • NEAR_DUPLICATE_CONTENT

    warning

    HeuristicAcross the site

    Fix: Merge the near-identical pages, or point the copies at the preferred one with rel="canonical"; if they should differ, add the content that makes each one distinct.

    Source: developers.google.com(opens in a new tab)
  • THIN_CONTENT

    info

    HeuristicEvery pageHidden by default

    Page has only 10 words (recommended: ≥300)

    Fix: Only expand the page if visitors need more information to complete their task; word count itself is not a ranking factor.

    Source: developers.google.com(opens in a new tab)
  • MULTIPLE_H1

    info

    HeuristicEvery pageHidden by default

    Page has 2 H1 headings; target is one

    Fix: Optional: keep one H1 for the page's main topic if it helps the outline; search engines accept several.

    Source: searchenginejournal.com(opens in a new tab)
  • DUPLICATE_H1

    info

    HeuristicAcross the site

    Fix: Make each page's H1 name its own subject rather than a section label shared by every page.

    Source: developers.google.com(opens in a new tab)
  • CONTENT_OUTLIER

    info

    HeuristicAcross the site

    Fix: Check whether these pages are missing content their siblings have, such as a description that failed to load or an empty product.

    Source: developers.google.com(opens in a new tab)

Security

4 checks · 1 group

Security headers and mixed content on pages served over HTTPS.

Security headers

4 checks

Response headers and HTTPS hygiene that protect visitors.

How a scan works

  • A real browser

    Pages render in headless Chromium at a mobile profile, the way Google indexes them. Arachne compares the raw HTML with the rendered page.

  • Site probes

    Each crawl also requests a page that should not exist, the http:// and www variants of the site, and robots.txt rules for search and AI crawlers.

  • Every scan, compared

    Each finding has a fingerprint. A new scan is matched with the previous one, so you see what was fixed, what is new, and what is still open.

  • Blocked, not guessed

    When bot protection answers with a challenge page, Arachne reports the page as blocked instead of analyzing the challenge.