# SEO and Paywalling Best Practices

Source: https://docs.ezoic.com/docs/subscriptions/seo-paywalling-best-practices/


When you gate article content, search engines need to understand that the content is intentionally paywalled. Ezoic Subscriptions gives you the access and checkout layer, but your page markup should still follow Google's paywalled-content guidance.

Use Google's official documentation as the source of truth: [Paywalled content structured data](https://developers.google.com/search/docs/appearance/structured-data/paywalled-content).


**This page applies only when you gate readable article or page content.** If your paid product removes ads, unlocks a tool or feature, or sells a download, you do not need paywalled-content structured data — you can skip this page. Add this markup only for article or page content you hide from readers behind the paywall.


## Recommended Page Structure

Keep teaser content visible to everyone, then wrap subscriber-only content in a real CSS class such as `.paywalled-content`.

```html
<article>
  <div class="article-teaser">
    <!-- Headline and first paragraphs visible to everyone -->
  </div>
  <div class="paywalled-content" data-premium-content hidden>
    <!-- Soft gate: full body lives here, revealed after access. -->
    <!-- Hard gate: fetch and inject the body after access instead. -->
  </div>
</article>
```

In this example:

- `.paywalled-content` is the CSS selector your structured data points at.
- `data-premium-content` is the hook your own integration code reveals after `hasAccess(...)` returns `allowed` (see [Onsite Script Integration](/docs/subscriptions/site-integration/)). The widget verifies access and renders the paywall; it never reveals your gated content for you.
- `hidden` starts the subscriber-only section hidden for a soft gate.

If you use a hybrid or server-side approach, keep the same selector on the gated container and fetch or render the protected body only after access is allowed.

## Add Paywalled-Content Structured Data

Add `isAccessibleForFree: false` and `hasPart` to your `Article` or `NewsArticle` structured data. Replace the example values with the real values for your article.

```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "NewsArticle",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://example.com/article-url"
  },
  "headline": "Article headline",
  "image": ["https://example.com/article-image.jpg"],
  "datePublished": "2026-06-18T08:00:00-07:00",
  "dateModified": "2026-06-18T09:00:00-07:00",
  "author": {
    "@type": "Person",
    "name": "Author Name"
  },
  "description": "Article teaser or summary",
  "isAccessibleForFree": false,
  "hasPart": {
    "@type": "WebPageElement",
    "isAccessibleForFree": false,
    "cssSelector": ".paywalled-content"
  }
}
</script>
```

If your page already has `Article` or `NewsArticle` JSON-LD, merge the paywall fields into the existing object instead of adding a conflicting duplicate article record.

## `cssSelector` Requirements

The `cssSelector` value must point to the gated content container on the page.

Use:

```json
"cssSelector": ".paywalled-content"
```

when your HTML contains:

```html
<div class="paywalled-content" data-premium-content hidden>
  <!-- Subscriber-only content -->
</div>
```

Do not point `cssSelector` at a missing class. Avoid nested paywalled sections unless your structured data accurately describes each gated section.


**SEO safety checks:** Do not `noindex` or robots-block gated pages that should rank in Google. Do not serve different article HTML to Googlebot based only on the user agent. Do not cloak full content to crawlers while showing unrelated or lower-quality content to users. For hard server-side walls, allow crawler access only after verifying crawler identity; do not trust a `Googlebot` user-agent string by itself.


## Social Previews

Social previews should not depend on subscriber state. Use teaser-level metadata that is safe for every visitor:

- `og:title`
- `og:description`
- `og:image`
- `twitter:card`

Do not require authentication before social crawlers can read these tags.

## Caching

How caching affects gated pages depends on where you reveal content:

- **Client-side gating (the default).** When you ship the same teaser HTML to everyone and let the widget reveal subscriber-only content per session, the page HTML is identical for every visitor — so caching it on a CDN is safe. Access is resolved in the browser after the widget loads, not baked into the cached HTML.
- **Server-side or hybrid gating.** If your server injects the protected body based on the visitor's session, be careful with caches:
  - Do not cache the subscriber version and serve it to anonymous visitors.
  - Do not let an anonymous version overwrite the subscriber version for signed-in visitors.
  - Keep any response that returns the protected body private to the visitor session (for example, `Cache-Control: private`).

## Validate Before Launch

Before you launch a gated article:

1. Test a live URL with Google's [Rich Results Test](https://search.google.com/test/rich-results).
2. Confirm the rendered HTML includes the JSON-LD.
3. Confirm `cssSelector` points to the gated content container.
4. Use URL Inspection in Google Search Console after the page is live.

Validation does not guarantee ranking or rich-result display, but it helps catch markup mistakes before they affect search visibility.

