TL;DR
- Roles are named bundles of permissions; capabilities are the individual permissions.
- Create roles on activation, not every request.
- Use least‑privilege access and document what each role can do.
Roles vs Capabilities (Semantic Model)
In WordPress, roles are bundles of permissions and capabilities are the atomic permissions themselves. The default roles (Administrator, Editor, Author, Contributor, Subscriber) are just predefined bundles. When you build a custom workflow, you’re really deciding which capabilities a role should have and which it must not have.
Think in terms of capability mapping. For example, if a role can create posts but can’t publish them, you’d grant edit_posts but not publish_posts. This makes your access control explicit and auditable.
Plan Roles Before You Code
Start with a simple role matrix: list tasks on the left and roles across the top. Mark which actions are allowed. This exercise prevents over‑privileged roles and helps you identify which capabilities you actually need to grant. If a role only needs to manage a custom post type, do not grant site‑wide permissions.
A common anti‑pattern is copying Administrator capabilities and removing a few. That approach is risky because you may forget a dangerous permission like manage_options or activate_plugins. Instead, build the role bottom‑up.
Create a Custom Role (Safe Pattern)
Create roles on plugin or theme activation to avoid repeating the operation on every page load. The example below adds a support_agent role with scoped capabilities.
function fyrepress_add_support_role() {
add_role('support_agent', 'Support Agent', array(
'read' => true,
'edit_posts' => true,
'edit_others_posts' => false,
'publish_posts' => false,
'upload_files' => true,
));
}
register_activation_hook(__FILE__, 'fyrepress_add_support_role');
FyrePress tool: Use the User Role & Capability Creator to generate a tailored role quickly.
Capability Mapping for Custom Post Types
If you register a custom post type, you can define a custom capability type and map capabilities automatically. This lets you grant permissions for a specific content type without exposing global post capabilities.
register_post_type('ticket', array(
'label' => 'Tickets',
'public' => false,
'show_ui' => true,
'capability_type' => array('ticket', 'tickets'),
'map_meta_cap' => true,
));
With map_meta_cap enabled, you can grant capabilities like edit_tickets or publish_tickets to specific roles.
How to Remove or Update Roles Safely
Use deactivation hooks to clean up custom roles and capabilities. If your role is no longer needed, remove it and optionally reassign users before deleting the role.
function fyrepress_remove_support_role() {
remove_role('support_agent');
}
register_deactivation_hook(__FILE__, 'fyrepress_remove_support_role');
Audit Existing Users
After deploying a new role, audit current users and reassign them. It’s common to find old admin accounts that should be downgraded. Keeping a lean admin roster is one of the simplest security wins you can make.
FyrePress tool: The WP User Role Audit generates SQL and WP‑CLI commands to review elevated roles.
Frequently Asked Questions
What’s the difference between roles and capabilities?
Roles are named bundles of permissions; capabilities are the individual permissions.
Can I add capabilities without creating a new role?
Yes. You can add or remove capabilities on existing roles.
Should I add roles on every page load?
No. Create roles on activation, not on every request.
How do I rollback a custom role?
Remove the role on deactivation or with a cleanup script.
Key Takeaways
- Custom roles should follow least‑privilege principles.
- Map capabilities to content types whenever possible.
- Audit users after role changes.
Generate a role profile instantly
Use the User Role & Capability Creator to build the exact role you need.