# Every site audit check explained

URL: https://serpel.app/docs/audit-checks

Updated: 2026-11-05

The Serpel site audit has 94 checks, sorted into 18 categories and three severity levels. Rendering and Core Web Vitals checks run when that data is available. This reference lists each check with its code, why it matters and how to fix it, generated from the same definitions the crawler uses.

- **94** audit checks: In 18 categories.
- **11** errors: Block indexing or visitors.
- **43** warnings: Cost visibility or clicks.
- **40** notices: Hints and missed chances.

## How are the audit checks sorted?

Every check has a severity and a category. The severity decides how urgent a finding is and how many points it takes off the audit score. The category groups checks by topic, and the **Issues** page of a project shows it on every finding. Each finding links to its own page with “Why it matters”, “How to fix it” and the affected URLs. The [site audit guide](https://serpel.app/docs/site-audit) explains how a crawl runs and how to read the results.

**Severity levels and what they cost the audit score**

- **Error** (6 to 20 points): Something blocks search engines or visitors, for example a broken page or a page that can’t be indexed. Fix these first. 11 checks have this severity.
- **Warning** (2 to 8 points): Something works but costs you visibility or clicks, for example a missing description or a slow response. 43 checks have this severity.
- **Notice** (0.5 to 2 points): A hint or a missed chance with little effect on its own. Review it when the errors and warnings are done. 40 checks have this severity.

The score starts at 100. Each issue type that a crawl finds takes off a fixed base amount plus a part that grows with the share of affected pages, so one broken page costs less than a broken template on every page. The deduction ranges in the cards above come straight from the scoring code.

## What do “heuristic” and “site-wide” mean?

- **Heuristic:** 15 checks rely on a rule of thumb instead of a certain signal, for example “Title is probably too long”, “Possible soft 404 page” or “Little text content”. The dashboard marks them “Based on a heuristic”: treat each finding as a hint and check it before you change your site.
- **Site-wide:** 11 checks look at the website as a whole, for example the robots.txt file, the XML sitemap, HTTPS or an llms.txt, instead of single pages. They count as if every page were affected.

> **Find a check by its code:** Each table below shows the code in brackets, for example `http_4xx`. The same code appears in exports, in the [REST API](https://serpel.app/docs/api) under `/projects/{id}/issues/{code}`, in `serpel audit issue <code>` in the [CLI](https://serpel.app/docs/cli) and in the MCP tool `get_crawl_issues`.

## Availability checks

6 checks: 4 errors, 2 warnings.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Internal URL returns a 4xx status code (`http_4xx`) | Error | Search engines and visitors land on an error page. The URL can’t rank, and link equity pointing to it is lost. **Fix:** Restore the content or redirect the URL with a 301 to the most relevant working page. Update internal links to point directly to the new target. |
| Internal URL returns a server error (5xx) (`http_5xx`) | Error | Server errors prevent crawling. If they persist, Google reduces its crawl rate and may remove pages from the index. **Fix:** Check the server and application logs for the time shown and fix the cause. Then run the crawl again. |
| URL can’t be fetched (timeout or connection error) (`request_failed`) | Error | The page didn’t respond or the connection failed. If the crawler can’t fetch it, a search engine can’t reliably index it either. **Fix:** Check availability, DNS, TLS certificate and response time of the URL. Very slow pages should respond completely within 10 seconds. |
| Soft 404: error page with status code 200 (`soft_404`) | Error | The page shows the content of an error page or is practically identical to the website’s 404 page, but responds with status 200. Google treats such pages as soft 404 and doesn’t index them; they also waste crawl budget. **Fix:** Return status code 404 or 410 for content that doesn’t exist. If the page exists, add its actual content; if it no longer exists, redirect it with a 301 to a topically relevant page. |
| Possible soft 404 page (`soft_404_possible`) | Warning (heuristic) | The page has traits of an error page, for example a note like “not found” in the title, very little text or a redirect to the homepage. But it doesn’t respond with 404. Google often treats such pages as soft 404. The assessment is a probability; the confidence is shown in the details. **Fix:** Check the page. If it’s actually empty or switched off, return 404 or 410 or redirect it to a topically relevant page. If it’s intended, add content and a meaningful title. |
| Embedded resource is unreachable (`broken_resource`) | Warning | An embedded image, stylesheet or script responds with an error. The page is therefore displayed incompletely or incorrectly: images don’t appear and scripts fail. **Fix:** Correct the resource path or restore the resource. The affected addresses are listed with each occurrence. |

## Redirects checks

4 checks: 1 error, 1 warning, 2 notices. Test a single address step by step with the free [redirect checker](https://serpel.app/tools/redirect-checker).

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Redirect loop (`redirect_loop`) | Error | The redirect goes in circles. Neither browsers nor search engines ever reach any content. **Fix:** Correct the redirect rules so that every URL points to a target with status 200 in at most one step. |
| Redirect chain with several hops (`redirect_chain`) | Warning | Every additional hop costs load time and crawl budget. Google only follows chains up to a limit. **Fix:** Redirect the original URL directly to the final target and update internal links to point to the final target. |
| Temporary redirect (302/307) (`redirect_temporary`) | Notice | Temporary redirects signal that the old URL will come back. That’s the wrong signal for permanent moves. **Fix:** If the redirect is meant to be permanent, change it to 301 or 308. |
| Embedded resources are redirected (`resource_redirect`) | Notice | Images, stylesheets or scripts are loaded through a redirect. Every redirect lengthens the page’s load time. **Fix:** Use the final address of the resource directly as the source. |

## Title and description checks

8 checks: 1 error, 3 warnings, 4 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Title is missing (`title_missing`) | Error | The title is one of the most important on-page signals and usually the headline in search results. Without a title, Google generates one itself. **Fix:** Add a unique, descriptive title with the main topic of the page, ideally 30 to 60 characters. |
| Same title on several pages (`title_duplicate`) | Warning | Identical titles make it hard for search engines to pick the right page for a query and look interchangeable in search results. **Fix:** Write a separate title for every indexable page that describes its specific content. |
| Meta description is missing (`meta_description_missing`) | Warning | Without a description, Google picks a text snippet itself. A good description can improve the click-through rate in search results. **Fix:** Add a meta description that summarizes the benefit of the page in 70 to 160 characters. |
| Same meta description on several pages (`meta_description_duplicate`) | Warning | Duplicate descriptions are often ignored by Google and give searchers no way to tell the pages apart. **Fix:** Write a separate description for every indexable page. |
| Title is probably too long (`title_too_long`) | Notice (heuristic) | Google truncates titles by pixel width, roughly from about 60 characters. Important terms at the end then aren’t shown. **Fix:** Shorten the title or move the most important terms to the beginning. The character limit is a rule of thumb, not a hard limit. |
| Title is very short (`title_too_short`) | Notice (heuristic) | Very short titles don’t use the space available in search results and often describe the content too vaguely. **Fix:** Extend the title with the specific topic or offer of the page. |
| Meta description is probably too long (`meta_description_too_long`) | Notice (heuristic) | Long descriptions are cut off in search results, roughly from about 160 characters. **Fix:** Shorten the description and put the key message first. |
| Meta description is very short (`meta_description_too_short`) | Notice (heuristic) | Very short descriptions barely use the space in search results and are more often replaced by a snippet Google picks itself. **Fix:** Extend the description with the benefit and specific content of the page. |

## Headings checks

4 checks: 2 warnings, 2 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| H1 heading is missing (`h1_missing`) | Warning | The H1 names the main topic of the page for visitors and search engines. **Fix:** Add exactly one visible H1 that describes the main topic of the page. |
| Empty heading (`heading_empty`) | Warning | A heading without text carries no information but appears in the outline and is announced by screen readers. **Fix:** Add text to the heading or remove the element. For plain spacing, use CSS instead of headings. |
| Multiple H1 headings (`h1_multiple`) | Notice | Multiple H1s are technically allowed but often dilute the weight of the main topic. **Fix:** Check whether one H1 carries the main topic and demote the others to H2. |
| Heading level skipped (`heading_level_skipped`) | Notice | The headings jump, for example, from H2 to H4. A clean outline helps screen readers, search engines and readers understand the structure of the page. **Fix:** Use heading levels in order without gaps (H1, H2, H3) and choose the level by structure, not by the font size you want. |

## Indexing checks

8 checks: 4 warnings, 4 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Multiple canonical tags (`canonical_multiple`) | Warning | If the canonical declarations conflict, Google ignores all of them. **Fix:** Keep exactly one canonical tag in the head of the page. |
| Canonical target is unreachable or redirects (`canonical_target_error`) | Warning | A canonical pointing to an error page or a redirect is a conflicting signal and is often ignored. **Fix:** Point the canonical to a URL that responds directly with status 200 and is indexable. |
| Canonical tag is invalid (`canonical_invalid`) | Warning | An empty or unresolvable canonical can’t be evaluated. **Fix:** Enter a complete, absolute URL in the href attribute. |
| Canonical in HTTP header conflicts with canonical in HTML (`canonical_header_conflict`) | Warning | The Link header names a different canonical URL than the canonical tag in the HTML. With conflicting signals, Google may ignore both and choose itself. **Fix:** Use only one source for the canonical or make sure the header and the HTML name the same absolute URL. |
| Page is set to noindex (`noindex`) | Notice | The page isn’t added to the search index. That’s only right if you want to exclude it on purpose. **Fix:** Check whether the exclusion is intended. If not, remove noindex from the meta robots tag or the X-Robots-Tag header. |
| Page tells search engines not to follow links (nofollow) (`nofollow_page`) | Notice | With page-level nofollow, the page passes no signals to the pages it links to. **Fix:** Remove nofollow from the meta robots tag or the X-Robots-Tag unless it was set on purpose. |
| No canonical tag (`canonical_missing`) | Notice | A self-referencing canonical helps search engines map variants with parameters or different spellings to the right URL. **Fix:** Add a canonical tag with the page’s own absolute URL on indexable pages. |
| Canonical points to a different URL (`canonical_to_other`) | Notice | The page reports another URL as the authoritative version. Only the target should then rank. **Fix:** Check whether that’s intended. If the page is standalone, set the canonical to its own URL. |

## robots.txt and sitemap checks

8 checks: 3 errors, 2 warnings, 3 notices. How the crawler reads your robots.txt is described on the [SerpelBot page](https://serpel.app/docs/serpelbot). Test a file with the free [robots.txt checker](https://serpel.app/tools/robots-txt-checker) and a sitemap with the [sitemap checker](https://serpel.app/tools/sitemap-checker).

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| robots.txt responds with a server error (`robots_txt_error`) | Error (site-wide) | If robots.txt keeps returning a 5xx status, Google treats the entire website as if it were blocked. **Fix:** Make sure /robots.txt responds with status 200 (or 404 if there is none). |
| Start URL blocked by robots.txt (`start_url_blocked`) | Error (site-wide) | The robots.txt forbids crawling the homepage. Search engines then see no content. **Fix:** Remove the relevant Disallow rule if the website should be indexed. |
| Search engine crawler blocked by robots.txt (`search_crawler_blocked`) | Error (site-wide) | The robots.txt forbids Googlebot or Bingbot from fetching the homepage. New and changed content then no longer reaches the index. **Fix:** Remove the Disallow rule for the blocked crawler from robots.txt or limit it to the paths that should really be blocked. |
| Sitemap can’t be read (`sitemap_invalid`) | Warning (site-wide) | The sitemap can’t be fetched or isn’t valid XML. Search engines can’t use it then. **Fix:** Check availability, content type and XML syntax of the sitemap. |
| Sitemap contains broken, redirecting or non-indexable URLs (`sitemap_url_not_ok`) | Warning | A sitemap should only contain canonical, indexable URLs with status 200. Faulty entries reduce trust in the sitemap. **Fix:** Remove the affected URLs from the sitemap or replace them with their target addresses. |
| No robots.txt found (`robots_txt_missing`) | Notice (site-wide) | Without a robots.txt, everything may be crawled. That’s allowed, but it lacks the usual place for the sitemap reference. **Fix:** Create a file at /robots.txt, at least with a reference to the XML sitemap. |
| Internally linked URL blocked by robots.txt (`blocked_by_robots`) | Notice | The URL is linked internally but may not be crawled. Blocked URLs can still end up in the index without content. **Fix:** Check whether the block is intended. For pages that should stay out permanently, noindex is often the better tool. |
| No XML sitemap found (`sitemap_missing`) | Notice (site-wide) | A sitemap helps search engines find all important URLs, especially deeply linked and new pages. **Fix:** Create an XML sitemap with all indexable URLs and reference it in robots.txt. |

## Linking checks

10 checks: 1 error, 4 warnings, 5 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Internal link points to a broken URL (`broken_internal_link`) | Error | Broken links send visitors into dead ends and waste crawl budget. The linking page passes link equity to an error page. **Fix:** Change the link target to a working URL or remove the link. The affected link targets are listed with each occurrence. |
| Page only found via the sitemap (no internal links) (`orphan_page`) | Warning (heuristic) | Pages without internal links receive no link equity and are hardly found by visitors. **Fix:** Link the page from topically relevant pages or the navigation. |
| External link points to a broken URL (`broken_external_link`) | Warning | The linked target responds with an error or is unreachable. Visitors end up in a dead end, and the page looks less maintained and trustworthy. **Fix:** Replace the link with a working target, for example the source’s new address, or remove it. The affected targets are listed with each occurrence. |
| Internal links with tracking parameters (`internal_link_tracking_parameters`) | Warning | Internal links with parameters like utm_source, gclid or sessionid create many URL variants for each page. That dilutes signals, wastes crawl budget and splits visits into new sessions in web analytics. These parameters were removed during the crawl so pages aren’t counted multiple times. **Fix:** Remove tracking parameters from all internal links. Use events in your web analytics for internal clicks instead of parameters, and set a canonical to the clean URL on the target pages. |
| Links without anchor text (`link_anchor_empty`) | Warning | Anchor text tells search engines and screen readers where a link leads. Links with no text, no image alt text and no aria-label don’t pass on this information. **Fix:** Add meaningful link text. For image links, set an alt text; for icons, an aria-label. |
| Internal links point to redirecting URLs (`internal_link_to_redirect`) | Notice | Links to redirects create unnecessary extra hops for visitors and crawlers. **Fix:** Point the internal links directly to the redirect target. |
| High click depth (`deep_page`) | Notice (heuristic) | Pages that can only be reached after many clicks from the homepage are crawled less often and are often seen as less important. **Fix:** Shorten the click path with links from the navigation, category pages or hub pages. |
| External links point to redirecting URLs (`external_link_redirect`) | Notice | The linked addresses redirect. That costs load time, and the redirect may eventually disappear so that the link goes nowhere. **Fix:** Use the final address shown in the details directly as the link target. |
| Internal links with nofollow (`internal_nofollow_link`) | Notice | With rel="nofollow" on internal links, pages pass no link equity to the target and don’t improve its discoverability. That’s rarely intended internally. **Fix:** Remove nofollow from internal links that search engines should follow. Keep it only on links that shouldn’t be counted on purpose, such as logins or shopping carts. |
| Links with generic anchor text (`link_anchor_generic`) | Notice (heuristic) | Texts like “click here”, “more” or “read more” tell neither visitors nor search engines what the target page is about. Descriptive anchor text strengthens the topic of the linked page. **Fix:** Write the link text so that it describes the target, for example “View pricing and plans” instead of “click here”. |

## Images checks

3 checks: 2 warnings, 1 notice.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Images without alt attribute (`image_missing_alt`) | Warning | Alt text describes images for screen readers and search engines. Without an alt attribute, this information is missing entirely. **Fix:** Add meaningful alt text. For purely decorative images, set an empty alt="". |
| Most important image is lazy loaded (`lcp_image_lazy_loaded`) | Warning (heuristic) | The largest image in the visible area, probably the LCP element, has loading="lazy". The browser then loads it late, and the Largest Contentful Paint gets noticeably worse. **Fix:** Remove loading="lazy" from images in the visible area and set fetchpriority="high" on the most important image. Lazy loading only belongs on images further down the page. |
| Images without width and height (`image_missing_dimensions`) | Notice | Without fixed dimensions, the browser reserves no space before loading. The page jumps as soon as the image appears and worsens the Cumulative Layout Shift. **Fix:** Set the width and height attributes on every img to the original dimensions or define an aspect-ratio in CSS. |

## HTTPS checks

4 checks: 1 error, 3 warnings. They cover HTTPS on the whole site, the redirect from HTTP and links or resources that still use plain HTTP.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Website isn’t reachable over HTTPS (`https_missing`) | Error (site-wide) | HTTPS is a ranking signal and a prerequisite for browsers not marking the page as insecure. **Fix:** Set up a valid TLS certificate and redirect all HTTP URLs to HTTPS with a 301. |
| HTTP version doesn’t redirect to HTTPS (`http_not_redirected`) | Warning (site-wide) | If the page is reachable over both HTTP and HTTPS, duplicate versions arise and visitors can land on an unencrypted version. **Fix:** Redirect http:// permanently (301) to the HTTPS variant. |
| Internal links to HTTP URLs (`internal_http_link`) | Warning | Links to the unencrypted variant cause extra redirects and can expose duplicate versions. **Fix:** Switch internal links to https://, ideally as relative or protocol-relative paths. |
| HTTPS page loads resources over HTTP (mixed content) (`mixed_content`) | Warning | Browsers block insecure scripts and stylesheets on HTTPS pages. The page may therefore display incorrectly. **Fix:** Load all images, scripts, stylesheets and frames over HTTPS. |

## Content checks

4 checks: 1 warning, 3 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Identical text content on several URLs (`duplicate_content`) | Warning (heuristic) | Multiple URLs with identical text compete with each other. Search engines then pick a version themselves. **Fix:** Merge the pages, redirect duplicates with a 301 or set a canonical to the main version. |
| Very similar text content (`near_duplicate_content`) | Notice (heuristic) | The texts differ only slightly. That can be intended, for example with variants, but often points to interchangeable pages. **Fix:** Check whether the pages offer unique value. Differentiate the content or consolidate it with a canonical. |
| Little text content (`thin_content`) | Notice (heuristic) | Pages with very little text rarely answer search queries comprehensively. Word count alone says nothing about quality, though. **Fix:** Check whether the page covers the topic sufficiently and add helpful content if needed. |
| Language not specified in the HTML (`html_lang_missing`) | Notice | The lang attribute tells browsers, screen readers and translation services which language the page is written in. **Fix:** Set the language on the html element, for example `<html lang="en">`. |

## Response time checks

4 checks: 2 warnings, 2 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| HTML document too large (`page_too_large`) | Warning | Google only reads the first 2 megabytes of an HTML file. Everything after that, including links and structured data, isn’t evaluated. Very large documents also load slowly. **Fix:** Reduce the HTML: move inline CSS, JavaScript and Base64 images into separate files, split very long lists across several pages and remove unnecessary markup. |
| Response exceeds the crawler’s size limit (`response_truncated`) | Warning | The response was larger than the 5 megabyte limit and was cut off. The analysis of this page is incomplete, and search engines also read only part of it. **Fix:** Reduce the HTML significantly, for example by moving inline CSS, JavaScript and data into separate files or by splitting the page. |
| Slow server response (`slow_response`) | Notice (heuristic) | Long response times hurt the user experience and limit how many pages Google crawls per visit. **Fix:** Check caching, database queries and server resources for the affected URLs. |
| Render-blocking resources in the head (`render_blocking_resources`) | Notice (heuristic) | Scripts without async or defer and many stylesheets in the `<head>` hold up the first visible rendering of the page. That worsens First Contentful Paint and Largest Contentful Paint. **Fix:** Load scripts with defer or async, include only critical CSS as blocking and load other stylesheets later or combine them. |

## Mobile display checks

1 check: 1 warning.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Viewport tag is missing (`viewport_missing`) | Warning | Without `<meta name="viewport">`, smartphones show the page scaled down in desktop width. Google evaluates the mobile version of a page. **Fix:** Add `<meta name="viewport" content="width=device-width, initial-scale=1">` to the `<head>`. |

## Structured data checks

3 checks: 2 warnings, 1 notice. Validate a single page with the free [schema validator](https://serpel.app/tools/schema-validator).

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Structured data can’t be read (`structured_data_invalid`) | Warning | A JSON-LD block with syntax errors is ignored completely by search engines. Rich results such as breadcrumbs, ratings or FAQ are then lost. **Fix:** Check the contents of the `<script type="application/ld+json">` for missing commas, quotation marks or brackets and test it in Google’s Rich Results Test. |
| Structured data with errors (`structured_data_errors`) | Warning | Required fields are missing from the structured data or values are invalid. Google can’t create rich results such as stars, prices, breadcrumbs or events from it. **Fix:** Add the required fields named in the details and correct invalid values. Then test the result in Google’s Rich Results Test. |
| Structured data without recommended fields (`structured_data_warnings`) | Notice | The structured data is valid, but fields recommended by Google are missing. Rich results are possible, but with more complete data they’re shown more often and are more informative. **Fix:** Add the recommended fields named in the details, as long as the information is visibly present on the page. |

## AI search checks

2 checks: 1 warning, 1 notice. These checks look at your site from the side of AI search. What ChatGPT and Google AI Overviews actually answer is measured separately under [AI visibility](https://serpel.app/docs/ai-visibility). Create an llms.txt with the free [llms.txt generator](https://serpel.app/tools/llms-txt-generator).

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| AI search crawler blocked by robots.txt (`ai_search_crawler_blocked`) | Warning (site-wide) | Crawlers such as OAI-SearchBot, Claude-SearchBot or PerplexityBot fetch content for AI search. If they’re blocked, the website can’t appear as a source in ChatGPT, Claude or Perplexity. **Fix:** Allow the search crawlers in robots.txt. Training crawlers such as GPTBot or ClaudeBot can be blocked separately. |
| llms.txt is missing (`llms_txt_missing`) | Notice (site-wide) | The llms.txt summarises for language models which pages of a website matter. It’s a young proposal, not a standard: coding agents and some tools read it, but no major AI search provider has confirmed using it. **Fix:** Create a short Markdown file at /llms.txt with a name, a description and links to the most important pages. |

## JavaScript rendering checks

8 checks: 3 warnings, 5 notices. These checks need JavaScript rendering. The [site audit guide](https://serpel.app/docs/site-audit) explains when the crawler renders pages.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Content only appears via JavaScript (`js_only_content`) | Warning | Most of the text only appears on the page after JavaScript runs. Google renders JavaScript with a delay, while many other crawlers and most AI crawlers don’t run it at all and then see the page as almost empty. **Fix:** Deliver the main text in the HTML on the server (server-side rendering, static generation or pre-rendering). Only load content via JavaScript that isn’t important for ranking. |
| Canonical differs in the rendered DOM (`rendered_canonical_differs`) | Warning | The canonical in the delivered HTML and the canonical after JavaScript runs name different URLs, or only one of them exists. Conflicting signals can lead Google to ignore the canonical or pick the wrong version. **Fix:** Set the canonical only in the delivered HTML and make sure JavaScript doesn’t change, add or remove it. |
| Meta robots differs in the rendered DOM (`rendered_robots_differs`) | Warning | The robots directive (for example noindex) differs between the delivered HTML and the rendered DOM. If noindex is already in the HTML, Google often doesn’t render the page at all and misses a correction made by JavaScript. If JavaScript adds noindex, the result depends on when rendering happens. **Fix:** Set meta robots only in the delivered HTML or the X-Robots-Tag header and don’t change it with JavaScript. |
| Page probably needs JavaScript rendering (`rendering_recommended`) | Notice (heuristic) | The delivered HTML contains almost no text, but there are traces of a JavaScript framework. Without rendering, the crawler can’t reliably check the title, headings, text and links of this page. That’s why Serpel reports no missing-content issues for it, rather than producing false positives. **Fix:** Turn on JavaScript rendering in the project settings (“Automatic for empty pages” or “Always render”) if the render service is available. The most reliable approach for all crawlers is to deliver title, headings, text and links in the HTML on the server (server-side rendering or static generation). |
| Internal links only created via JavaScript (`js_only_links`) | Notice | A large share of the internal links isn’t in the delivered HTML but is only created by JavaScript. Crawlers without JavaScript then don’t find the linked pages, and link equity isn’t passed on. **Fix:** Put important internal links into the delivered HTML as regular `<a href>` elements, for example in the navigation, and don’t rely on onclick alone for navigation. |
| Title, description or H1 change through JavaScript (`js_meta_changed`) | Notice | The delivered HTML and the rendered DOM contain different values. Depending on method and timing, search engines see one version or the other, and other crawlers only see the raw HTML. **Fix:** Set the title, meta description and H1 to their final value in the delivered HTML so that JavaScript doesn’t need to change them. |
| Page couldn’t be rendered (`render_failed`) | Notice | The render service couldn’t load the page or hit a timeout. The analysis for this page therefore relies on the delivered HTML only; content that only appears via JavaScript is missing from the check. **Fix:** Check whether the page loads completely in the browser and whether scripts or requests take very long or fail. The exact message is in the details of the occurrence. |
| JavaScript errors while rendering (`javascript_errors`) | Notice | Errors occurred in the browser console while rendering the page. Faulty scripts can cause content, links or navigation not to appear even though the page looks complete in the source. **Fix:** Open the page in the browser’s developer tools, check the console and fix the reported errors. The first messages are in the details of the occurrence. |

## Multilingual (hreflang) checks

8 checks: 7 warnings, 1 notice.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| hreflang with invalid language or region code (`hreflang_invalid_code`) | Warning | For hreflang, Google only accepts ISO 639-1 language codes, optionally with an ISO 3166-1 alpha 2 region code, plus “x-default”. Invalid values such as “en-uk”, “en_US” or “de-EU” are ignored, and the language version isn’t assigned. **Fix:** Correct the code, for example “en-GB” instead of “en-uk” and “en-US” instead of “en_US”. The language code always comes before the region code; a region on its own isn’t valid. |
| hreflang without self-reference (`hreflang_missing_self`) | Warning | Every language version must include itself in its own hreflang list. Without the self-reference, Google may evaluate the group of language versions incompletely or not at all. **Fix:** Add an entry with the page’s own language code and its own absolute URL to this page’s hreflang list. |
| hreflang without return link (`hreflang_missing_return`) | Warning | hreflang only applies if both pages reference each other. If page A points to page B, B must also point to A. Without the return link, Google ignores the annotation. **Fix:** Add an hreflang entry back to the referring page on the affected page. The most reliable approach is to output the same complete list on every language version. |
| hreflang points to an unsuitable page (`hreflang_target_not_ok`) | Warning | An hreflang target must respond with status 200, be indexable and declare itself as its canonical. Targets with an error status, a redirect, noindex or a canonical pointing to another URL are ignored by Google. **Fix:** Only point to language versions that respond directly with status 200, aren’t set to noindex and canonicalize to themselves. Otherwise adjust the target or remove the entry. |
| hreflang language code used several times with different URLs (`hreflang_duplicate_language`) | Warning | The same language or region code may only appear once in a list. Several targets for the same code are contradictory, and Google can’t reliably use any of them. **Fix:** Keep exactly one URL per language and region code and remove the other entries. |
| hreflang annotations conflict between HTML, header and sitemap (`hreflang_conflicting_sources`) | Warning | hreflang can be in the HTML, the HTTP header and the XML sitemap. If the annotations for the same page differ, it’s unclear which one applies, and Google may ignore them altogether. **Fix:** Use exactly one source for hreflang, or make sure all sources list the same language codes and URLs for every page. |
| hreflang with relative URL (`hreflang_relative_url`) | Warning | hreflang references must be full URLs with protocol and domain. Relative paths like “/de/” aren’t recognized as a language version by Google. **Fix:** Enter the full, absolute URL in href, for example “https://example.com/de/”. |
| hreflang without “x-default” (`hreflang_x_default_missing`) | Notice | The “x-default” entry names the page for users whose language or region isn’t covered, such as a country selector. Without it, Google decides itself which version these users see. **Fix:** Add an hreflang entry “x-default” with the URL of the default version or the language selector, on all language versions. |

## Core Web Vitals checks

7 checks: 3 warnings, 4 notices. Core Web Vitals come from Google PageSpeed Insights for your most important pages. The [site audit guide](https://serpel.app/docs/site-audit) explains which pages are measured.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Largest Contentful Paint is poor (`lcp_poor`) | Warning | The Largest Contentful Paint (LCP) is over 4 seconds. Google rates that as poor. Visitors wait too long for the main content and leave more often. **Fix:** Optimize the largest visible element, usually an image or a text block: compress and resize images, load the LCP image with high priority (fetchpriority="high") and without lazy loading, and reduce server response time and blocking resources. |
| Interaction to Next Paint is poor (`inp_poor`) | Warning | The Interaction to Next Paint (INP) is over 500 milliseconds. Google rates that as poor. The page responds sluggishly to clicks, taps and input. **Fix:** Shorten long JavaScript tasks, split up heavy computations, remove unnecessary third-party scripts and keep event handlers short. |
| Cumulative Layout Shift is poor (`cls_poor`) | Warning | The Cumulative Layout Shift (CLS) is over 0.25. Google rates that as poor. The page shifts heavily while loading, causing misclicks and frustration. **Fix:** Give images, videos and ad slots fixed dimensions (width and height or aspect-ratio), reserve space for content that loads later and load web fonts so that no text shift occurs. |
| Largest Contentful Paint needs improvement (`lcp_needs_improvement`) | Notice | The Largest Contentful Paint (LCP) is between 2.5 and 4 seconds. Google rates up to 2.5 seconds as good. The value shows how quickly visitors see the main content and is part of the page experience assessment. **Fix:** Optimize the largest visible element, usually an image or a text block: compress and resize images, load the LCP image with high priority (fetchpriority="high") and without lazy loading, and reduce server response time and blocking resources. |
| Interaction to Next Paint needs improvement (`inp_needs_improvement`) | Notice | The Interaction to Next Paint (INP) is between 200 and 500 milliseconds. Google rates up to 200 milliseconds as good. The page reacts noticeably late to clicks and input. **Fix:** Shorten long JavaScript tasks, split up heavy computations, remove unnecessary third-party scripts and keep event handlers short. |
| Cumulative Layout Shift needs improvement (`cls_needs_improvement`) | Notice | The Cumulative Layout Shift (CLS) is between 0.1 and 0.25. Google rates up to 0.1 as good. Content visibly jumps while loading, causing misclicks and frustration. **Fix:** Give images, videos and ad slots fixed dimensions (width and height or aspect-ratio), reserve space for content that loads later and load web fonts so that no text shift occurs. |
| Core Web Vitals couldn’t be measured (`web_vitals_unavailable`) | Notice (site-wide) | The PageSpeed Insights request failed or the quota is exhausted. The crawl was still fully analysed; only the measurements for load time, stability and responsiveness are missing. **Fix:** The measurement is retried on the next crawl. The exact cause is in the details. If you run Serpel yourself, add a PageSpeed API key (PAGESPEED_API_KEY) to raise the quota. |

## Social previews checks

2 checks: 2 notices.

| Check | Severity | Why it matters and how to fix it |
| --- | --- | --- |
| Open Graph tags are missing or incomplete (`open_graph_missing`) | Notice | Open Graph controls the title, description and preview image when the page is shared on social networks or messengers. Without them, random or no previews appear. **Fix:** Add the meta tags og:title, og:description, og:image and og:url to the `<head>`. The preview image should be at least 1200 by 630 pixels. |
| Twitter card (X) is missing (`twitter_card_missing`) | Notice | Without twitter:card, X shows shared links only as a plain preview, even if Open Graph tags are present. The large preview image is only available with the card tag. **Fix:** Add `<meta name="twitter:card" content="summary_large_image">` and, if needed, twitter:title, twitter:description and twitter:image. |

## Where to go next

- [Site audit](https://serpel.app/docs/site-audit): Start a crawl, read the score and compare crawls.
- [SerpelBot](https://serpel.app/docs/serpelbot): How the crawler identifies itself and reads robots.txt.
- [Opportunities](https://serpel.app/docs/opportunities): How audit findings turn into prioritised tasks.
- [Site audit features](https://serpel.app/features/site-audit): What the audit covers, in short.