WordPress user roles and permissions: what each role can actually do

A complete reference to the six built-in WordPress roles, what each capability controls, how Author differs from Editor, and how to add custom roles safely with add_role() or a role editor plugin without breaking the least-privilege model.

Scope and versions. This article documents the WordPress roles and capabilities system as implemented in WordPress 5.5 and newer, verified against the current Roles and Capabilities page in the official documentation (last revised September 2024) and the developer reference for get_role() and the WP_Roles class. Capability lists are stable across recent WordPress releases, but WooCommerce and plugin-provided capabilities track their own versions and are flagged separately below.

A short framing before the tables. WordPress does not grant permissions directly to users. It grants capabilities to roles, assigns roles to users, and checks capabilities at runtime via current_user_can() and user_can(). Roles are a convenient bundle. Capabilities are the thing that actually matters. If you understand that split, the rest of this article is bookkeeping.

The six built-in roles and their capability table

WordPress ships with five roles on a single-site install and adds a sixth on Multisite. The table below is the full set of primitive capabilities, grouped by the lowest role that has them. A "yes" means the role has that capability by default, "no" means it does not, and "(single)" means the capability is present on single-site Administrators but restricted to Super Admin on Multisite.

Capability Super Admin Administrator Editor Author Contributor Subscriber
read yes yes yes yes yes yes
edit_posts yes yes yes yes yes no
delete_posts yes yes yes yes yes no
edit_published_posts yes yes yes yes no no
publish_posts yes yes yes yes no no
delete_published_posts yes yes yes yes no no
upload_files yes yes yes yes no no
edit_others_posts yes yes yes no no no
delete_others_posts yes yes yes no no no
read_private_posts yes yes yes no no no
edit_private_posts yes yes yes no no no
delete_private_posts yes yes yes no no no
edit_pages yes yes yes no no no
edit_others_pages yes yes yes no no no
edit_published_pages yes yes yes no no no
publish_pages yes yes yes no no no
delete_pages yes yes yes no no no
delete_others_pages yes yes yes no no no
delete_published_pages yes yes yes no no no
edit_private_pages yes yes yes no no no
read_private_pages yes yes yes no no no
delete_private_pages yes yes yes no no no
moderate_comments yes yes yes no no no
manage_categories yes yes yes no no no
manage_links yes yes yes no no no
unfiltered_html yes yes (single) yes (single) no no no
switch_themes yes yes no no no no
edit_theme_options yes yes no no no no
manage_options yes yes no no no no
import / export yes yes no no no no
list_users / promote_users / remove_users yes yes no no no no
edit_dashboard yes yes no no no no
customize yes yes no no no no
activate_plugins yes yes no no no no
delete_site yes yes no no no no
install_plugins / install_themes yes yes (single) no no no no
update_core / update_plugins / update_themes yes yes (single) no no no no
delete_plugins / delete_themes yes yes (single) no no no no
edit_plugins / edit_themes / edit_files yes yes (single) no no no no
create_users / edit_users / delete_users / add_users yes yes (single) no no no no
manage_network / manage_sites / create_sites / delete_sites yes no no no no no
manage_network_users / manage_network_plugins / manage_network_themes / manage_network_options yes no no no no no
upload_plugins / upload_themes / upgrade_network / setup_network yes no no no no no

One capability is deliberately missing from every role: unfiltered_upload. It is never granted by default, not even to Super Admin. It only becomes available when you set define( 'ALLOW_UNFILTERED_UPLOADS', true ); in wp-config.php, and even then only Administrators on single-site or Super Admins on Multisite receive it. This is why WordPress rejects SVG uploads out of the box.

Super Admin (Multisite only)

Super Admin is not a WordPress role in the database sense. It is a separate flag stored as an entry in the site_admins network option, not a row in the user_roles option. On a single-site install, Super Admin does not exist at all. Administrators on single-site effectively fill the same function.

On Multisite, Super Admin is the only user type that can manage the network. That means creating, editing, or deleting sites, installing or updating plugins and themes network-wide, managing network users, and editing wp-config.php-style options at the network level. Site-level Administrators on a subsite keep control of their own posts, pages, categories, and subsite settings, but lose the ability to install plugins or themes, delete users, or edit theme and plugin files. Those capabilities shift up to Super Admin.

