Cloudflare Access + Antora Sites

Overview

This runbook documents how to protect Antora documentation sites with Cloudflare Access (Zero Trust) while ensuring static assets (CSS, JS, fonts) load correctly for authenticated users.

The Problem

When you protect an Antora site with Cloudflare Access:

  1. Users authenticate and can see the HTML content

  2. But static assets at /_/* return 302 redirects to the Access login page

  3. The page renders with a white background because CSS/JS can’t load

  4. Browser console shows failed requests for //css/site.css, //js/site.js, etc.

The Solution

Create a separate Access application for the static assets path (/_/*) with a bypass policy. This allows static assets to load without authentication while keeping the main site protected.

Architecture

Cloudflare Access Static Assets

Two Access Applications:

Application Domain Policy

domus-docs

docs.domusdigitalis.dev

Allow: Your email

domus-docs-static

docs.domusdigitalis.dev/_/*

Bypass: Everyone

Prerequisites

  • Cloudflare API token with Account:Access: Apps and Policies:Edit permission

  • CF_ACCOUNT_ID and CF_API_TOKEN environment variables

dsource d000 dev/app

Procedure

Step 1: List Existing Access Applications

Verify your current Access configuration:

curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[] | {id, name, domain}'
Example Output
{
  "id": "328ba6e8-bf0d-4490-8f66-8837acaaaa82",
  "name": "domus-docs",
  "domain": "docs.domusdigitalis.dev"
}

Step 2: Create Static Assets Access Application

Create a new Access application specifically for the /_/* path:

curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "domus-docs-static",
    "type": "self_hosted",
    "domain": "docs.domusdigitalis.dev/_/*",
    "session_duration": "24h"
  }'
Expected Response
{
  "result": {
    "id": "ac2893db-f5c2-40dd-b2e1-08d1a1f047c2",
    "name": "domus-docs-static",
    "domain": "docs.domusdigitalis.dev/_/*",
    "self_hosted_domains": ["docs.domusdigitalis.dev/_/*"]
  },
  "success": true
}

Save the id from the response for the next step.

Step 3: Add Bypass Policy

Add a bypass policy to allow everyone to access static assets:

# Replace with your app ID from Step 2
STATIC_APP_ID="ac2893db-f5c2-40dd-b2e1-08d1a1f047c2"

curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/${STATIC_APP_ID}/policies" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bypass Static Assets",
    "decision": "bypass",
    "include": [{"everyone": {}}]
  }'
Expected Response
{
  "result": {
    "id": "fae81e60-b364-4aaa-b6bc-197e660e5b6e",
    "name": "Bypass Static Assets",
    "decision": "bypass",
    "include": [{"everyone": {}}]
  },
  "success": true
}

Step 4: Verify Configuration

Test that static assets now load without redirect:

curl -sI "https://docs.domusdigitalis.dev/_/css/site.css" | head -5
Expected Output
HTTP/2 200
date: Thu, 12 Feb 2026 04:21:06 GMT
content-type: text/css; charset=utf-8
content-length: 82721
access-control-allow-origin: *

If you see HTTP/2 302 with a redirect to cloudflareaccess.com, the bypass policy is not working.

Step 5: Clear Browser Cache

The browser may have cached the 302 redirect. Clear cache or use incognito:

  • Hard refresh: Ctrl+Shift+R

  • Full cache clear: Ctrl+Shift+Delete → Clear cached images/files

  • Incognito: Ctrl+Shift+N

Troubleshooting

CSS Still Returns 302

  1. Verify the static assets app exists:

    curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
      -H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[] | select(.name | contains("static"))'
  2. Verify the bypass policy exists:

    STATIC_APP_ID="<your-static-app-id>"
    
    curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/${STATIC_APP_ID}/policies" \
      -H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[]'
  3. Check that the domain pattern is correct:

    The domain must be docs.domusdigitalis.dev/_/ (with the trailing /)

Authentication Error (10000)

Your API token doesn’t have Access permissions. Create a new token at dash.cloudflare.com/profile/api-tokens with:

  • AccountAccess: Apps and PoliciesEdit

Page Still White After Fix

  1. Clear browser cache completely

  2. Check browser console (F12) for any other blocked resources

  3. Verify all static asset paths start with /_/:

    curl -s "https://docs.domusdigitalis.dev/_/js/site.js" | head -1
    curl -s "https://docs.domusdigitalis.dev/_/css/site.css" | head -1

Security Considerations

The /_/* path only contains:

  • Minified CSS (site.css)

  • Minified JavaScript (site.js, vendor/highlight.js)

  • Web fonts

  • Images (logos, icons)

These are not sensitive - they’re the same assets used by the public Antora UI bundle. Bypassing authentication for this path does not expose any documentation content.

API Reference

List Access Applications

curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[]'

Delete Access Application

APP_ID="<app-id>"

curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/${APP_ID}" \
  -H "Authorization: Bearer ${CF_API_TOKEN}"

List Policies for an Application

APP_ID="<app-id>"

curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/${APP_ID}/policies" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[]'

Delete Policy

APP_ID="<app-id>"
POLICY_ID="<policy-id>"

curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/${APP_ID}/policies/${POLICY_ID}" \
  -H "Authorization: Bearer ${CF_API_TOKEN}"

Revision History

Date Author Changes

2026-02-12

Evan Rosado

Initial documentation after debugging production CSS loading issue