Technical SEO audit

demo.ivanets.co

494 pages · crawled 15 September 2026 in 6 minutes · server-rendered HTML only
494
pages analysed
494
URLs in the sitemap
75*
average page score / 100
53 ms
median time to first byte

An average across the 48 checks that run on every page. A fault repeated by a template counts on every page it affects but is fixed once — the effort table shows where these concentrate.

Read this first

Malformed sitemap entries

3 broken addresses, and it is the one to start with: the sitemap is what a search engine is asked to crawl, and these entries are not addresses it can reach. It is set out in full, with the fix, in the findings below.

Every finding below also comes back as a user story with acceptance criteria a developer can close on a measurement. One example after the findings.

Indicative effort

4–5 weeks with one dedicated developer and one QA engineer

16–20
developer-days
8
QA-days
4–5
elapsed weeks, one developer
What gets changedDaysFindings it covers
Structured data template9No structured data; Structured data missing required properties
The sitemap generator6Out-of-stock products — catalogue check needed; Malformed sitemap entries; Dead URLs still in the sitemap; Soft 404s
Parameterised URLs published in the sitemap3Parameterised URLs published in the sitemap
Redirects and internal links1Internal links to removed pages

Findings fixed in the same place are costed once: the largest at its full size, each additional one at a day. Sizes are S 1, M 3, L 8 developer-days, reduced where a finding affects only a handful of pages.

Elapsed time is per developer, and the streams above are largely independent: two developers splitting them finish in roughly 2–3 weeks. Assumes 4 productive days per week with QA running alongside development rather than after it. Indicative only: it assumes a developer already familiar with the platform and a normal release cadence. Your team should re-estimate against its own velocity.

Each row above is broken into individual stories, one per finding, with acceptance criteria — see the findings below.

This estimate covers the template, metadata, sitemap, structured-data and redirect work detailed below. It does not cover server-rendering the navigation and page content — that is a change to how the front end is built, and it needs its own discovery before anyone can size it honestly. Completing everything in this estimate will materially improve indexing and crawl efficiency; it will not by itself put the page content into the server response.

Root cause

Navigation not in server HTML

277 of the 467 crawled URLs have no internal link in any page’s server-delivered HTML — navigation is client-side. It also explains the one further check reporting against nearly every page: it looks for something the server response does not contain. That check is listed below but counted once — No breadcrumb structured data, on 467 pages.

Fixing this is a change to how the front end is built and needs its own discovery, which is why it is not covered by the estimate above.

This is a free sample: 10 findings in detail out of 37. Every sitemap entry was checked, so these are counts rather than estimates. The complete audit crawls the whole sitemap and follows internal links beyond it.

Findings

10 shown · 37 found.

13 high · 16 medium · 8 low
high

Malformed sitemap entries

3 broken addresses
Indexability · Crawl efficiency · Backend

This sitemap entry contains an unresolved template placeholder.

${siteMapUrl.loc}

Fix Fix the sitemap generator so every <loc> is an absolute, fully resolved HTTPS URL. Entries containing an unresolved template placeholder or a literal "undefined" indicate a variable that was never substituted — trace it to the generating code rather than removing the URLs by hand.

3 broken addresses are not valid crawlable addresses, repeated across 6 sitemap entries. contains an unresolved template placeholder; contains whitespace; contains a literal 'undefined', 'null' or 'NaN', which means a template variable resolved to nothing.

View the user story for developers

As a search engine, I should receive only fully resolved absolute URLs in a sitemap, so that no crawl budget is spent on addresses that cannot exist.

Acceptance criteria
Scenario: Every entry is an absolute URL
Given the full sitemap is re-generated
When each <loc> is read
Then each is an absolute https:// URL with no whitespace
And none contains an unresolved template placeholder
Scenario: The generator fails loudly rather than writing a broken entry
Given a product whose URL cannot be resolved
When the sitemap is generated
Then that entry is omitted and the failure is logged
Verification
curl -s https://demo.ivanets.co/sitemap.xml | grep -oE '<loc>[^<]*</loc>' | grep -vE '<loc>https://[^ <]+</loc>'
# expected: no output
Expected response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

