Page Protect

Page Protect is a Cloudflare Page Shield-style client-side security feature. It monitors, inventories, and enforces client-side resources (scripts, connections) loaded by website visitors via Content-Security-Policy (CSP) headers, with log-based violation report collection, a CSP policy builder, script inventory with code-change detection, an inventory beacon, beacon trust, and a policy recommender — all scoped per-backend and integrated into the existing HAProxy config generation pipeline.

How It Works

  Browser loads page

       │  1. HAProxy serves HTML response
       │     - Injects <script src="/_cx-assets.js"> (if beacon enabled)
       │     - Sets Server-Timing: cxid header (if beacon trust enabled)
       │     - Sets Content-Security-Policy header (per backend policy)


  Browser loads resources (scripts, styles, images, XHR)

       │  2. CSP violation? → POST to /_csp-report
       │     3. Beacon JS collects Resource Timing entries
       │        + reads cxid from Server-Timing
       │        → POST to /_cx-assets


  HAProxy captures both in JSON log-format (txn.csp_report, txn.asset_beacon)


  Page Protect Sampler (background thread, every 10s)
       │  - Reads HAProxy container logs via Docker SDK
       │  - Parses CSP reports → stores in csp_reports table
       │  - Parses beacon POSTs → upserts script inventory (source="beacon")
       │  - Prunes old reports (report_retention_days)


  Page Protect Hasher (background thread, every 60s)
       │  - Fetches due script URLs via httpx (HTTP/2)
       │  - Computes SHA-256, compares to last-known hash
       │  - Flags hash_changed on mismatch
       │  - Bypasses WAF/rate-limit/security rules via X-CoreX-Internal token


  Dashboard / Inventory / Reports tabs in the UI

Four Pillars

  1. CSP Report Collection — HAProxy captures CSP violation report POST bodies in its JSON log-format (txn.csp_report var); a background sampler reads HAProxy container logs via Docker SDK, extracts reports, and stores them in the database.
  2. CSP Policy Builder — A dedicated PageProtectPolicy model (scoped per-backend via backend_ids) generates CSP headers (Content-Security-Policy for enforce, Content-Security-Policy-Report-Only for monitor) emitted in HAProxy backend sections. Separate from the generic Response Headers feature.
  3. Script/Connection Inventory — The sampler parses CSP reports and beacon POSTs to build/maintain an inventory of detected scripts, connections, and other resources with first-seen/last-seen tracking, domain attribution, and source tracking (csp, manual, or beacon).
  4. Code Change Detection — A background hasher periodically fetches detected script URLs, computes SHA-256 hashes, and flags changes (supply-chain attack detection).

UI Tabs

The Page Protect page (Protection > Page Protect) has 6 tabs:

TabDescription
DashboardLive stats: total scripts, violations (24h), changed scripts, active policies, top violated directives, top blocked URIs
PoliciesCSP policy builder with directive editor, live header preview, auto-suggest from inventory, and policy recommender
InventoryScript/connection inventory with sorting, filtering, manual add, hash check, reset, ignore, view content, delete
ReportsCSP violation reports with filtering, expandable rows, CSV export, clear all
BaselineBaseline window for policy recommendations — start/stop a collection window, then recommend a policy
SettingsMonitoring, change detection, report retention, beacon injection, beacon trust, manual report sampling

Dashboard Tab

The dashboard shows 4 stat cards and 2 top-lists:

CardDescription
Total ScriptsCount of entries in the script inventory
Violations (24h)CSP reports captured in the last 24 hours
Changed ScriptsScripts where the hash changed since last check (red if > 0)
Active PoliciesEnabled CSP policies
  • Top Violated Directives — most frequently violated CSP directives
  • Top Blocked URIs — most frequently blocked resource URIs

Auto-refreshes every 15 seconds.

Policies Tab

CSP policies are scoped per-backend (via backend_ids; empty = all backends). Each policy generates a CSP header emitted in the HAProxy backend section.

Policy Fields

FieldDescription
NameUnique policy name
Modemonitor (Report-Only — collect violations without blocking) or enforce (block violations)
Sample Rate (%)Percentage of responses that get the CSP header (1–100; uses rand(100) lt <rate> for sampling)
Report PathURL path browsers POST violation reports to (default /_csp-report; must match report-uri in the policy)
BackendsWhich backends this policy applies to (none = all)
EnabledToggle to enable/disable the policy
DirectivesCSP directive → source list mapping (see below)

CSP Directives

DirectiveControls
default-srcFallback for all resource types
script-srcJavaScript sources
style-srcStylesheet sources
img-srcImage sources
font-srcFont sources
frame-srcIframe sources
connect-srcXHR, WebSocket, fetch destinations
media-srcAudio/video sources
object-srcPlugin/embed sources
worker-srcWorker sources
frame-ancestorsWho can embed this page (clickjacking protection)
base-uriAllowed <base> tag URIs
form-actionForm submission destinations
report-uriViolation report endpoint
upgrade-insecure-requestsForce HTTPS for all resources