The practical rule on Multisite: an Administrator runs a site. A Super Admin runs the network. If you are running a single-site install, ignore Super Admin entirely.

Subscriber to Administrator: what each role is actually for

The table is precise but abstract. Here is what the roles do in practice, from the lowest to the highest, with the decisions they enable.

Subscriber

A Subscriber has exactly one capability: read. That is enough to log in, see the admin bar, and edit their own profile. They cannot write, upload, comment-moderate, or see anything meaningful in the dashboard. Subscriber exists for membership-style sites where front-end access is the only thing a user needs: paywalled content, members-only downloads, or forum participation where the forum plugin does its own access checks. If you are not using one of those patterns, Subscriber is the correct default for new registrations, and leaving it as the default (Settings > General > New User Default Role) is the safest choice.

Contributor

A Contributor adds edit_posts and delete_posts on top of Subscriber. That lets them write and save drafts, but it specifically does not include publish_posts or upload_files. In other words, a Contributor can draft a post but cannot hit publish, and cannot insert their own images. Drafts go to "Pending Review", and an Editor or Administrator has to approve them. They also lose edit access to their own posts the moment those posts go live, because edit_published_posts is absent.

This makes Contributor the right role for guest writers, external reviewers, or situations where a workflow gate is non-negotiable. It is the wrong role for anyone you trust to publish without review, because you will spend your entire week clicking "Publish" on their drafts.

Author

An Author is a Contributor plus publish_posts, edit_published_posts, delete_published_posts, and upload_files. An Author can write, publish, revise, and unpublish their own posts, and upload media into the library. They cannot touch anyone else's posts, cannot create or edit pages, cannot manage categories, and cannot moderate comments.

The key limitation that trips people up: an Author cannot be restricted to specific categories out of the box. There is no core capability that says "this user can only publish in News, not in Reviews". Authors can assign any existing category to their own posts, and Editors can create new ones freely. If you need that kind of constraint, you need a plugin: PublishPress Permissions is the most capable option, and Members covers simpler cases. That is also true for taxonomy-scoped editing on Editors; the core capability system does not model it.

Editor

An Editor is the first role with real editorial authority. They can edit, publish, and delete anyone's posts and pages, manage categories and links, moderate comments, and see private posts. They cannot install plugins, change themes, edit files, change site settings, or manage users. On single-site they also have unfiltered_html, which means they can save raw <script> and <iframe> markup in post content. On Multisite, unfiltered_html is restricted to Super Admin only, which is a deliberate security boundary because an Editor on one subsite should not be able to inject arbitrary JavaScript on a network they do not own.

Editor is the correct role for anyone who runs the content side of the site but should not be able to break the site itself. "Content lead" is the mental model.

Administrator

An Administrator has the full set of single-site capabilities: install and update plugins, themes, and core, edit theme and plugin files through the dashboard editor, create and delete users, and change every setting. On single-site, Administrator is the highest privilege level. On Multisite, Administrators lose install_plugins, install_themes, update_core, delete_users, create_users, edit_users, edit_plugins, edit_themes, and edit_files. Those all move up to Super Admin.

There is a persistent misconception worth correcting: Administrator does not grant server access. The WordPress Administrator role controls the WordPress application layer only. It does not give SSH, SFTP, or filesystem access. The "WordPress needs to access your web server" prompt you sometimes see during updates is a function of file ownership and OS permissions, not WordPress roles.

The nuance is that single-site Administrators hold edit_plugins and edit_themes, which let them modify PHP files through the dashboard Theme and Plugin File Editor. That is a WordPress-layer code execution vector. It is not server shell access, but it is enough to run arbitrary PHP. On a hardened install I always set define( 'DISALLOW_FILE_EDIT', true ); in wp-config.php, which removes the editor screens and the underlying capabilities entirely. That change is part of every WordPress install I run and is covered in more detail in my WordPress security hardening checklist.

WooCommerce Customer and Shop Manager

WooCommerce adds two roles on top of the core five. They follow the same capability model; they just add WooCommerce-specific capabilities. Verified against the WooCommerce roles and capabilities documentation.