3 entries currently fails this. Written as observable outcomes, so every criterion is verifiable with an HTTP request and none of it assumes a particular platform.

high

Dead URLs still in the sitemap

29 pages
Indexability · Crawl efficiency · Backend

The URL returns HTTP 404 but is still listed in the sitemap.

demo.ivanets.co/catalogue/cooking/built-in-ovens/ardvane-au…

Fix The status code is fine; the defect is that a removed URL is still being advertised in the sitemap. Remove it from the sitemap generator.

View the user story for developers

As a search engine, I should not be offered URLs that no longer exist, so that crawl budget is spent on live products.

Acceptance criteria
Scenario: A retired trade page is requested
Given the URL https://demo.ivanets.co/catalogue/cooking/built-in-ovens/ardvane-aurora-200-oven/p/KV-900000
When a crawler requests it
Then the response status is 410
And the URL appears in no XML sitemap
Scenario: A retired commercial page has a successor
Given a URL marked "review" in the redirect map
When an owner decision has been recorded
Then the URL either 301s to a named live URL
Or is restored and returns 200
Scenario: The sitemap is clean after remediation
Given the full sitemap is re-crawled
When every declared URL is requested
Then no URL returns 404
Verification
curl -s -o /dev/null -w "%{http_code}\n" https://demo.ivanets.co/catalogue/cooking/built-in-ovens/ardvane-aurora-200-oven/p/KV-900000
# expected: 410
Expected response
HTTP/1.1 410 Gone
Content-Type: text/html; charset=utf-8

Written as observable outcomes, so every criterion is verifiable with an HTTP request and none of it assumes a particular platform.

high

Navigation not in server HTML

277 of 467 pages
Images & links · Indexing · Platform

No internal link to this URL appears in the server HTML of any page scanned.

demo.ivanets.co/catalogue/cooking/built-in-ovens/belquist-r…

Fix Render the links inside category listings, pagination and related-product modules server-side, so they exist in the HTML before JavaScript runs. Google does execute JavaScript, but it does so on a second pass that is slower and not guaranteed, and every other crawler, link-equity calculation and internal-search index sees only what is in the initial response.

277 of the 467 crawled URLs that could carry an inbound link — pages answering 200 in a linkable segment — have none, while every page carries about 25 identical navigation links. That pattern means the listings are rendered client-side, not that the pages are orphaned.

View the user story for developers

As a crawler, I should find the links to products in the HTML the server returns, so that the catalogue is reachable by following links and not only by reading the sitemap.

Acceptance criteria
Scenario: A category listing links its products before JavaScript runs
Given the category page /catalogue/cooking/hobs/ardvane-aurora-200-hob/p/KV-600000
When the HTML is requested with JavaScript disabled
Then it contains an anchor to every product shown on that page
And each anchor has an href a crawler can follow, not an onclick handler
Scenario: Pagination is reachable without JavaScript
Given a category with more products than fit on one page
When the first page is requested
Then the HTML contains a link to page two
And following those links reaches every product in the category
Scenario: Every product has at least one inbound internal link
Given the 277 URLs listed in this finding
When the site is crawled without executing JavaScript
Then each of them is reached from some other page
Verification
# Fetch a listing and count the product links in the raw HTML.
curl -s https://assets-cdn.kelverton-demo.invalid/catalogue/cooking/hobs/ardvane-aurora-200-hob/p/KV-600000 \
  | grep -o 'href="[^"]*/product/[^"]*"' | sort -u | wc -l
# expected: the number of products the page displays, not 0
Expected response
A number matching the products shown on the page.
Today this returns 0 on listings that build their grid in the browser.

