Picture the site. A course platform, or a members’ area behind a small shop, or a club with a login so people can see the fixtures. Every account on it is a subscriber — or a customer, or a member, or whatever the plugin that created it decided to call the role. None of them will ever write a post. And every one of them, the moment they log in, gets the WordPress admin bar: the W logo, the site name, Howdy, and thirty-two pixels shaved off the top of a design somebody paid for.
Then the email arrives. “What is this black bar? Did I break something?” They did not. WordPress assumes anyone who is logged in wants a toolbar for running the site, because for the first ten years of its life that was true. On a site with members, it no longer is.
So: hide the admin bar for subscribers. Not for the editors, not for you, and — this
is the part people get wrong — not inside /wp-admin/. Front end only,
decided by what the user can do, in ten lines you own. No plugin from the
directory required, although the recommended home for it is technically a plugin
of one file, and I will get to that.
What the WordPress admin bar is for
It is worth being fair to the thing before removing it. The admin bar is the bridge between the site and its back office: Edit Page on whatever you are looking at, New for a post or a media upload, the comment count, the update count, Customize, and the account menu with Log Out in it. For anyone who works on the site it earns its thirty-two pixels several times a day.
Every one of those links is either useless to a subscriber or hidden from them already. What they get is the frame with nothing in it — a logo, a site name and a greeting — which is worse than either having the bar or not having it. The bar is for people who can edit. That sentence is also the test we are about to write.
The show_admin_bar filter, with a capability check
WordPress decides whether to render the bar in one function,
is_admin_bar_showing(), and the last thing that function does before
answering is run the show_admin_bar filter. Core’s own comment on it
says returning false there is the recommended way to hide the admin
bar. That is the whole mechanism. Everything else is deciding who.
Here is the decision:
add_filter( 'show_admin_bar', function ( bool $show ): bool {
return current_user_can( 'edit_posts' ) ? $show : false;
}, 1000 );
Read it as a sentence: if this user can edit posts, leave the bar as WordPress
and the user’s own preference already decided; otherwise, no bar.
edit_posts is the capability every Contributor, Author, Editor and
Administrator holds and no Subscriber does — it is where WordPress itself draws the
line between “works on the site” and “reads the site”. If your site draws it
somewhere else, swap the capability: edit_pages, the management
capability your shop plugin registers, or one you have registered yourself. The
shape stays the same.
Three small things about those three lines. The parameter arrives as a boolean —
core hands you true, false, or the user’s own
Show Toolbar when viewing site preference, already cast — so the type
declarations are honest, not decorative. You do not need
is_user_logged_in(): a visitor who is not logged in never reaches this
filter with true, and current_user_can() is false for them
anyway. And the priority, 1000, is a choice rather than a magic
number: it runs after the theme and the other plugins have had their say, and for a
user who cannot edit it should be the last word.
Why a capability and not a role name
The version of this snippet you will find in a hundred forum answers checks the role instead:
// Don't do this.
add_filter( 'show_admin_bar', function ( bool $show ): bool {
return in_array( 'subscriber', wp_get_current_user()->roles, true ) ? false : $show;
}, 1000 );
It works on the day you write it and breaks quietly afterwards, because a role is a
label over a set of capabilities, and the label is the part that
changes. The shop plugin you install next year adds a customer role
and a shop_manager role; your check knows about neither, so customers
get the bar back and the shop manager — who genuinely needs it — is fine by
accident. The membership plugin creates member, gold_member
and trial. A colleague makes a custom role for the newsletter people.
Somebody holds two roles at once. On a multisite, the super admin has a
subscriber role on this particular site and your test hides the bar from
the one person who most needs it.
WordPress never asks “what is this user’s role?” when it decides what they may do.
It asks current_user_can(), every time, for every screen. Your snippet
should ask the same question, so that the answer changes only when the
permissions change — which is the one time you want it to.
Why you never hide the admin bar on wp-admin
Here is the safety property, and it is the reason this snippet is safe to paste on
a live site at five o’clock on a Friday: the show_admin_bar
filter only governs the front end. Open
wp-includes/admin-bar.php and read is_admin_bar_showing()
— the check for is_admin() comes before the filter runs and
returns true unconditionally. Inside /wp-admin/ the bar is
part of the interface, and core does not let a filter take it away. Whatever you
return, every user who lands on /wp-admin/profile.php — and every
subscriber can — sees the bar, with the account menu and Log Out in it.
Which means this snippet cannot lock anyone out. Not you, not an editor, not a subscriber. That is not a property of your code; it is a property of the filter, and it is why the filter is the right tool and the alternatives are not:
- Not CSS.
#wpadminbar { display: none }hides the bar everywhere, wp-admin included, and leaves the thirty-two-pixel gap that core adds to make room for it. It also hides it from you. Do not. - Not a redirect or a
wp_die()for “users who should not be in wp-admin”. That is a different feature with different risks, and the day it is wrong it is wrong for the administrator too. Keep it out of this file. - Not
show_admin_bar( false )on its own. That function exists, and you will see it recommended; it sets the global default to off for everyone, administrators included, and then still runs the filter. It is the blunt version. Use the filter with the capability check and you get the same effect for exactly the people who should have it.
Two things the bar was quietly providing need a home once it is gone for
non-editors. The logout path: the bar’s account menu carried
Log Out, so give members a link the theme owns instead — a menu item that
points at wp_logout_url(), or the log-out link on their account page,
which every membership and shop plugin I have met puts there anyway. Do not
hand-write /wp-login.php?action=logout; it needs a nonce, and
wp_logout_url() adds it. The Edit links: nothing to
do. The capability check leaves the bar exactly as it was for everyone who can edit,
Edit Page included.
Where the snippet lives: mu-plugin, functions.php or a snippets plugin
Three places will run those lines. They differ in who can switch them off, and by accident.
A must-use plugin — recommended
wp-content/mu-plugins/ is a directory WordPress loads on every request,
before the ordinary plugins, without asking. Anything in it appears under
Plugins → Must-Use with no activate or deactivate link. It survives a theme
switch, it survives a plugin clear-out, and nobody can turn it off from the admin
— the only way to remove it is to remove the file. For a rule about who sees what,
that is exactly the durability you want.
- Look for
wp-content/mu-plugins/besideplugins/andthemes/. Most sites do not have it. Create it. - Save the complete script below as
wp-content/mu-plugins/hide-admin-bar-for-non-editors.php. Directly in the directory, not in a subfolder — WordPress only loads the PHP files at the top level ofmu-plugins/. - Upload it however you deploy files. It is live on the next request; there is nothing to activate.
- Open Plugins → Must-Use and confirm it is listed.
The theme’s functions.php — simplest
Paste the add_filter call — just those three lines, not the plugin
header, and not a second <?php if the file already opens with one
— at the end of the active theme’s functions.php. Thirty seconds, and
if you are already in the theme it is the natural place. The cost: it
dies with the theme. Switch themes and the bar is back for every
member; update a theme you did not build and the file may be overwritten. If it
goes here, it goes in a child theme.
A snippets plugin — a UI, at a price
There is a whole category of plugins whose job is to hold PHP snippets and give you
an admin screen to manage them. Paste the add_filter call into a new
PHP snippet, scope it to the front end if the plugin offers that, save, enable. What
you get is a UI and a description field. What you pay is another plugin, another
thing that can be deactivated, and one more place for the next developer to look when
the bar disappears and nobody remembers why. On a site that already runs one, fine.
Do not install one for this.
The complete script
This is the mu-plugin form. It is a single file, no dependencies, PHP 7.4 or later — which is also where the arrow function in the aside below needs to be. Copy the whole thing.
<?php
/**
* Plugin Name: Hide the admin bar for non-editors
* Description: On the front end, hides the WordPress admin bar for logged-in users who cannot edit content. wp-admin is untouched.
* Version: 1.0.0
*/
// The show_admin_bar filter only runs on the front end: is_admin_bar_showing()
// returns true for wp-admin before this is ever consulted, so nobody can be
// locked out. Swap 'edit_posts' for the capability that defines "works on the
// site" for you; keep it a capability, not a role name.
add_filter( 'show_admin_bar', function ( bool $show ): bool {
return current_user_can( 'edit_posts' ) ? $show : false;
}, 1000 );
No declare( strict_types ), no if ( ! defined( 'ABSPATH' ) )
guard, no class. A must-use plugin is included by WordPress, not evaluated by
anything else; the header is what puts it on the Must-Use list with a readable
name, and the rest is the three lines from above.
Aside — disable the admin bar for non-admins instead. If the rule on your site is “only administrators see the bar”, the one-line variant is the same filter with a different capability:
add_filter( 'show_admin_bar', fn ( bool $show ): bool => current_user_can( 'manage_options' ) ? $show : false, 1000 );
manage_options is the capability that marks an administrator. Your
editors lose the bar too, so they lose Edit Page too — decide that on
purpose.
How to verify it
- As a subscriber. Open a private window, log in with a subscriber (or customer, or member) account, load the home page. No bar, and no thirty-two-pixel gap at the top — core only adds that spacer when the bar is showing, so it goes with it.
- As an editor. Another private window, an editor account, the same page. Bar present, Edit Page in it.
- On wp-admin, as either. Visit
/wp-admin/profile.phpwith each account. Bar present both times. That is the safety property, seen rather than believed. - The logout link. As the subscriber, find the log-out link you gave them and use it. If you cannot find it, neither can they.
If step one still shows the bar, the usual reasons are that the file is in a
subfolder of mu-plugins/, that a page cache served the logged-in view
from before the change, or that the account you are testing with holds a role that
carries edit_posts after all — which, as above, is the test doing its
job.
The other half: hiding it for the editors too
I should say where this piece comes from. My own plugin,
Freshet Edit
Jump, does the opposite: it hides the admin bar for the people who
can edit — the same filter, current_user_can( 'edit_posts' )
the other way round — and gives them Cmd/Ctrl+E to
jump to the edit screen of whatever they are looking at, which was the only thing
they used the bar for anyway. It deliberately leaves the bar alone for everyone else,
because that was never its job. Install both and nobody sees the bar at all: editors
get a shortcut, members get their thirty-two pixels back. That is a choice, not a
collision — the two filters pass each other’s answer straight through — and on a
members’ site it is usually the right one.
Common questions
How do I hide the WordPress admin bar for subscribers without a plugin?
A show_admin_bar filter that returns false unless the
user has edit_posts. Ten lines in wp-content/mu-plugins/,
or in the theme’s functions.php, or in a snippets plugin you already
run. The complete script is above.
Does hiding the admin bar lock users out of wp-admin?
No. is_admin_bar_showing() checks is_admin() before it
runs the filter and always shows the bar inside wp-admin. Every logged-in user still
reaches /wp-admin/profile.php with the bar, and Log Out, in
place.
Why check a capability instead of the user’s role?
Because WordPress does. Roles are names for bundles of capabilities, and the names
change: a shop plugin’s customer, a membership plugin’s
member, a custom role, a user with two roles, a super admin on a
multisite. current_user_can( 'edit_posts' ) is true for exactly the
people who can edit, whatever the role is called this year.
Where do users log out if the admin bar is hidden?
From a link the theme gives them — wp_logout_url() in a menu, or the
log-out link on their account page — and, failing both, from
/wp-admin/profile.php, where the bar still shows.
More of the same, from the jobs that made me build things, on the blog.