A security review asks which pages in the site are restricted and who exactly can read them. Confluence has no screen for it. Restrictions are set one page at a time, by almost anyone, and nothing rolls them up. A site-wide view has been requested for years (CONFCLOUD-74405).
Why restriction visibility goes blind at scale
- Restrictions are decentralized by design. Any editor can lock a page to a few people without asking an admin. Convenient on the day, invisible at audit time.
- The native surface is per page or per person. The lock control shows who holds view and edit on this page. Space permissions show who can enter the space at all. Neither lists the restricted pages inside.
- Restrictions rot. They keep naming accounts that were deactivated a year ago, and pages under a read-restricted ancestor are gated by a page nobody is looking at.
The DIY approach: sweep every page's restrictions
- Enumerate current pages with
GET /wiki/api/v2/pages?status=current, cursor-paged across the whole site, personal spaces included. - Pull each page's restrictions from
GET /wiki/rest/api/content/{id}/restrictionwith user and group expansions, and keep the read and update subjects separately. - Flag the debris: subjects that are deactivated accounts, and pages whose effective read gate is an ancestor's restriction rather than their own.
- Store the inventory by date and diff each sweep against the last, so the next review is "what changed" rather than a full pass.
The catch is that there is no single call for any of this. A site-wide sweep is real work (pagination, per-page lookups within rate limits, retries), it needs scheduling and storage to be more than a one-off, and a single pass still cannot tell you what changed since last time.
A ready-made option: Page-Restriction Governance
Page-Restriction Governance runs that sweep daily and on demand and keeps the result: every restricted page site-wide with the exact users and groups holding read and update, grouped by space. Restrictions naming deactivated accounts are flagged as orphans, inherited restrictions carry a note pointing at the ancestor that actually gates them, and each sweep is diffed against the previous one so the audit becomes a delta. The inventory exports as CSV, one row per subject. Lookups are bounded per run, so large sites are scanned politely; reporting is read-only, with one opt-in, previewed cleanup of orphaned restrictions.
Key facts
- Native coverage: one page at a time via the lock dialog; no site-wide list.
- Two APIs, not one: v2 for pages, v1 for restrictions, joined per page.
- What reviewers want: the inventory, the orphans, and the delta since last quarter.
- Least privilege: page, space, and user read; app storage. Nothing changes on the reporting path.
Related reading: Find what is eating your Confluence storage and where groups are actually used in Jira.
A version of this piece also appears on Atlassian Community: read it on App Central.
Written by Abaho Katabarwa at Katabarwa Labs. We build small, single-purpose Atlassian and Azure tools that run entirely inside your own tenant. Questions: support@llmgraph.ai.