Google does execute JavaScript, so this is not "the pages are invisible" — it is that discovery is slower, less certain, and carries no internal link signal. Written as observable outcomes, so every criterion is verifiable with an HTTP request and none of it assumes a particular platform.

high

Structured data missing required properties

139 pages
Structured data · Rich results · Backend

1 structured-data node is missing the properties Google requires, so the page cannot qualify for the matching rich result.

demo.ivanets.co/catalogue/cooking/built-in-ovens/belquist-r…

Fix Add the missing properties. Google will not show a rich result when a required property is absent, even though the JSON-LD itself is valid.

View the user story for developers

As a search engine, I should find every required property on a product, so that the page qualifies for the product rich result instead of being skipped.

Acceptance criteria
Scenario: A product carries the required properties
Given the URL https://demo.ivanets.co/catalogue/cooking/built-in-ovens/belquist-ransom-189x-oven/p/KV-100018
When its Product structured data is read
Then name, image and offers are present
And offers carries price, priceCurrency and availability
Scenario: A product with no price is not advertised as purchasable
Given a product with no price in the catalogue
When its page is generated
Then no offers block is emitted rather than one with an empty price
Verification
curl -s https://demo.ivanets.co/catalogue/cooking/built-in-ovens/belquist-ransom-189x-oven/p/KV-100018 | grep -o '"\(name\|image\|price\|priceCurrency\|availability\)":' | sort -u
# expected: all five present
Expected response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

"offers":{"@type":"Offer","price":"…","priceCurrency":"UAH","availability":"…"}

139 pages are missing at least one. Written as observable outcomes, so every criterion is verifiable with an HTTP request and none of it assumes a particular platform.

high

Soft 404s

5 pages
Indexability · Indexing · Backend

The URL redirects to the homepage. Google may treat a redirect to an unrelated page as a soft 404: the old URL stays in the index, the redirect earns nothing, and crawl budget is spent re-checking it.

demo.ivanets.co/brands/c/B901

Fix Either 301 to a genuine replacement page, or return 404 or 410. A redirect to the homepage may be treated as a soft 404: it keeps the old URL in the index and earns nothing.

high

Internal links to removed pages

3 pages
Internal links · Crawl efficiency · Backend

Links to https://demo.ivanets.co/catalogue/cooking/built-in-ovens/ardvane-aurora-200-oven/p/KV-900000 (HTTP 404) with the anchor "Ardvane Aurora 200 oven".

demo.ivanets.co/help/spare-parts

Fix Update or remove the links listed. A removed page can be de-indexed correctly and still be linked from live pages, which wastes crawl budget, leaks internal link equity into nothing, and sends visitors to an error. This is usually a stale navigation block, a "related products" module reading from an unfiltered query, or a hard-coded footer link.

3 links from 3 pages point at 3 URLs.

high

Content not server-rendered

25 pages
Content · Indexing · Platform

The server HTML contains only 111 words of text.

demo.ivanets.co/help/delivery
high

No structured data

220 pages
Structured data · Rich results · Backend

No JSON-LD structured data was found in the server-rendered HTML.

demo.ivanets.co/catalogue/cooling/fridge-freezers/belquist-…

Fix Add schema.org JSON-LD describing the page: Product/Offer for product pages, Article for editorial, Organization + WebSite on the home page.

medium

Parameterised URLs published in the sitemap

90 sitemap entries
Indexability · Crawl efficiency · Backend

?lat= carries 90 distinct parameter combinations.

demo.ivanets.co/store/aberdeen?lat=57.1372&long=-2.1068
Fix
  1. Stop emitting parameterised URLs into the sitemap. A sitemap should list only the canonical URLs you want crawled.
  2. Give each parameterised URL a rel="canonical" pointing at the clean page, and leave crawling allowed so the canonical can actually be read.
  3. Only once the parameterised URLs have dropped out of the index, consider a robots.txt Disallow — and treat it purely as a crawl-budget decision, never as a de-indexing one. Blocking before step 2 has taken effect strands the duplicates in the index with no way to consolidate them.