Customer is a Subscriber with account context. Core-wise, it has read and nothing else, but WooCommerce recognises it as the role assigned automatically to anyone who registers during checkout. A Customer can view their own order history and edit their billing and shipping details on the My Account page. They cannot see anyone else's orders or access any backend settings.

Shop Manager is the role WooCommerce designed specifically so store staff do not need Administrator. It has the full Editor capability set plus manage_woocommerce (which unlocks the WooCommerce settings screens, product editing, and order management) and view_woocommerce_reports. It does not have install_plugins, update_core, or the user-management capabilities that Administrator holds. A Shop Manager can run the shop end-to-end, process orders, edit products, and pull reports, but cannot install a new payment gateway plugin or change any general WordPress setting. This is the correct role to give a store manager or customer-service lead; Administrator is almost always too much.

Adding, removing, and reassigning users

Adding a user is straightforward from Users > Add New on single-site. You set a username, email, and role, and WordPress writes the row to wp_users and assigns the capabilities in wp_usermeta via the wp_capabilities meta key. The role is stored as a serialized array of role slugs, which is why a user can in principle hold more than one role at once (core only assigns one, but WP_User::add_role() and several plugins use this).

On Multisite, the flow changes. A site-level Administrator cannot create_users on the network, only add_users to their own subsite from a pool of users who already exist on the network. If the user does not exist yet, only a Super Admin can create them. This is a deliberate network-wide integrity rule, because a user may have membership on multiple subsites and a single subsite admin should not be able to create a global identity.

Changing a user's role from Users > All Users updates wp_capabilities in wp_usermeta immediately. Demotions take effect on the next page load. Promoting a user to Administrator on single-site is effectively irreversible from the recipient side: once they have the role, they can change anyone else's role, including the person who promoted them. Take promotions seriously.

Deletion semantics: admin UI vs wp_delete_user vs WP-CLI

Deleting a user is where most of the accidental data loss in WordPress happens, because the behaviour differs between the admin UI, the wp_delete_user() function, and WP-CLI. The defaults are not the same across the three, and assuming they are is a fast way to delete posts you meant to keep.

Via the admin UI (Users > Delete). WordPress always presents a choice: delete all content belonging to the user, or attribute all content to another user. There is no silent default. The admin has to explicitly pick one or the other. This is the safe path for interactive work.

Via wp_delete_user( $user_id, $reassign ) in PHP. The second parameter defaults to null. If you leave it out or pass null, every post the user authored is deleted along with the user. If you pass a valid user ID, post authorship is transferred to that user instead. This is the footgun: a half-written cleanup script that calls wp_delete_user( $id ) without a reassign argument will silently destroy content. Always pass an explicit reassign ID unless you genuinely want deletion.

Via wp user delete <id> on WP-CLI. Same rule as the PHP function. Without --reassign=<user_id>, posts are deleted. With --reassign=<user_id>, posts are transferred. I always write WP-CLI deletion commands with --reassign first and the ID second so the intent is visible on the line: wp user delete 42 --reassign=1.

Multisite adds one more rule. On Multisite, delete_users is restricted to Super Admin only. Site-level Administrators on a subsite cannot delete users from the network at all. They can only remove_users, which detaches the user from their subsite without touching the underlying account. This is intentional: a user may belong to several subsites, and a single subsite admin should not be able to wipe them from the network.

The safe pattern I use on every project: before deleting any user, open the Users screen, sort by "Posts", confirm the count, and decide explicitly whether content should be reassigned or removed. For programmatic cleanup, I reassign to a generic "Archive" user rather than deleting content. Content is hard to recover from a backup; a reassigned author is easy to fix.

When to use custom roles, and how

Custom roles are appropriate when the built-in five do not cover the job, and when the job is going to keep happening. A one-off capability grant to a single user belongs on the user, not on a new role. Here are the two typical cases.

Small, repeatable adjustments: a code snippet with add_role()

If you need a role that is "Editor minus the ability to delete published pages", a short PHP snippet is enough. It runs once on plugin activation and writes to the user_roles option in wp_options.

