---
title: "JSON-LD contains a literal </script or <!-- (geo/jsonld-unsafe)"
description: "geo/jsonld-unsafe: User or CMS content containing </script> ends the script element early; that breaks the JSON and is an XSS vector."
canonical: https://void-design.vercel.app/rules/geo/jsonld-unsafe
lastModified: 2026-09-16
---

# JSON-LD contains a literal </script or <!--

`geo/jsonld-unsafe` · severity **error** · category AI search · detected by `void geo` and `void audit`

## Why it matters

User or CMS content containing </script> ends the script element early; that breaks the JSON and is an XSS vector. The Next.js JSON-LD guide requires escaping <.

## How to fix it

Serialize with JSON.stringify(data).replace(/</g, '\\u003c') and use a native <script>, not next/script.

## Example

```tsx
// components/json-ld.tsx (Server Component). JSON.stringify doesn't escape "<", so a string containing
// </script> would break out of the tag; replace it with the JSON escape \u003c.
import type { Graph, Thing, WithContext } from 'schema-dts'

export function JsonLd({ data }: { data: WithContext<Thing> | Graph }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data).replace(/</g, '\\u003c') }}
    />
  )
}
```

## References

- https://nextjs.org/docs/app/guides/json-ld

## More ai search rules

`void geo` reports 41 rules in this category. Generative-engine optimisation: whether AI crawlers that don't run JavaScript see the same content, valid and visible JSON-LD, an explicit AI robots policy, llms.txt, Markdown mirrors and answer-first writing.

- `hidden-streamed-content` Crawlers receive main content only inside hidden streaming containers — [geo/hidden-streamed-content](https://void-design.vercel.app/rules/geo/hidden-streamed-content)
- `ai-bot-blocked` AI crawler user agents get 401/403/429/503 or a challenge page — [geo/ai-bot-blocked](https://void-design.vercel.app/rules/geo/ai-bot-blocked)
- `ai-content-differs` AI crawlers get materially different HTML than browsers — [geo/ai-content-differs](https://void-design.vercel.app/rules/geo/ai-content-differs)
- `jsonld-parse-error` JSON-LD block is not valid JSON — [geo/jsonld-parse-error](https://void-design.vercel.app/rules/geo/jsonld-parse-error)
- `jsonld-missing` Page has no JSON-LD — [geo/jsonld-missing](https://void-design.vercel.app/rules/geo/jsonld-missing)
- `jsonld-not-in-raw` JSON-LD is injected by JavaScript — [geo/jsonld-not-in-raw](https://void-design.vercel.app/rules/geo/jsonld-not-in-raw)
- `home-entity-missing` Home page lacks Organization + a single WebSite node — [geo/home-entity-missing](https://void-design.vercel.app/rules/geo/home-entity-missing)
- `jsonld-schema` JSON-LD @context isn't schema.org or @type isn't a schema.org type — [geo/jsonld-schema](https://void-design.vercel.app/rules/geo/jsonld-schema)

Detected by `void geo` and `void audit`. Explain it in a terminal: `void rules geo/jsonld-unsafe`