Each directive accepts:

  • 'self' — same origin
  • 'none' — no sources
  • 'unsafe-inline', 'unsafe-eval', 'strict-dynamic' — special keywords
  • Specific URLs (e.g. https://cdn.example.com)
  • Nonces (per-request generated)
  • Hashes

Directive Editor

The policy form includes a visual directive editor:

  • Add/remove directives from a dropdown of all CSP directives
  • Add sources to each directive via text input (press Enter or click Add)
  • Remove individual sources with the × button
  • Live CSP header preview at the bottom of the form

Auto-Suggest from Inventory

When the script inventory has detected scripts or connections, the policy form shows clickable suggestions:

  • Detected scripts (blue chips) — click to add to script-src
  • Detected connections (purple chips) — click to add to connect-src

Policy Recommender

The Recommend Policy button analyzes the script inventory and CSP violation reports to generate a recommended CSP policy:

  1. Click Recommend Policy (optionally filter by backend first)
  2. A modal shows:
    • Summary: scripts analyzed, reports analyzed, baseline window, backend filter
    • Warnings (e.g. origins seen from < 2 distinct IPs — may be attacker probes)
    • Recommended directives (preview)
    • Per-origin details: origin, occurrence count, distinct IPs (amber if < 2)
  3. Click Apply to Form to populate the policy form with the recommendations
  4. Edit further before saving

The recommender uses the baseline window if one is set (see Baseline Tab).

CSP header precedence

If you have manually added a Content-Security-Policy header via the Response Headers feature, the Page Protect CSP header (emitted in the backend section) takes precedence because it runs after the frontend’s response headers (HAProxy set-header = last one wins).

Inventory Tab

The inventory tracks all client-side resources detected via CSP reports, beacon POSTs, or manual entry.

Inventory Fields

FieldDescription
URLResource URL
Typescript, connect, img, style, font, frame, object, other
DomainExtracted hostname from the URL
SourceHow the entry was detected: CSP (from violation report), Beacon (from beacon POST), or Manual (manually added)
MethodFetch method for hashing: Auto (probe — HEAD then GET), GET, or POST
OccurrencesHow many times this resource was seen in reports/beacons
Last SeenLast time this resource appeared in a report or beacon
Last CheckedLast time the hasher fetched and hashed this resource
Hash StatusOK (hash matches), Changed (hash differs — possible supply-chain attack), Error (fetch failed), Unchecked (not yet hashed), Ignored

Sorting and Filtering

  • Sort — click any column header to sort ascending/descending (URL, type, domain, source, method, occurrences, last seen, last checked, hash status)
  • Filter by type — dropdown to show only a specific resource type
  • Filter by status — All, Changed, Unchanged, Ignored

Inventory Actions

ActionDescription
Add AssetManually add a URL to the inventory (source = Manual). Useful when CSP is in enforce mode (no violation reports) or to proactively monitor a critical asset. Requires http:// or https:// URL.
Check AllForce a hash check on all non-ignored assets immediately (overrides the interval)
Check (per-row)Force a hash check on a single asset
Reset HashReset the hash baseline for an asset — the next check establishes a fresh baseline (useful after a legitimate update)
View ContentOpen the fetched script content in a new window (if available)
Ignore / Stop IgnoringToggle ignored state — ignored assets are not hashed but remain in the inventory
DeleteRemove the asset from the inventory
Scripts are never auto-pruned

Inventory entries are checked indefinitely until manually deleted. The auto_prune_stale_days setting (in Settings) removes assets not seen in traffic or successfully hashed within the configured window, but changed assets are always preserved.

Reports Tab

The Reports tab shows captured CSP violation reports.

Report Fields

FieldDescription
TimeWhen the report was captured
Client IPIP of the browser that sent the report
Document URIThe page where the violation occurred
DirectiveThe CSP directive that was violated
Blocked URIThe resource that was blocked
HostnameExtracted hostname from the document URI

Click any row to expand and see the full report JSON (referrer, effective directive, original policy, source file, line/column number, status code, script sample, backend name, listener name, report type).

Filtering and Export

  • Filter by violated directive — text input
  • Filter by hostname — text input
  • Export CSV — download all reports (up to 10,000) as CSV
  • Clear All — delete all reports (requires write permission)

Baseline Tab

The baseline window defines a collection period for policy recommendations. During a baseline window, the recommender only considers scripts and reports captured within the window.

Baseline States

StatusDescription
IdleNo baseline window set
BaseliningCollection in progress (shows elapsed time, live counts)
CompleteWindow finished (shows duration, final counts)

Baseline Fields

FieldDescription
NoteOptional label for the baseline window
WindowStart → end timestamps (end is “in progress” while baselining)
ScriptsCount of scripts detected during the window
ReportsCount of CSP reports captured during the window
Distinct IPsCount of distinct client IPs that sent reports
Pages ObservedCount of distinct pages (document URIs) in reports

Baseline Workflow

  1. Click Start Baseline (optionally add a note)
  2. Crawl your site (or have users browse all pages) to ensure complete resource coverage
  3. Counts update every 5 seconds
  4. Click Stop Baseline when done
  5. Go to Policies > Recommend Policy — the recommender uses the baseline window
  6. Click Clear to discard the window and start fresh

Settings Tab

Monitoring

SettingDefaultDescription
Monitoring EnabledfalseEnables CSP report capture in coreX and the background sampler. After enabling, apply the coreX config to start capturing.
Report Path/_csp-reportThe URL path browsers POST CSP violation reports to. Must match the report-uri in your CSP policies.
Report Retention (days)7How long to keep CSP reports before pruning.

Code Change Detection

SettingDefaultDescription
Code Change DetectionfalsePeriodically fetches detected scripts and hashes their content to detect supply-chain changes.
Change Detection Interval (hours)24How often to re-hash scripts.
Auto-Prune Stale (days)0Assets not seen in traffic or successfully hashed within this many days are automatically removed. 0 = disabled. Changed assets are preserved.

Inventory Beacon

The inventory beacon injects a <script src="/_cx-assets.js"> tag into HTML responses via the resp_transform Rust filter. The beacon JS uses the Resource Timing API to collect all loaded resources and POSTs them to /_cx-assets. The sampler parses these POSTs and upserts resources with source="beacon".

Requires Response Transformations

The inventory beacon and beacon trust both require Response Transformations to be enabled in System > Settings > Advanced. When beacon is enabled, resp_transform_enabled is forced True so the Rust module loads. The UI disables these controls and shows a warning when Response Transformations is off.

SettingDefaultDescription
Inventory BeaconfalseInject the beacon JS into HTML responses to collect resource timing data
Beacon Endpoint Path/_cx-assetsURL path the beacon JS POSTs resource data to
Beacon Script Path/_cx-assets.jsURL path where the beacon JS file is served
Beacon Content Typestext/htmlComma-separated content types to inject the beacon into
Beacon Path Patterns(empty)Comma-separated path prefixes to match (empty = all paths)
Beacon Backends(empty)Which backends to inject the beacon into (empty = all)

When beacon is enabled, CSP headers automatically allowlist the beacon script path in script-src (or 'self' is added to default-src).

Beacon Trust

Beacon Trust is an anti-spoofing mechanism that proves a client IP received a real HTML response from coreX. It works alongside the inventory beacon — the beacon JS reads a per-request cxid from the Server-Timing response header and submits it with the beacon POST, and HAProxy validates it before marking the source IP as trusted. Trusted IPs stay trusted for BEACON_TRUST_TTL_SECONDS (default 900s / 15 minutes) with a sliding-window refresh on every subsequent request.

Trusted IPs can be referenced in security rules via the ip.beacon_trusted field (see Security Rules).

SettingDefaultDescription
Beacon TrustfalseEnable the beacon trust anti-spoofing mechanism
Beacon Trust stick-tables

Beacon Trust uses two stick-tables: cxid_table (string-keyed, 1m slots, 120s expiry — tracks recent cxids) and beacon_trust_table (ip-keyed, 100k slots, 900s sliding-window expiry — tracks trusted IPs). These are always emitted so security rules referencing ip.beacon_trusted don’t break; they’re simply empty when beacon trust is disabled. HAProxy’s tune.stick-counters is set to at least 6 to accommodate the sc4/sc5 tracking.

Manual Report Collection

The Sample Reports Now button triggers an immediate CSP report sample from coreX logs (instead of waiting for the next sampler interval). Reports the number of new reports stored.

Environment Variables

VariableDefaultDescription
PAGE_PROTECT_SAMPLER_INTERVAL_SECONDS10How often the sampler polls HAProxy logs
PAGE_PROTECT_REPORT_BODY_MAX_BYTESMax CSP report body size to capture
PAGE_PROTECT_HASH_TIMEOUT_SECONDSTimeout for script hash fetches
PAGE_PROTECT_HASH_USER_AGENTcoreX-Manager-PageProtect/2.0UA used by the hasher (also part of the bypass ACL)
PAGE_PROTECT_HASHER_BYPASS_TOKEN(random)Secret token for hasher bypass (pin via env for stability)
PAGE_PROTECT_HASHER_BYPASS_HEADERX-CoreX-InternalHeader name for the bypass token
PAGE_PROTECT_DEFAULT_REPORT_PATH/_csp-reportDefault CSP report endpoint path
PAGE_PROTECT_BEACON_JS_PATH/etc/haproxy/page-protect-beacon.jsPath to the beacon JS file in the HAProxy container
BEACON_TRUST_TTL_SECONDS900How long a trusted IP stays trusted (sliding window)
BEACON_CXID_TTL_SECONDS120How long a cxid stays in the table

Step-by-Step: Configure CSP for a Listener

  1. Navigate to Protection > Page Protect > Settings
  2. Enable Monitoring Enabled
  3. Set Report Path to /_csp-report (or your preferred path)
  4. Save and Apply Changes — HAProxy now captures CSP violation reports
  5. Navigate to Policies
  6. Click Add Policy
  7. Name: my-csp-policy, Mode: monitor (start with Report-Only)
  8. Select the backend(s) it applies to (or leave empty for all)
  9. Set default-src to 'self'
  10. Set script-src to 'self' plus any allowed CDN URLs (use auto-suggest from inventory if available)
  11. Set style-src to 'self' plus any allowed CDN URLs
  12. Set img-src to 'self' data: (if using data URIs)
  13. Set frame-ancestors to 'none' (clickjacking protection)
  14. Review the CSP header preview
  15. Save and Apply Changes

Step-by-Step: Use the Policy Recommender

  1. Navigate to Protection > Page Protect > Baseline
  2. Click Start Baseline (optionally add a note)
  3. Crawl your site or have users browse all pages
  4. Watch the counts update (scripts, reports, distinct IPs, pages)
  5. Click Stop Baseline when done
  6. Navigate to Policies
  7. Click Add Policy
  8. Click Recommend Policy
  9. Review the recommended directives, warnings, and per-origin details
  10. Click Apply to Form
  11. Edit the policy as needed (add/remove directives or sources)
  12. Save and Apply Changes

Step-by-Step: Enable the Inventory Beacon

  1. Navigate to System > Settings > Advanced
  2. Enable Response Transformations and Apply Changes
  3. Navigate to Protection > Page Protect > Settings
  4. Enable Inventory Beacon
  5. Configure beacon settings (endpoint path, script path, content types, path patterns, backends)
  6. Save and Apply Changes
  7. The beacon JS is now injected into HTML responses
  8. Browse your site — resources appear in the Inventory tab with source = Beacon

Step-by-Step: Enable Beacon Trust

  1. Ensure Response Transformations is enabled (see above)
  2. Ensure Inventory Beacon is enabled (beacon trust requires the beacon JS to read the cxid)
  3. Navigate to Protection > Page Protect > Settings
  4. Enable Beacon Trust
  5. Save and Apply Changes
  6. HAProxy now inserts cxid into the Server-Timing header on HTML responses
  7. The beacon JS reads it and submits it with the beacon POST
  8. Validated IPs are tracked in beacon_trust_table for 15 minutes
  9. Use the ip.beacon_trusted field in security rules to gate access on trusted IPs

Step-by-Step: Detect Script Changes (Supply-Chain Monitoring)

  1. Navigate to Protection > Page Protect > Settings
  2. Enable Code Change Detection
  3. Set Change Detection Interval (hours) (e.g. 24 for daily checks)
  4. Save and Apply Changes
  5. Navigate to Inventory
  6. Review detected scripts — the Hash Status column shows OK/Changed/Error/Unchecked
  7. Click Check All to force an immediate hash check
  8. If a script shows Changed, investigate — it may be a legitimate update or a supply-chain attack
  9. If legitimate, click Reset Hash to establish a new baseline
  10. If suspicious, click Ignore to stop monitoring it, or Delete to remove it

Verification

  1. Check CSP header:

    curl -k -sI https://localhost/ | grep -i content-security-policy
  2. Check Server-Timing header (beacon trust):

    curl -k -sI https://localhost/ | grep -i server-timing

    Expect: cxid;desc="<uuid>" on HTML responses (when beacon trust is enabled).

  3. Check beacon script injection:

    curl -k -s https://localhost/ | grep -o '_cx-assets.js'

    Expect: the beacon script tag in HTML responses (when beacon injection is enabled).

  4. Check inventory:

    • Navigate to Protection > Page Protect > Inventory
    • Verify scripts detected via CSP reports (source = CSP) or beacon (source = Beacon) appear
    • Verify hash status shows OK after the first check
  5. Test violation reporting:

    • Open your site in a browser
    • Open developer tools > Console
    • Look for CSP violation reports
    • Verify violations appear in the Reports tab
  6. Test enforcement:

    • Switch a policy from monitor to enforce
    • Apply Changes
    • Attempt to load a non-allowlisted script
    • Verify the browser blocks it
  7. Test beacon trust:

    • Enable beacon trust (see step-by-step above)
    • Browse your site
    • Navigate to System > Tables
    • Expand beacon_trust_table — verify your IP appears
    • Expand cxid_table — verify recent cxids appear

Next Steps