// Place this in a mu-plugin, a small custom plugin, or a site-specific
// plugin. Do NOT put it in functions.php: themes change, and roles are
// persistent database state that should outlive the theme.
function myproject_register_content_manager_role() {
    // Guard against re-creating the role on every request. add_role()
    // returns null if the role already exists.
    if ( null === get_role( 'content_manager' ) ) {
        add_role(
            'content_manager',
            'Content Manager',
            array(
                // Inherit the useful bits of Editor...
                'read'                   => true,
                'edit_posts'             => true,
                'edit_others_posts'      => true,
                'edit_published_posts'   => true,
                'publish_posts'          => true,
                'delete_posts'           => true,
                'delete_others_posts'    => true,
                'upload_files'           => true,
                'moderate_comments'      => true,
                'manage_categories'      => true,
                // ...but deliberately leave out page capabilities and
                // unfiltered_html, which would let this role inject
                // arbitrary HTML into post content.
            )
        );
    }
}
register_activation_hook( __FILE__, 'myproject_register_content_manager_role' );

// Matching cleanup: remove the role on plugin deactivation to keep
// the DB clean. Reassign affected users to a fallback role first.
function myproject_remove_content_manager_role() {
    remove_role( 'content_manager' );
}
register_deactivation_hook( __FILE__, 'myproject_remove_content_manager_role' );

Two things matter here. First, the get_role() guard. add_role() writes to wp_options if the role does not exist; calling it on every request is wasteful and sometimes causes subtle race conditions on multi-server setups that use object caching against the options table. Second, the activation hook. Roles are persistent database state, not runtime configuration. If you register a role in functions.php unguarded, you write to the database on every request, and if you ever delete that theme without cleaning up the role, the role stays in the database forever with no code referencing it.

To modify an existing role from code, use add_cap() and remove_cap() on a WP_Role object. Those also write to the database immediately, so treat them the same way: guard with a version check, run once, and provide a reversal path.

// Grant the Editor role the ability to edit general site settings.
// This is aggressive; I would almost never do this in production.
$role = get_role( 'editor' );
if ( $role && ! $role->has_cap( 'manage_options' ) ) {
    $role->add_cap( 'manage_options' );
}

Per-user overrides use WP_User::add_cap() and WP_User::remove_cap(). Those write to wp_usermeta on the specific user, not to the role, which is the right tool when one person needs a one-off capability that does not belong on a shared role.

Bigger jobs: Members and PublishPress Permissions

When the custom-role need looks like "six staff roles, each with a different dashboard", a code snippet stops being maintainable. Two plugins dominate this space.

Members by MemberPress is the straightforward role editor. As of version 3.2.19 (February 2026), it has a UI for creating, cloning, and editing roles; supports multiple roles per user; lets you explicitly deny capabilities in addition to granting them; and covers content permissions at the post and block level. All of its previously paid add-ons are now included for free. It has around 300,000 active installs and is tested against WordPress 6.9.4. Members is the right choice when the job is "make a new role with these capabilities", and the built-in dashboard is enough.

PublishPress Permissions (formerly Press Permit Core) is a different kind of tool. As of version 4.8.0 (6 April 2026), it moves beyond role-level permissions and models per-post, per-category, and per-term access control. It is the tool you reach for when the question is "this Editor should only be able to publish in the 'News' category, and this Author should only be able to edit posts tagged 'Europe'". Core WordPress cannot express that; PublishPress Permissions can, with a rules engine that sits on top of the capability system. It has around 10,000 active installs, which is smaller than Members because the problem it solves is rarer.

My rule of thumb: reach for Members first. If the job is still hard after Members, you need PublishPress Permissions. If the job is still hard after PublishPress Permissions, you are probably trying to build a workflow engine and should look at something like PublishPress Pro or a dedicated editorial tool. A small-business WordPress site almost never gets past the first bullet.

Custom post types and map_meta_cap

Custom post types are where the capability system gets quietly ugly. When you register a CPT with register_post_type(), you can set capability_type (for example 'book'), and WordPress will automatically map a set of capability names such as edit_book, read_book, edit_books, edit_others_books, publish_books, and read_private_books. By default, none of these capabilities are granted to any role. The CPT exists but nobody can edit it from the dashboard.

