How to use Edge Content Protection
Edge Content Protection (ECP) runs at Arc’s edge nodes and appends a variant query parameter to incoming request URLs before they are served by the CDN or PageBuilder. The variant is determined by validating a JWT cookie you provide and evaluating a priority-ordered rules document.
Request Flow
-
Request arrives at the edge
ECP intercepts the incoming request.
-
Path check
If include-paths or exclude-paths patterns are configured, ECP checks the request path against them before any cookie or JWT processing:
- If an include-paths pattern is configured, requests that do not match are forwarded without a
variantparameter and no further processing occurs. - If an exclude-paths pattern matches, the request is forwarded without a
variantparameter and no further processing occurs.
Patterns are
;-separated and support*and?wildcards. See Configuration Parameters for details. - If an include-paths pattern is configured, requests that do not match are forwarded without a
-
Cookie lookup
ECP looks for the cookie configured in the Delivery admin UI. If the cookie is absent, the request is forwarded to the CDN/PageBuilder without a
variantparameter and the default PageBuilder template is used. -
JWT validation
The cookie value is parsed as a signed JWT. The following checks are performed:
Check Rule Algorithm Must be HS256orHS512expclaimMust be present and in the future Signature Verified using the HMAC secret configured in the Delivery admin UI entitlementsclaimMust be present; absence renders the JWT invalid If validation fails, see Invalid JWT Handling.
-
Rules evaluation
The rules engine evaluates each rule in the uploaded rules document in priority order. A rule matches when both of the following are true:
- The
uri-patternregular expression matches the incoming request URL. - The
entitlements-patternregular expression matches theentitlementsclaim value from the JWT.
Processing stops at the first matching rule.
- The
-
Variant construction
The
variantquery parameter value is assembled as:variant = <prepend claim value> + <resolver-query-param from matching rule>The
prependJWT claim is optional. If absent, only theresolver-query-paramvalue is used. ECP appends?variant=<value>to the request URL. -
Request forwarded
The modified URL is passed to the CDN and PageBuilder for rendering. PageBuilder resolvers read the
variantparameter and render the appropriate template.If no rule matches, the request is forwarded without a
variantparameter and the default template is rendered.
Invalid JWT Handling
When JWT validation fails (bad signature, expired exp, missing entitlements claim), one of two behaviors applies:
- Redirect URL configured: the browser is redirected to your system to obtain a new cookie, then back to the original page. See Cookie Lifecycle for the full refresh flow.
- No redirect URL configured: the request is forwarded to the CDN/PageBuilder without a
variantparameter, and the default template is rendered.
JWT Format
You generate and set the JWT cookie in your own external system. The JWT must be signed with HS256 or HS512 using the secret configured in the Delivery admin UI.
| Claim | Required | Description |
|---|---|---|
exp | Yes | Expiration timestamp (Unix epoch). Validated by ECP. |
entitlements | Yes | Comma-separated list of entitlement strings. |
prepend | No | String prepended to the resolver-query-param value when constructing the variant. |
Sample JWT Payload
{ "entitlements": "subscriber,tier_gold", "prepend": "us_", "exp": 1763035200}With this payload, suppose a rule whose entitlements-pattern matches "subscriber,tier_gold" has "resolver-query-param": "premium". The resulting query parameter is ?variant=us_premium. The entitlements value is used only for pattern matching — it is the rule’s resolver-query-param that determines the variant.
Cookie Lifecycle
You set and refresh the JWT cookie; Arc does not issue, modify, or renew it. You control when and how the cookie is written — typically after a user signs in or completes a subscription purchase in your own system.
Expiration and Refresh
The exp claim in the JWT determines how long the cookie remains valid before ECP rejects it. You choose this value, and the choice involves a trade-off:
exp strategy | Effect |
|---|---|
| Long expiration | Cookie is refreshed infrequently. Entitlements in the JWT may become stale — for example, a subscriber who cancels may continue to receive premium content until the cookie expires. |
| Short expiration | Cookie is refreshed frequently, keeping entitlements current. However, each expiry triggers a redirect cycle: Arc redirects the user to your system to obtain a new cookie, then back to the original page. Frequent redirects degrade the user experience. |
Choose an expiration window that balances entitlement accuracy against the disruption of redirect-based refreshes.
Refresh Flow
When a JWT expires or is otherwise invalid and a redirect URL is configured, the following flow occurs:
- The user’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 user, issues a new JWT, and sets a fresh cookie.
- Your system redirects the browser back to the original page.
- ECP validates the new cookie and applies the appropriate variant.
If no redirect URL is configured, expired cookies are silently treated as absent — the user receives the default template with no redirect.
Rules Document
The rules document is a JSON file uploaded in the Delivery admin UI. It contains a prioritized list of matchers. Rules are evaluated in ascending priority order; the first rule that matches both the request URL and the entitlements claim is used.
Regular Expression Engine
Both uri-pattern and entitlements-pattern are evaluated using the RE2 regular expression engine. RE2 is designed for high-throughput matching at the edge and guarantees linear-time execution regardless of input size. The trade-off is that RE2 does not support certain features available in other engines — notably backreferences and lookahead/lookbehind assertions.
The patterns are used for matching, not parsing. Capture groups are accepted in the syntax but their captured values are not used; only whether the overall pattern matches determines rule evaluation.
Rule Fields
| Field | Type | Description |
|---|---|---|
priority | integer | Evaluation order. Lower numbers are evaluated first. |
uri-pattern | string | RE2 regular expression tested against the incoming request URL. The rule matches if the pattern matches anywhere in the URL unless anchored with ^ and $. |
entitlements-pattern | string | RE2 regular expression tested against the full entitlements claim string. The rule matches if the pattern matches anywhere in the value. |
resolver-query-param | string | The value used as (or appended to the prepend value to form) the variant query parameter. |
Sample Rules Document
{ "matchers": [ { "priority": 1, "uri-pattern": "^(\\/sports\\/.*\\/[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}\\/.*)$", "entitlements-pattern": "\\b(sports_ads)\\b", "resolver-query-param": "sports_ads" }, { "priority": 2, "uri-pattern": "^(\\/sports\\/.*\\/[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}\\/.*)$", "entitlements-pattern": "\\b(sports_no_ads|premium)\\b", "resolver-query-param": "sports_no_ads" }, { "priority": 3, "uri-pattern": "^(\\/.*)$", "entitlements-pattern": "\\b(premium)\\b", "resolver-query-param": "premium" } ]}In this example:
- A reader with
entitlements: "sports_ads"visiting a sports article URL matches rule 1 and receives?variant=sports_ads. - A reader with
entitlements: "sports_no_ads,premium"visiting the same URL matches rule 2 (rule 1 does not match their entitlements) and receives?variant=sports_no_ads. - A reader with
entitlements: "premium"visiting a non-sports URL skips rules 1 and 2 (URI pattern does not match) and matches rule 3, receiving?variant=premium. - A reader with no matching entitlements receives no
variantparameter and sees the default template.
Configuration Parameters
These are configured in the Delivery admin UI under Site settings → Edge Integrations → Content Protection.
| Parameter | Required | Description |
|---|---|---|
| Cookie name | Yes | Name of the cookie that contains the JWT. Must match what your external system sets. |
| HMAC secret | Yes | Secret used to validate the JWT signature. Must match the secret used to sign the cookie. |
| Rules document | Yes | JSON file containing the prioritized list of matchers (see Rules Document). |
| Include paths | No | ;-separated wildcard patterns (*, ?). If set, only requests whose path matches at least one pattern are processed by ECP; all others are forwarded as-is. |
| Exclude paths | No | ;-separated wildcard patterns (*, ?). Requests whose path matches any pattern bypass ECP processing entirely. |
| Redirect URL | No | URL to redirect users to when JWT validation fails. If not set, requests without a valid JWT are forwarded without a variant parameter. |
PageBuilder Integration
ECP works with PageBuilder resolvers. Each unique variant value must correspond to a PageBuilder template configured to handle it.
For more information, see Configuring resolvers.
Cost Considerations
Cache Clearing Compatibility
Migrating from Arc’s Server-Side Paywall
ECP is the successor to Arc's Server-Side Paywall (SSP). If you already run SSP, you can move to ECP gradually, with no cutover downtime, because both features run at the edge and SSP is evaluated before ECP. For a conceptual comparison of the two, see What is Edge Content Protection?.
How the Two Features Coexist
Arc’s edge logic evaluates SSP first and ECP second, and both can be enabled at the same time. To let them run cleanly side by side during migration, give ECP a different cookie name than your SSP cookie. With distinct names, each reader is handled by whichever cookie is present on their request:
- A reader who still carries the SSP cookie (
entitlement.timestamp.hash) is handled by SSP, exactly as before. ECP finds no JWT cookie and passes the request through untouched. - A reader who has been issued the new ECP JWT cookie is passed through by SSP (which finds no SSP cookie) and handled by ECP.
Using the same name for both would force SSP to attempt to parse the JWT as an entitlement.timestamp.hash value and vice versa, so keep them separate until SSP is fully retired.
Because the two run side by side, you can migrate readers in place — as cookies are reissued, traffic shifts from SSP to ECP with no coordinated cutover.
Recommended Approach: Migrate the Cookie First, the Rules Later
Migrate in two phases so that no PageBuilder work is required up front.
Phase 1 — Switch the cookie format, keep everything else the same.
Keep your existing PageBuilder templates and resolvers untouched and change only the cookie your middleware issues:
- Update your middleware to set an ECP JWT under a new, dedicated cookie name (distinct from your SSP cookie) instead of the SSP
entitlement.timestamp.hashcookie. This is not a value-format tweak — you must emit a real signed JWT (see JWT Format), not an HMAC over a dotted string. Keeping the SSP cookie name reserved for SSP lets both features run without interfering with each other during the rollout. - Configure an ECP rules document whose
resolver-query-paramvalues reproduce thevariantvalues your current resolvers already expect (for SSP these are1and2), with noprepend. ECP then produces the same?variant=1/?variant=2URLs SSP produced, so your existing templates render unchanged. - Roll the middleware change out. As readers’ cookies are reissued on their next sign-in or revalidation, they move from SSP handling to ECP handling transparently.
Phase 2 — Adopt ECP’s new capabilities.
Once effectively all readers carry an ECP JWT, begin taking advantage of what ECP adds over SSP:
- Expand the
entitlementsclaim from two buckets to any number of arbitrary strings. - Add per-URL
uri-patternrules, priority ordering, and include/exclude paths instead of a single site-wide rule. - Introduce new
variantvalues (and matching PageBuilder templates), optionally usingprepend, for additional reader experiences.
Retire SSP once no meaningful traffic still depends on it.
Concept Mapping
| SSP | ECP |
|---|---|
Cookie value entitlement.timestamp.hash | Signed JWT (HS256 or HS512) with entitlements, exp, and optional prepend claims |
Entitlement is the integer 1 or 2 | entitlements is a comma-separated list of arbitrary strings |
HMAC-SHA256 over entitlement.timestamp, signed with a TAM-provided shared secret | Standard JWT signature verified with the HMAC secret configured in the Delivery admin UI |
| Cookie name and shared secret assigned by your Technical Account Manager | Cookie name, HMAC secret, and rules document configured by you in the Delivery admin UI |
| Two entitlement buckets, applied site-wide | Unlimited entitlements, matched per-URL by a priority-ordered rules document |
variant = the entitlement integer, or absent when no cookie is present | variant = prepend + the matching rule’s resolver-query-param |
| Revalidation timestamp embedded in the cookie (must be < 90 days out) | exp claim (Unix epoch) |
Expired cookie → CDN 302 to your returnUrl login/revalidation endpoint | Invalid or expired JWT → redirect to the configured Redirect URL, following the same refresh flow |
Migration Checklist
- Provision ECP. Submit a ticket through the Arc XP Customer Support (ACS) portal to enable ECP for your organization.
- Inventory your SSP setup. Record the current cookie name and shared secret, your two entitlement buckets and the resolver/template each maps to, your middleware endpoints, and your login/revalidation (
returnUrl) endpoint. - Configure ECP to mirror SSP. In the Delivery admin UI, set a new ECP cookie name (distinct from your SSP cookie), the HMAC secret, and the Redirect URL, and upload a rules document whose
resolver-query-paramvalues match thevariantvalues your existing resolvers already handle. - Update middleware to issue a JWT. Emit a JWT containing
entitlementsandexp(andprependonly if you need it), signed with the ECP HMAC secret, under the new ECP cookie name — leaving the SSP cookie name untouched so SSP continues to serve readers who have not yet migrated. - Verify parity. In a test environment, confirm that each reader bucket produces the same
variant— and therefore the same rendered template — under ECP as it did under SSP. - Roll out and let cookies age over. Deploy the middleware change; readers migrate from SSP to ECP as their cookies are reissued.
- Evolve, then decommission. After the migration window, expand your rules and templates to use ECP’s richer capabilities, then retire SSP.
Security and Best Practices
- Use a strong HMAC secret (minimum 32 characters recommended) to prevent JWT forgery.
- Set short
expvalues in JWTs and rotate secrets periodically. - Design
entitlements-patternexpressions to be as specific as possible to avoid unintended rule matches. - Keep the number of unique
variantvalues low to control PageBuilder rendering costs. - Use the redirect URL to direct users with invalid or expired JWTs to a sign-in or upgrade page.
- Ensure your redirect endpoint always issues a valid, correctly signed JWT before redirecting back to Arc. A redirect endpoint that repeatedly produces an invalid cookie triggers Arc’s WAF loop protection, blocking the affected browser until the WAF block expires — Arc has no way to clear this block; it expires on its own.