90 of 494 sitemap URLs (18%) are parameterised or filtered variants: ?lat, ?long 90 of them are ?lat / ?long permutations of a single page rather than product facets, so the target is the unparameterised page — URL-parameter handling suits those better than a canonical on each one.

medium

Out-of-stock products — catalogue check needed

18 pages
Indexability · Indexing · Merchandising, Backend

The page returns HTTP 200 and is listed in the sitemap, while its Product structured data declares availability: OutOfStock. Out-of-stock is a valid indexable state for a product that will return, so this is not automatically a fault — but it cannot be told apart from a discontinued product without the catalogue.

demo.ivanets.co/catalogue/cooking/built-in-ovens/belquist-r…

Fix Split these against the catalogue. Products that will return are correct as they stand — keep them indexed with the current markup. Products that are discontinued should answer 404 or 410, or 301 to a comparable successor, and leave the sitemap.

Also found — not detailed in this sample
Absent from server-rendered HTML — one check, one rendering dependency

This element was absent from the initial server response. It may appear once JavaScript executes, so treat the count as evidence of the rendering dependency rather than an independent fault. A rendered crawl is what separates genuinely missing elements from client-side ones.

Every finding becomes a ticket

Stop emitting sitemap entries that are not valid URLs

SEO-1 · 3 URLs across 1 template · Backend · Crawl efficiency · 1 developer-days, 1 QA-day

As a search engine, I should receive only fully resolved absolute URLs in a sitemap, so that no crawl budget is spent on addresses that cannot exist.

Acceptance criteria
Scenario: Every entry is an absolute URL
Given the full sitemap is re-generated
When each <loc> is read
Then each is an absolute https:// URL with no whitespace
And none contains an unresolved template placeholder
Scenario: The generator fails loudly rather than writing a broken entry
Given a product whose URL cannot be resolved
When the sitemap is generated
Then that entry is omitted and the failure is logged
Verification
curl -s https://demo.ivanets.co/sitemap.xml | grep -oE '<loc>[^<]*</loc>' | grep -vE '<loc>https://[^ <]+</loc>'
# expected: no output
Expected response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

Every story is written this way: the acceptance criteria are checkable with an HTTP request, so they can be signed off without an SEO in the room.

Redirect map

54 URLs needing a redirect, a removal, or a decision

Every URL below is classified by what it does now. Where a successor could be identified it is named and resolved; where none could be, the row says so and recommends removal or review rather than guessing. A catalogue export is what turns the review rows into named successors.

