Skip to content

REST API Read Permissions: Aligning with WordPress Core

CMB2 is changing how its REST API handles read access to options-page boxes and fields, to match WordPress core conventions.

For site owners (non-technical)

What is CMB2? A developer library many themes and plugins use to build custom fields and settings screens. It usually arrives bundled inside something else you installed.

Why am I seeing this notice? Only when all of these are true:

  • You're logged into the admin as an administrator — an account that can manage the site's settings.
  • The site has at least one CMB2 settings page whose data is marked readable through the WordPress REST API.
  • That settings page still uses the original read behavior; its developer hasn't switched on the new one yet.
  • Nobody has dismissed the notice — dismissing hides it permanently, for every administrator on the site.

It also disappears on its own once a developer enables the new behavior.

What's changing, and what should I do? Core lets content like post fields be read publicly but restricts site settings to administrators. Where a developer has deliberately opted a CMB2 settings page into the REST API — which is off by default, so most settings pages aren't in the API at all — CMB2 has allowed anyone to read its values; a future release will require the permission needed to view that settings screen. For almost all sites nothing changes — settings screens keep working, and only direct REST API reads are affected. If a custom front-end reads your settings, send this page to your developer.

For developers

What's changing, precisely

CMB2's REST read callbacks have always allowed reads for any box registered with show_in_rest. Going forward, boxes whose object_types include options-page will have reads gated by the box's capability (default manage_options) — matching core, where WP_REST_Settings_Controller::get_item_permissions_check() requires manage_options while object meta reads stay public. Writes and deletes are unchanged. The change ships opt-in in 2.13.0, with an admin notice naming affected registrations, and becomes the default in a later release. A box — or a single field on it — can override all of it with the rest_read_capability property below.

Why now — a bit of history

CMB2's REST support shipped in v2.2.3 (2016-10-25), six weeks before WordPress 4.7 introduced the core settings endpoint and its manage_options read gate. Public reads were the period-correct convention then; core later settled on a different one for settings.

The rest_read_capability property

Set it in the box registration array — no filters involved. It works on any box with a readable show_in_rest, not just options-pages.

php
$cmb = new_cmb2_box( array(
	'id'           => 'my_settings',
	'title'        => 'My Settings',
	'object_types' => array( 'options-page' ),
	'option_key'   => 'my_settings',
	'capability'   => 'manage_options',
	'show_in_rest' => WP_REST_Server::READABLE,

	'rest_read_capability' => 'box-capability', // Gate reads by this box's `capability`.
) );
ValueWho may read
falseNobody, administrators included (maps to core's do_not_allow).
trueEveryone, logged in or not — an alias for core's exist, which WordPress grants every visitor.
'box-capability'Holders of the box's own capability (falling back to manage_options). A reserved sentinel, not a real capability — use it instead of repeating a value that can drift.
any other non-empty string, e.g. 'edit_posts'Holders of that capability.
unset (default)CMB2's default policy — see Which setting do I want?.

Per-field declarations

The same property is also a field parameter, taking the same values. It cascades like show_in_rest: the field's value wins, then the box's, then the default policy.

php
$cmb->add_field( array(
	'name' => 'License Key',
	'id'   => 'license_key',
	'type' => 'text',

	// Only this field is gated; the rest of the box keeps the box's setting.
	'rest_read_capability' => 'box-capability',
) );

Gated fields are omitted from the /boxes/{id}/fields listing, which still returns 200 even when the collection empties out; requesting one directly returns rest_forbidden. That mirrors core, where collections omit what you may not see and single resources deny.

Which setting do I want?

"Only privileged users should read this."'box-capability', or a capability such as 'edit_posts'.

"Reads should keep working publicly, permanently."true. The future default change leaves the box alone.

"REST reads should be off entirely."false.

"I'll follow CMB2's default." → leave it unset:

  • Today: these reads are public.
  • In a future release: options-page boxes left unset will require the box's capability, so any anonymous integration reading those settings will stop working at that update.
  • Non-options-page boxes (post, user, term, comment) left unset stay public, with no change planned.

Declare rest_read_capability per box when you own the registration. When the boxes come from a theme or plugin you don't, flip the gate site-wide:

php
add_filter( 'cmb2_rest_enforce_options_page_read_permissions', '__return_true' );

Keeping public reads for a specific box

If a box's values are genuinely public — a headless front-end reading display settings, say — declare it, and it holds before and after the default flips:

php
'rest_read_capability' => true, // Everyone; reads stay public.

First check that every field on it is fit for anonymous reading — options pages accumulate API keys, license keys, and email addresses. Where only some are, keep the box public and gate the exceptions with a per-field declaration.

If you cannot edit the box registration

The permission filters do the same job at runtime, and they run last with final say:

php
$keep_public = function ( $can_access, $controller ) {
	return 'your_box_id' === $controller->rest_box->cmb->cmb_id ? true : $can_access;
};

add_filter( 'cmb2_api_get_box_permissions_check', $keep_public, 10, 2 );
add_filter( 'cmb2_api_get_field_permissions_check', $keep_public, 10, 2 );
php
add_filter( 'cmb2_rest_enforce_options_page_read_permissions', '__return_false' );

Preserves the historical behavior for every options-page box whose registration doesn't say otherwise — a rest_read_capability declaration always wins over this filter. A transition aid; prefer the property.

Documentation for the CMB2 WordPress developer toolkit.