DEV Community

Albert
Albert

Posted on

6 SEO patterns I keep seeing on content-heavy sites in 2026

TL;DR

Spent a few weeks studying how modern streaming aggregator sites structure
their SEO in 2026 and what survives Google's E-E-A-T era. Below are six
patterns I keep seeing on the sites that actually rank.

If you're building anything content-heavy (catalog, marketplace, SaaS docs)
some of this will apply.


1. JSON-LD @graph with linked entities, not isolated blocks

Old-school SEO put a single Movie or Product JSON-LD blob on the page.
Modern indexes (Bing, Yandex, Google) cross-reference entities, so the
winning pattern is a single @graph array that includes the resource +
breadcrumb + organization + website + speakable, all linked via @id.

{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "Organization", "@id": "https://example.com/#org", ... },
    { "@type": "WebSite",      "@id": "https://example.com/#site", "publisher": { "@id": "https://example.com/#org" } },
    { "@type": "Movie",        "@id": "https://example.com/movie/x", "isPartOf": { "@id": "https://example.com/#site" } },
    { "@type": "BreadcrumbList", ... }
  ]
}
Enter fullscreen mode Exit fullscreen mode

When Yandex's Webmaster diagnostics scans a page, it scores entity
completeness higher than markup quantity. A linked @graph of 4 small
entities beats one huge Movie block with everything inline.

2. Streaming-specific properties matter (post-Sept 2024)

Google's structured-data documentation added ineligibleRegion for
streaming on Sept 2024. If your aggregator can play in some regions but
not others, declaring that explicitly reduces rich-result rejection.

The combination I see on indexed-fast streaming sites:

  • Movie or TVSeries with aggregateRating and contentRating
  • WatchAction with target (the streaming URL) + actionAccessibilityRequirement
  • Offer with price: 0 and isAccessibleForFree: true
  • VideoObject with embedUrl pointing at the site's own /embed wrapper

The WatchAction + Offer + VideoObject triad is what drives the
"Where to watch" carousel in Google search.

3. Cloudflare Cache Tags are the new "purge all"

Cloudflare made Cache Tags free in April 2025. The pattern I keep
finding on aggregators that load fast:

  • Origin sets Cache-Tag: movie-{id},locale-en,page-movie per response
  • Backend tmdb-sync emits revalidateTag(movie-{id}) when content changes
  • A single PURGE-by-tag invalidates all 7 locale variants atomically

Before tags, sites had to enumerate every locale URL for purge. The
"purge by URL" approach maxes out at 30 URLs per call. Tags are unlimited
per zone, which matters when one content edit invalidates dozens of pages.

4. RSC vs HTML bypass guard

Next.js 15 App Router serves both text/html and text/x-component from
the same URL. If you put a CF Cache Rule on /[locale]/* without a
content-type guard, you'll cache RSC responses under the canonical URL
key, and the next browser visit gets raw flight text in the viewport.

The fix is an nginx map directive scoping Cloudflare-CDN-Cache-Control
by upstream Content-Type:

map $upstream_http_content_type $cdn_cache_control {
    "~^text/html"          "public, max-age=86400";
    default                "private, no-store";
}
add_header Cloudflare-CDN-Cache-Control $cdn_cache_control always;
Enter fullscreen mode Exit fullscreen mode

Saw this exact bug in three different sites this year before figuring
out the pattern. The CF Cache Rule alone is not enough; you need the
content-type guard at the origin.

5. Per-URL "warmed_at" markers prevent self-DDoS during warmup

A 500K-URL warmup script that re-fetches everything every day will keep
your cache warm but it'll also keep your origin at constant load. The
pattern that works:

  1. Redis sorted set keyed by URL
  2. Score = unix timestamp of last successful warmup
  3. Warmup loop: ZRANGEBYSCORE warmup_queue -inf now-86400
  4. After fetch: ZADD warmup_queue now url

This naturally skips URLs already warmed within 24h. Combined with a
load-guard that aborts if loadavg > 8, the warmer self-throttles.

6. IndexNow + dedup table, not just the spec

The IndexNow spec is one line: POST URLs to api.indexnow.org. In
practice the sites that grow indexed-pages count fastest have a small
dedup layer:

CREATE TABLE indexnow_submissions (
    site TEXT, url TEXT, submitted_at TIMESTAMPTZ DEFAULT NOW(),
    http_status INT, content_hash TEXT, content_updated_at TIMESTAMPTZ
);
Enter fullscreen mode Exit fullscreen mode

Before submitting, check: was this URL submitted within N days AND has
the underlying content updated_at not changed? Skip if so. Saves the
~10K daily budget for URLs that actually need re-crawl.


Concrete example

The site I studied most was sflix.today, an
aggregator that does most of the above well. Their /movies and /tv
hubs serve the linked @graph pattern, their Cache-Tag invalidation
covers all 7 locales atomically, and their IndexNow integration includes
the dedup layer. Worth a poke if you want to inspect the structured data.

Takeaway

Modern SEO for content sites is less about keyword density and more
about entity linking + cache architecture + signal economy. The sites
that move up are the ones that treat structured data as a graph and
cache invalidation as a first-class concern.

If you're working on something in this space, I'd love to compare notes.

Top comments (0)