URLActionTargetReason
demo.ivanets.co/catalogue/…/ardvane-aurora-200-hood/p/KV-800000301demo.ivanets.co/…/thurlby-stan…/p/KV-100114The URL already resolves to demo.ivanets.co/…/thurlby-stanmer-541sx-warm…/p/KV-100114, which returns HTTP 200, but only after 3 redirects. Point the original URL straight at the destination.
demo.ivanets.co/catalogue/…/ardvane-aurora-200-oven/p/KV-900000410—The 404 is a correct response for a removed page — the defect is that the URL is still advertised in the sitemap. Remove it from the sitemap generator. A 410 can be used to signal deliberate permanent removal, but changing 404 to 410 is not required.
demo.ivanets.co/brands/c/B901review—The URL redirects to the homepage, which Google may treat as a soft 404. No successor could be identified from the URL structure alone. Redirect to an equivalent page if one exists; otherwise return 404 or 410 and remove the URL from the sitemap. A catalogue export is what lets a successor be proposed instead.
demo.ivanets.co/catalogue/…/farholt-hallow-201-microwave/p/KV-800001301demo.ivanets.co/…/thurlby-yeld…/p/KV-100126The URL already resolves to demo.ivanets.co/…/thurlby-yeldon-137sx-warmi…/p/KV-100126, which returns HTTP 200, but only after 3 redirects. Point the original URL straight at the destination.
demo.ivanets.co/catalogue/…/grendale-stanmer-206-oven/p/KV-900006410—The 404 is a correct response for a removed page — the defect is that the URL is still advertised in the sitemap. Remove it from the sitemap generator. A 410 can be used to signal deliberate permanent removal, but changing 404 to 410 is not required.
demo.ivanets.co/brands/c/B902review—The URL redirects to the homepage, which Google may treat as a soft 404. No successor could be identified from the URL structure alone. Redirect to an equivalent page if one exists; otherwise return 404 or 410 and remove the URL from the sitemap. A catalogue export is what lets a successor be proposed instead.
assets-cdn.kelverton-demo.invalid/…/farholt-hallow-201-hood/p/KV-600001review—The URL could not be fetched: DNS lookup failed for assets-cdn.kelverton-demo.invalid. That is not a removal and not a status code — establish whether the URL should resolve at all before deciding between 200, 301 and 410.
demo.ivanets.co/catalogue/…/belquist-ransom-189x-oven/p/KV-100018review—The URL returns HTTP 200 and is listed in the sitemap, while its Product structured data declares the item unavailable. Out-of-stock is a valid indexable state for a product that will return, so check it against the catalogue: keep it as it stands if it will come back, 410 or 301 it if it is discontinued.
demo.ivanets.co/catalogue/…/ardvane-aurora-200-range-cooker/p/KV-700000review—Search engines cannot read the page while it returns 403. Decide whether it should be publicly accessible.
demo.ivanets.co/catalogue/…/kestrane-orwell-202-range-cooker/p/KV-800002301demo.ivanets.co/…/velmont-hall…/p/KV-100129The URL already resolves to demo.ivanets.co/…/velmont-hallow-591x-warmin…/p/KV-100129, which returns HTTP 200, but only after 3 redirects. Point the original URL straight at the destination.
demo.ivanets.co/…/prendale-vantry-203-warming-drawer/p/KV-800003301demo.ivanets.co/…/yarnton-pall…/p/KV-100111The URL already resolves to demo.ivanets.co/…/yarnton-pallant-262pro-war…/p/KV-100111, which returns HTTP 200, but only after 3 redirects. Point the original URL straight at the destination.
demo.ivanets.co/catalogue/…/marbeck-merrow-212-oven/p/KV-900012410—The 404 is a correct response for a removed page — the defect is that the URL is still advertised in the sitemap. Remove it from the sitemap generator. A 410 can be used to signal deliberate permanent removal, but changing 404 to 410 is not required.
AI crawler readiness

Whether a retrieval bot can fetch, parse and quote these pages.

A separate score, reported beside the technical one and never folded into it — the two have different owners and different fixes. It is measured the only way a crawl can measure it: with HTTP requests. It is not a measure of how often the brand appears in AI answers, which needs a prompt set rather than a crawler.

75
Technical SEO
average page score / 100
85
AI crawler readiness
good / 100
100
Band A — Accessweight 45
Whether a retrieval bot is allowed to fetch the page, and whether it actually receives it.
92
Band B — Extractabilityweight 35
Whether the answer is in the server response, and whether the bot can find it there.
39
Band C — Machine-readable identityweight 20
Whether the page says what it is, whose it is, and what it sells, in markup.
medium

Incomplete Product and Offer markup

Band C

61 of the 220 assessed product pages carry complete Product and Offer markup.

  • offers.price missing on 139 product pages
  • offers.priceCurrency missing on 139 product pages
  • brand missing on 20 product pages
  • url matching the canonical missing on 2 product pages

Fix Complete the Product and Offer markup in the product template: price as a bare number, priceCurrency as an ISO 4217 code, availability as a schema.org value, and url matching the canonical.

low

19% of assessed pages do not have exactly one H1

Band B