There are two things you almost always need to set alongside capability_type:

  • map_meta_cap => true. This tells WordPress to use map_meta_cap() to translate per-object checks (like "can this user edit this specific book?") into primitive capability checks ("does this user have edit_others_books?"). Without it, the Administrator role cannot edit CPT posts without manually getting the capabilities assigned, because WordPress will not perform the meta-capability translation. Setting map_meta_cap => true is almost always what you want.
  • A block of add_cap() calls that grant the primitive capabilities to whichever roles should be able to use the CPT. Without this, the role definition for "Editor can edit books" does not exist and the dashboard menu will not appear for them.
// Minimal primitive-capability grant for a "book" CPT, run once at
// plugin activation alongside the register_post_type() call.
function myproject_grant_book_caps_to_editor() {
    $role = get_role( 'editor' );
    if ( ! $role ) {
        return;
    }
    $role->add_cap( 'edit_books' );
    $role->add_cap( 'edit_others_books' );
    $role->add_cap( 'edit_published_books' );
    $role->add_cap( 'publish_books' );
    $role->add_cap( 'delete_books' );
    $role->add_cap( 'delete_others_books' );
    $role->add_cap( 'delete_published_books' );
    $role->add_cap( 'read_private_books' );
}
register_activation_hook( __FILE__, 'myproject_grant_book_caps_to_editor' );

The common failure mode: someone registers a CPT, sets capability_type => 'book', forgets map_meta_cap => true, and then spends an afternoon wondering why the Administrator account cannot see the "Books" menu. The fix is either flipping map_meta_cap to true or manually granting every primitive capability to the Administrator role, and the first option is almost always correct.

Application Passwords are not scope-limited

Application Passwords were introduced in WordPress 5.6 (December 2020) as a way for external applications to authenticate against the REST API without storing a user's real password. They are generated per-user from the Edit User profile screen and can only be used for machine-to-machine authentication; wp-login.php explicitly rejects them. That part is a meaningful security improvement.

What is not yet true, despite being frequently assumed: Application Passwords do not narrow capabilities. A token created by an Administrator account has full Administrator-level access to the REST API. There is no built-in "read-only" scope, no per-endpoint scope, no "can only edit posts" scope. Per-scope application passwords were discussed as a core improvement but were never shipped in WordPress 6.x. If you need a reduced-capability token, you either create a dedicated lower-privileged user and generate the password on that user, or you restrict usage with the wp_is_application_passwords_available_for_user filter, which only enables or disables availability rather than narrowing scope.

The practical rule: treat an Application Password exactly like giving someone the underlying user's full role. If the user is an Administrator, that token can do everything an Administrator can. If you need a limited-capability token, create a limited-capability user first.

Least privilege is the whole point

The capability system exists so you can give each user exactly the power they need and nothing more. In practice, that turns into three habits.

Assign the lowest role that works. A writer who drafts posts and needs review is a Contributor, not an Author. A content lead who runs the editorial calendar is an Editor, not an Administrator. A store manager on WooCommerce is a Shop Manager, not an Administrator. The rule "when in doubt, go one role lower and add capabilities if needed" is almost always safer than "start at Administrator and trust everyone".

Keep Administrators rare and audit them. Every Administrator on a production site is a potential full compromise. I review the Administrator list on every site at least quarterly and remove anyone who does not actively need it. For emergency access, there is a reason WordPress has DISALLOW_FILE_EDIT; removing the dashboard file editor removes the single most useful tool a compromised Administrator has.

Reassign before you delete. Whether from the admin UI, from PHP, or from WP-CLI, always reassign authored content to another user before deleting an account. The only exception is when you genuinely want the content gone, in which case make that decision deliberately, not through a default. If you want to understand the downstream access errors that happen when permissions go wrong, the sorry, you are not allowed to access this page article walks through the capability checks that generate it and how to trace which capability is missing.

Need a WordPress fix or custom feature?

From error fixes to performance improvements, I build exactly what's needed—plugins, integrations, or small changes without bloat.

Explore web development

Search this site

Start typing to search, or browse the knowledge base and blog.