Independent Editorial DeskWordPress Releases, Builds, and Operations
Back to Archive
Hardening NotesImplementation Notes

WordPress Custom Roles and Capabilities Example: Least-Privilege Access

Create custom WordPress roles and capabilities with least-privilege access, activation hooks, capability checks, migrations, and testing steps.

Published

April 20, 2026

Reading Time

3 min read

Updated

April 20, 2026

Abstract user roles connected to a permissions panel for least-privilege WordPress access.
Control LedgerHardening Notes

Hardening Notes

Baselines, access reduction, and default settings that stand up in production.

Best For

Teams preparing, launching, or maintaining WordPress as a backend service in a production stack.

Primary Topics

Hardening NotesImplementation Notes

Editorial Focus

Control Ledger: Baselines, access reduction, and default settings that stand up in production. Updated on April 20, 2026.

Full Report

Last reviewed: April 20, 2026

WordPress roles and capabilities are the difference between “everyone is an administrator” and a site that can be operated safely. Many teams create editor accounts for convenience, then discover that the role can do too much or too little for the real workflow.

This guide shows how to create a custom role and capability model for a plugin. The goal is least-privilege access: users should have the permissions they need, without gaining unrelated administrative power.

Who this guide is for

This article is for WordPress plugin developers, agencies, publishers, and site operators who need custom editorial, support, moderation, or operational roles.

Role vs capability

A role is a named bundle of capabilities. A capability is the permission checked in code. Production code should usually check capabilities, not role names. A user may have multiple roles, custom capabilities, or a role modified by another plugin.

if ( ! current_user_can( 'vulnwp_manage_reviews' ) ) {
	wp_die( esc_html__( 'You are not allowed to manage reviews.', 'vulnwp-roles' ) );
}

Create a custom role on activation

<?php
/**
 * Plugin Name: VulnWP Review Role
 * Description: Example custom role and capability setup.
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

register_activation_hook( __FILE__, 'vulnwp_add_review_role' );
register_deactivation_hook( __FILE__, 'vulnwp_remove_review_role' );

function vulnwp_add_review_role() {
	add_role(
		'vulnwp_reviewer',
		__( 'Security Reviewer', 'vulnwp-roles' ),
		array(
			'read'                  => true,
			'edit_posts'            => true,
			'upload_files'          => true,
			'vulnwp_manage_reviews' => true,
		)
	);

	$administrator = get_role( 'administrator' );

	if ( $administrator ) {
		$administrator->add_cap( 'vulnwp_manage_reviews' );
	}
}

function vulnwp_remove_review_role() {
	remove_role( 'vulnwp_reviewer' );

	$administrator = get_role( 'administrator' );

	if ( $administrator ) {
		$administrator->remove_cap( 'vulnwp_manage_reviews' );
	}
}

This creates a focused reviewer role and also gives administrators the custom capability. The plugin removes the role and custom administrator capability on deactivation. If the capability controls persistent business data, document whether it should remain after uninstall.

Use capability checks in admin pages

add_action( 'admin_menu', 'vulnwp_reviews_menu' );

function vulnwp_reviews_menu() {
	add_menu_page(
		__( 'Review Queue', 'vulnwp-roles' ),
		__( 'Review Queue', 'vulnwp-roles' ),
		'vulnwp_manage_reviews',
		'vulnwp-review-queue',
		'vulnwp_render_review_queue'
	);
}

Using the custom capability in the menu registration keeps the screen hidden from users who do not need it. Still check capability inside the page callback as a defense-in-depth measure.

Use object-specific checks when possible

Some actions are not global. Editing a specific post should use a meta capability such as edit_post with the post ID.

if ( ! current_user_can( 'edit_post', $post_id ) ) {
	wp_send_json_error(
		array( 'message' => 'You cannot edit this post.' ),
		403
	);
}

This lets WordPress map the check to the correct primitive capabilities for that post, author, status, and role configuration.

Migration and upgrade rules

Roles are stored in the database. If a plugin changes its capability model in a later release, treat that as a migration. Do not assume every site has the same role state. Administrators may have edited roles manually, another plugin may have changed them, or a staging import may have copied old capabilities.

function vulnwp_roles_maybe_upgrade() {
	$version = get_option( 'vulnwp_roles_version', '0' );

	if ( '1.1.0' === $version ) {
		return;
	}

	$reviewer = get_role( 'vulnwp_reviewer' );

	if ( $reviewer && ! $reviewer->has_cap( 'vulnwp_export_reviews' ) ) {
		$reviewer->add_cap( 'vulnwp_export_reviews' );
	}

	update_option( 'vulnwp_roles_version', '1.1.0' );
}

add_action( 'admin_init', 'vulnwp_roles_maybe_upgrade' );

Keep migrations idempotent. Running the same migration twice should not break access.

Testing workflow

  1. Create a user with the custom role only.
  2. Confirm the user can access only the intended admin screens.
  3. Try blocked actions directly by URL, AJAX, and REST if applicable.
  4. Test an administrator account after capability changes.
  5. Deactivate the plugin on staging and confirm the removal policy works.

Production checklist

  • Check capabilities in code, not role names.
  • Use custom capabilities for plugin-owned privileges.
  • Add roles and capabilities on activation, not on every request.
  • Use object-specific meta capability checks where possible.
  • Test with real low-privilege accounts.
  • Document who should receive each custom role.
  • Review custom roles during access audits.

Common mistakes

  • Giving everyone administrator access. Convenience increases incident impact.
  • Checking role names directly. Capabilities are the correct authorization boundary.
  • Adding capabilities on every page load. Role setup belongs in activation or controlled migrations.
  • Forgetting administrators. Custom capabilities may need to be granted to administrator roles too.
  • No removal policy. Decide what happens when the plugin is deactivated or uninstalled.

Related reading

This pairs with our administrator access audit checklist and the secure Admin AJAX example.

References and further reading

Popular Guides

Popular WordPress guides to read next.

These articles connect recurring production concerns: implementation details, updates, troubleshooting, recovery paths, and operational cleanup.

Continue Reading

More from the archive.

Diagnostic dashboard scene representing a WordPress Site Health review before major updates.
01Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Site Health Check Before Major Updates: What to Review First

A pre-update WordPress Site Health checklist covering loopbacks, connectivity, debug settings, and environment readiness.

Structured data and route review scene representing permalink validation after a WordPress migration.
02Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Permalink Checklist After Migration: Catch URL Problems Early

A post-migration WordPress permalink checklist for checking rewrite rules, post URLs, archives, and redirect noise.

Technical media workspace representing image preparation and optimization before upload to WordPress.
03Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Image Optimization Checklist: What to Fix Before Upload

A practical WordPress image optimization checklist covering dimensions, compression, formats, and Media settings before upload.