467 of the 467 assessed pages declare a <main> or <article> landmark, and 377 of the 467 assessed pages have exactly one H1. Where a page does not carry exactly one H1, a retrieval bot has no single heading to treat as the subject of the page.

Fix Give every page exactly one H1.

low

Structured data does not parse

Band C

8 of the 467 assessed pages carry JSON-LD that does not parse — 2% of what was assessed. The technical audit reports this in full; the count is not repeated here.

Fix Reported in full by the technical audit — the same fix resolves both. A block that does not parse is not read at all, so everything below it in this band is unavailable to a model.

low

No Organization node with sameAs

Band C

6 of the 467 assessed pages declare an Organization node with sameAs.

Fix Add an Organization node to the site-wide template with sameAs pointing at the company's own verified profiles.

low

No BreadcrumbList structured data

Band C

No assessed page carries BreadcrumbList markup.

Fix Add BreadcrumbList to the category and product templates.

low

No dateModified agreeing with the sitemap

Band C

No assessed page declares dateModified, so a model has nothing to judge freshness by.

Fix Emit dateModified from the same content-modified timestamp the sitemap lastmod uses, so the two cannot drift.

Reported, not scored

No training crawler is disallowed. That is a policy choice either way, and it is not scored.

No llms.txt file is published. Not scored either way. Ahrefs examined the 137,210 domains in its Web Analytics that received traffic in May 2026 and found that 97% of llms.txt files were fetched by nothing at all that month; AI retrieval crawlers were 1.1% of the requests that did arrive (ahrefs.com/blog/llmstxt-study, 15 June 2026). Google has said it has no plans to support it, and OpenAI's crawler documentation points to robots.txt instead. Perplexity is the one confirmed consumer.

What this score does not tell you

85 of 100 is the "good" band — 90 and above is healthy, 80 good, 65 needs improvement. Band A carries weight 45 because a bot cannot quote what it cannot fetch, and bands B and C are moot where A fails. Every vendor score of this kind is proprietary and none are comparable to each other. A site can score well here and still never be cited.

What this audit does not tell you

Scope and limits.

* 75 of 100 is the "needs improvement" band — 90 and above is healthy, 80 good, 65 needs improvement. It is an average of page content: the 48 checks that run on every page in a full-site audit. A further 19 — Core Web Vitals, sitemap and broken-link checks — are reported at site level instead, so the figure cannot reflect them. A site can score well here and still be badly indexed, which is why the findings above it matter more than the number.

What the complete audit includes

You are reading the free sample: 10 findings of 37, across 100% of the sitemap.

This site is small enough that the sample covered all of it. On a larger catalogue the sample is a slice, and the complete audit is what makes the counts totals.

Prices are published at ivanets.co/pricing.

Once the fixes are live

Closing a story is a measurement, not an opinion.

Every acceptance criterion in the backlog is an HTTP-observable outcome.

Once your team has released, the same crawl runs again over the same 494 URLs. Each story returns pass or fail, with the response that proves it. Anything that regressed elsewhere on the site is flagged in the same crawl.

The output is a verification report: story by story, what was claimed, what was measured, and the evidence. Use it for internal sign-off, share it with stakeholders, or pass it to a client where that applies.

One verification crawl is included. Where the fixes land across several releases, it can re-run after each — up to three crawls, within 90 days of delivery.

What I need from you

Nothing, to commission it.

The sitemap is public and already crawled.

A product-catalogue export turns the redirect map from decisions into instructions — dead URLs matched to successor SKUs and issued as 301s, instead of 410 with a note. Without it roughly 50 rows stay as 410-or-review. It also splits the 18 out-of-stock pages into returning and discontinued, which cannot be told apart from outside the business.

Read-only Search Console access orders the backlog by traffic actually at risk rather than by page count, so the expensive things get fixed first.

Either can arrive during the five days. Data is read-only, held only for the engagement, deleted on delivery. I am happy to sign an NDA where required.

Commission it, or ask a question — artem@ivanets.co