Edge Content Protection tutorial: hard paywall
This tutorial walks you through the simplest useful Edge Content Protection (ECP) configuration: a binary hard paywall. Subscribers receive the full article; everyone else sees a subscribe page with no article content. There are only two outcomes, one rule, and no prepend claim.
Scenario
Every article on your site is subscriber-only. Readers fall into one of two states:
| State | JWT cookie | Entitlement | Access |
|---|---|---|---|
| Subscriber | Present and valid | subscriber | Full article |
| Everyone else | Absent or invalid | — | Hard paywall |
“Everyone else” covers anonymous visitors, registered-but-not-subscribed users, and readers whose JWT has expired — all receive the same paywall template.
Step 1: Design the JWT
Your external system issues a signed JWT cookie after a reader completes a subscription purchase or signs in with an active subscription.
For this scenario you only need two claims:
| Claim | Value |
|---|---|
entitlements | "subscriber" |
exp | Expiration timestamp (Unix epoch) |
No prepend claim is needed. The variant value is taken directly from the rule’s resolver-query-param.
Sample JWT payload
{ "entitlements": "subscriber", "exp": 1763035200}Readers without an active subscription receive no cookie. ECP skips rules evaluation entirely and forwards the request without a variant parameter.
Step 2: Build the rules document
A single rule is sufficient for this scenario.
{ "matchers": [ { "priority": 1, "uri-pattern": ".*", "entitlements-pattern": "subscriber", "resolver-query-param": "full" } ]}- If ECP finds a valid JWT with
entitlementsmatching"subscriber", the rule fires and appends?variant=fullto the request URL. - If there is no cookie, or the JWT is invalid or expired, no rule matches and the request is forwarded without a
variantparameter.
Step 3: Configure the PageBuilder resolver
A single resolver handles all article URLs. It reads the variant query parameter and selects the appropriate template.
For more information on resolvers, see Configuring resolvers.
variant | Template | Notes |
|---|---|---|
full | full-article | Complete article body |
| (absent) | hard-paywall | Subscribe page; no article content rendered |
Because ECP’s default behavior (no variant) produces the paywall, hard-paywall is the resolver’s default template — the one rendered when no variant parameter is present.
Step 4: Build your templates
Two templates are required.
full-article
Renders the complete article body. This template is used only for readers whose JWT contains the subscriber entitlement.
hard-paywall
Renders no article content. The page presents a subscription offer — typically a headline, a brief description of the benefit, and links to available subscription plans. This is the default template, so it is rendered for all non-subscribers regardless of whether they have an account.
End-to-end matrix
| Reader state | variant | Template |
|---|---|---|
| Subscriber (valid JWT) | full | full-article |
| Anonymous or non-subscriber | (absent) | hard-paywall |
| Expired or invalid JWT (no redirect URL) | (absent) | hard-paywall |
| Expired or invalid JWT (redirect URL configured) | — | Redirect to your auth system |
JWT expiry and the redirect flow
When a subscriber’s JWT expires and a redirect URL is configured:
- The reader’s request arrives at the edge with an expired cookie.
- ECP rejects the JWT and redirects the browser to your configured redirect URL.
- Your system authenticates the reader, issues a fresh JWT with
entitlements: "subscriber", and sets a new cookie. - Your system redirects the browser back to the original article URL.
- ECP validates the new cookie, the rule fires, and
?variant=fullis appended.
Without a redirect URL configured, expired JWTs are silently treated as absent — the reader sees the hard-paywall template until they sign in again manually.