Technical SEO for webshops is no longer about the basics: in Oase Creative's test on 31 October, 56 of 66 Dutch product pages had those in order for Google, yet only 17 also listed shipping and returns.
What does technical SEO mean for a webshop?
Technical SEO for a webshop comes down to one question. What does a bot get when it fetches your product page without running JavaScript? That is the page Google builds its product results on, and the raw HTML an AI crawler without JavaScript receives. We measured exactly that on 31 October 2026 across 66 Dutch product pages, one per webshop.
The basics are there: 56 of the 66 pages have name, image, price and currency in structured data, the minimum Google asks for a product result with a price. What a buyer wants to know next usually is not. We found shipping and returns together on 17 of the 66 pages, GTIN on 31, variants on 6. And where it goes wrong, it is almost never a choice, but a wrong attribute in the theme, a price that only lives in the tracking code, or a page that only exists after JavaScript runs.
This follows on from our test of 70 webshops for AI access. There we checked whether a bot gets in, and wrote that shipping and returns had not been measured yet. Now they have. What follows is our own count with the method included, so you can repeat it on your own store.
How did we measure 66 product pages?
For each webshop we fetched one product page the way a simple crawler does, and counted which product data sits in the raw HTML.
Sample. The same 70 webshops as in our previous test: the top 10 of the Twinkle100 2026 and 60 smaller stores from the winners and finalists of the Dutch Shopping Awards 2025. For 69 stores we found a product page that was in stock, through the product sitemap or the homepage. 66 could be measured. Albert Heijn and Zalando refused our address, Picnic has no product pages on the web, and MyJewellery was unreachable. That leaves seven stores in the top 10 group.
Fetching. First a plain HTTP request with Chrome's user agent. If the server returned a 403 or 429, we tried once more with a headless browser with JavaScript switched off. That applied to 21 of the 66, including 11 of the 13 Shopify stores. Either way, the result is the HTML the server sends, before any script has run.
Counting. A parser reads JSON-LD and microdata, follows references between blocks and picks the main product on each page. As "basics" we count what Google asks for a merchant listing: name, image, an Offer with a price above zero, and a currency.
Limits. One product page per store, one moment, measured from our own Dutch IP address. We counted fields as present; we did not run every page through Google's Rich Results Test. A store can use a different template for variants or bundles.
Which product data is on the page without JavaScript?
Name, image and price are almost always there. The further you move from "what does it cost" towards "can I send it back", the thinner it gets.
| Field in the structured data | All stores (n=66) | Top 10 (n=7) | Smaller stores (n=59) |
|---|---|---|---|
| Product block present | 91% (60) | 6 | 54 |
| Basics for merchant listing | 85% (56) | 5 | 51 |
| Availability | 83% (55) | 5 | 50 |
| SKU | 80% (53) | 5 | 48 |
| Brand | 71% (47) | 6 | 41 |
| Condition (new, used) | 56% (37) | 5 | 32 |
| GTIN | 47% (31) | 4 | 27 |
| Aggregate rating | 39% (26) | 5 | 21 |
| Price valid until | 33% (22) | 0 | 22 |
| Shipping (shippingDetails) | 32% (21) | 3 | 18 |
| Return policy (hasMerchantReturnPolicy) | 30% (20) | 2 | 18 |
| Individual reviews | 18% (12) | 3 | 9 |
| Variants (ProductGroup) | 9% (6) | 1 | 5 |
| Basics plus shipping plus returns | 26% (17) | 2 | 15 |
Of the 60 Product blocks, 57 are JSON-LD and 3 are microdata. Smaller stores do no worse than the big players in this table: 51 of the 59 have the basics, against 5 of the 7 in the top 10. With seven large stores, that is a hint, not a conclusion.
Two fields stand out. GTIN is on fewer than half the pages, while it is the code Google and marketplaces use to recognise a product across stores. And variants appear as a ProductGroup on only 6 of the 66. For a clothing store or a shop with colours and sizes, that can mean a bot sees each size as a separate product, unconnected to the rest.
For comparison: HTTP Archive's Web Almanac found Product as JSON-LD on 0.77% of all pages in 2024. That covers the whole web, not product pages, so it says little about webshops. We found no Dutch count per field on product pages.
Why are the basics missing at 10 of the 66 webshops?
At ten stores we found no usable product data for a merchant listing. We fetched the most striking ones again by hand. Almost none of it is a deliberate choice.
| Webshop | What we found | Result for Google |
|---|---|---|
| Bamboo Basics (Shopify) | A complete Product object, but in <script type="application/json"> |
Not read as structured data |
| Gewoonstijl (Magento) | Product with name, SKU and GTIN, but no Offer; the price only sits in the Google Tag Manager dataLayer | No merchant listing |
| Wijnvoordeel | An empty shell with only og tags; everything comes through JavaScript | Nothing to read without rendering |
| Amazon.nl | No JSON-LD, no microdata and no price in the raw HTML | Nothing to read in the HTML |
| Jumbo and LuxuryFires | Only an AggregateOffer with a price range | Enough for a product snippet, not for a merchant listing |
The other four are a configurator page without a Product block, a WooCommerce store with three JSON-LD blocks but no product in them, a brand store without a Product block, and a Magento page without an Offer.
Bamboo Basics shows how small the cause can be. The product is all there: name, image, description. But the script block has the type application/json instead of application/ld+json. To a person, that is one word. To Google, it is the difference between a product and a piece of code it skips.
Gewoonstijl is the other way round: the product details are correct, but the price only sits in the code that goes to Google Analytics. Marketing has the price; the search engine does not.
A warning for anyone measuring this themselves. In our first count, Kamera Express looked like a page without a price. Its Offer block points with an @id to a separate block elsewhere on the page. A parser that does not follow that reference sees no price. After the fix, the page had a price and stock like any other. That error sits in the measuring tool, not in the store.
Do shipping and returns need to be on your product page?
Together with the basics they appear on 17 of the 66, roughly a quarter, and that is less bad than it sounds. Google would rather have that data once at store level than on every page.
On the product page we found shipping at 21 of the 66 and returns at 20. Basics, shipping and returns together: 17. Shopify stands out: 12 of the 13 Shopify stores have the basics, but only one adds shipping and returns. The default theme sets up Product and Offer and leaves the rest empty.
Still, "missing" is the wrong word here. In its documentation for return policies and shipping policies, Google advises setting one policy for the whole store under Organization, and only deviating per product when that product has different terms. Settings in Merchant Center or Search Console take precedence over both. If you have that in place, you do not need it on every product page.
So we also looked at the homepage. 50 of the 66 stores have an Organization block there. A return policy appears in 6, a shipping policy in 1. Policies on a separate returns page or in Merchant Center fall outside our test, so this is a lower bound.
For comparison, a larger international measurement. ARC Report checks more than ten thousand stores through plain HTTP requests, without a Dutch breakdown:
| Measurement | Returns in structured data | Shipping in structured data |
|---|---|---|
| ARC Report, as of 4 November 2026 | 4.5% | 3.8% |
| Our test, product page (n=66) | 30% | 32% |
| Our test, homepage (n=66) | 9% | 2% |
Our Dutch sample sits well above that. Partly because ARC also measures many small stores while we picked award winners. The pattern is the same: the policy exists, but rarely in a form a machine can read without guessing.
What does an AI agent see if it does not run JavaScript?
At most stores, enough. At a few, almost nothing. The difference is not in the structured data, which nearly always comes from the server, but in the text around it.
To check this, we put the raw HTML of 20 product pages next to the page as built by JavaScript. For 16 of them that worked. We counted in two ways: everything in the source, hidden blocks included, and only what a browser without JavaScript actually shows.
| Result (n=16) | Full source | Visible only |
|---|---|---|
| Price in JSON-LD only after JavaScript | 0 | 0 |
| Core field (name, price, description, reviews or delivery) only after JavaScript | 2 | 3 |
| Share of text that only appears through JavaScript | 21% | 29% |
| Median per page in the raw HTML | 90.5% | 87.8% |
On all 15 pages with a Product block, the price was already in the raw HTML. The exceptions are in the text:
- Belsimpel. Without JavaScript the page only says "Oeps, je hebt Javascript uitgeschakeld!" (Oops, you have JavaScript disabled). Description, reviews and delivery time arrive after the script. Name and price are in the JSON-LD.
- Coolblue. The server sends all the text, but in hidden blocks that a script moves into place. A parser finds everything. A browser without JavaScript shows 25 words of the whole page.
- Seepje. The review block with the score and number of ratings only appears through JavaScript.
- Kookwinkel Oldenhof. The raw HTML is a check page asking whether you are human. A crawler without JavaScript gets no further.
Why that matters: Vercel and MERJ wrote in December 2024 that none of the major AI crawlers execute JavaScript, naming OpenAI, Anthropic and Perplexity. Gemini, which runs on Googlebot's infrastructure, is the exception. Glenn Gabe repeated the test in October 2026 and got the same result. The documentation of OpenAI, Anthropic and Perplexity says nothing about it. What their fetchers do with hidden text, nobody outside those companies knows.
A description or review block that only appears through JavaScript is something an AI assistant cannot quote. An AI agent reads the page differently from a visitor, as we saw in our test of what AI agents read on product pages.
Does ChatGPT read the same technical SEO as Google?
No. Google reads the markup on your page. The shopping channels of ChatGPT and Shopify work from their own feed or catalog. Technical SEO for webshops therefore covers two layers.
| Data | Google merchant listing | OpenAI product feed | Shopify Catalog (UCP) |
|---|---|---|---|
| Name, price, currency | required | required | required |
| Image | required | required | optional |
| Availability | recommended | required | optional |
| Description | recommended | required | required |
| GTIN | recommended | optional | optional (barcodes) |
| Returns | recommended, preferably at store level | optional | optional (policy) |
| Reads the JSON-LD on your page | yes | not documented | no, through the platform |
OpenAI's feed specification is an upload format with its own fields, such as whether you accept returns and within how many days. Schema.org does not appear in it. The shared format only lists the United States, Canada and Mexico as markets. The help page on shopping in ChatGPT says product data comes from third-party providers and from merchants themselves, and that for Shopify stores it already arrives through Shopify Catalog. UCP's Catalog specification takes products from the platform, not from the page.
What that means for you: the markup on your product page is for Google, and the feed or the product data in your platform is for the agents. A gap in one layer is not fixed in the other. How strongly the Google feed carries through into ChatGPT is covered in our article on the Google Shopping feed and AI recommendations. How agents buy through Shopify is in agentic commerce through Shopify.
Should you handle training bots differently in robots.txt now?
Not yet, but soon it may take a single line. On 30 October 2026, OpenAI wrote that it will honour a common signal in robots.txt for training generative models, "in the coming months". The signal comes from an IETF draft published the same day. You write an ordinary robots.txt group with User-Agent: __usage_ai-training followed by Disallow: /.
On 31 October we fetched the robots.txt of the same 70 webshops. 62 could be read.
- The new signal: at 0 of the 62. One day after publication that is to be expected; this is the baseline.
- Training bots by name: 14 of the 62 name at least one separately, such as GPTBot, CCBot or Google-Extended, just over five per store on average.
- Cloudflare's signal: 5 of the 62 use
Content-Signal. Four of them say no to training, one says yes. OpenAI does not mention that signal.
One line in the draft matters if you already have rules in place: a group with User-Agent: * explicitly does not count as a preference. If you want to refuse training, you have to name the token.
This is about training only, not search. Leave search and user bots open separately, as described in our test of AI access. The draft has no formal status yet. Feel free to add the line if you want to refuse training, but do not remove your existing per-bot rules for it.
How do you check the technical SEO of your own product page?
In five steps on one product page. You do not need paid tools, but you do need the source code.
- Open the source, not the page. Use "view page source" or
curlwith a normal browser user agent. What you see there is what a crawler without JavaScript gets. Your browser's inspector shows the built page, which hides exactly the problem. - Search for
application/ld+json. Is there a Product with an Offer? Check the type of the script block.application/jsonlooks similar but is not read. - Look at the price. An Offer with a price above zero and a currency. An AggregateOffer with only a range is enough for a product snippet, not for a merchant listing. If your price only sits in the dataLayer, only your tracking sees it.
- Pick one place for shipping and returns. Set the policy in Merchant Center or as Organization markup on your site. Put it on the product page only for products with different terms, and then fill in country and policy type.
- Switch off JavaScript. See what is left of your description, reviews and delivery time. If the description disappears, an AI assistant cannot quote it.
Only then is Google's Rich Results Test worth running to validate the blocks. If you want to check category pages too, continue with what you do not need to fix on Shopify category pages.
What did we find when we ran the same test on our own site?
A clean base and one outdated habit. We fetched all 156 Dutch pages of oasecreative.nl from the sitemap of 31 October and read the structured data as described above.
The good news: zero broken JSON-LD blocks, and on every page exactly one organisation block with the same name, address and phone number. Every knowledge base article has a BlogPosting, every service page a Service.
The less good news: 119 of the 156 pages carry FAQPage markup. According to the Google Search Central changelog, that rich result has not appeared in Google since 7 May 2026. It is not an error; the markup is correct. But it is work for a result that no longer exists, and you only see that when you look at your own markup.
A second point: 32 of the 91 articles had no last-modified date in the schema, because our template only sets it when the article has an update date. Google calls that field recommended, not required. It is exactly the kind of gap this test is about: nothing technically wrong, but one field less than possible.
And on the Dutch keyword "technische seo" we had zero impressions in Search Console over 90 days. This article is our first page on it. Opening it with a measurement instead of a checklist is a deliberate choice. The checklists ranking at the top do not mention shipping or returns and have no data of their own.
How does Oase Creative help with technical SEO for your webshop?
We run the same test on your store. We fetch one product page per template without JavaScript, count every field, and put the raw HTML next to the built page. In a few minutes that shows whether your problem sits in the theme, the price or the text.
The work then runs through three layers:
- The page. Product and Offer complete, GTIN and variants included, and the script block type correct. That is part of our SEO service. If your store runs on Shopify, we adjust the theme as part of our Shopify work.
- The store. One shipping and returns policy, in Merchant Center or as Organization markup, and not accidentally different in two places.
- The feed. Product data in your platform that gives the same price, stock and terms as your page. Want to know when a theme update removes a field again? Then we run the check automatically, through AI automation.
What AI search asks of your webshop beyond this is covered in GEO for webshops. This test is from 31 October 2026. We will repeat it with the same stores and update this article when the numbers shift.
Send Louie the link to one product page. We fetch it without JavaScript and tell you which fields a bot finds and which it does not.

