When building a membership site, online store (WooCommerce), or a platform where users log in, the default WordPress behavior can be disruptive. Every logged-in user sees the black Toolbar at the top of the screen.
Learn more about WordPress development services at WPPoland. While useful for you as an administrator to quickly edit posts or jump to the dashboard, it breaks the immersion for a customer. It looks like an unfinished back-office view and frequently overlaps with your custom sticky navigation.
If your goal is a cleaner member area or storefront, the architectural fix is to deactivate the toolbar on the frontend for users who do not need administrative or editorial access, while leaving the dashboard experience untouched for administrators.
Why hide the WordPress admin bar for non-admins
The WordPress Toolbar appears for all logged-in users by default. While helpful for site operators, it creates concrete problems for public-facing user areas:
- Membership sites: Subscribers logging in to consume premium content are presented with shortcuts to WordPress profiles and menus that do not fit the product.
- E-commerce stores: Shoppers purchasing physical or digital products get distracted by administrative links, and fixed navigation bars become misaligned.
- Customer portals: Corporate clients accessing account dashboards receive an interface that looks like an unconfigured blogging engine rather than a custom web app.
- Custom web applications: Headless or hybrid portals integrated into WordPress experience styling conflicts where top-level elements shift by 32 pixels.
The core reasons to suppress the toolbar include:
- Overlapping sticky and fixed header components.
- Exposing internal WordPress administration paths to unauthorized roles.
- Consuming vertical mobile screen estate (46 pixels on narrow viewports).
- Triggering layout shifts during client-side hydration.
Core hook show_admin_bar vs legacy after_setup_theme
In older tutorials, disabling the toolbar often involved calling show_admin_bar(false) inside an action hook like after_setup_theme or init. While calling show_admin_bar() updates a global state variable, the canonical WordPress mechanism is the show_admin_bar filter.
The core function is_admin_bar_showing() evaluates this filter during template loading before sending HTML nodes or style tags to the response buffer.
<?php
/**
* Disable the toolbar on the frontend for non-administrators
*/
add_filter( 'show_admin_bar', 'wppoland_filter_admin_bar_visibility' );
function wppoland_filter_admin_bar_visibility( bool $show ): bool {
// Keep default behaviour inside /wp-admin/
if ( is_admin() ) {
return $show;
}
// Grant access only to accounts with manage_options capability
return current_user_can( 'manage_options' );
}Filtering show_admin_bar directly is cleaner than hooking into after_setup_theme:
- It returns a boolean value directly to the caller without mutating global variables out of sequence.
- It executes at priority 10, allowing other plugins or mu-plugins to inspect or override the value cleanly.
- It ensures that secondary core functions such as
wp_admin_bar_header()do not enqueue stylesheets or inline layout offsets.
Capability checks with current_user_can vs role checking
Checking permissions in WordPress should rely on capabilities rather than role slugs.
A common pitfall is checking for role names:
// Fragile pattern: checks role slug string
if ( ! current_user_can( 'administrator' ) ) {
show_admin_bar( false );
}Why role checking creates technical debt:
- String matching limitations:
'administrator'is a role name, not an actual capability. While WordPress maps role slugs through an internal check, it is an undocumented fallback that breaks when roles are customized. - Custom administrative roles: In enterprise sites, agency teams often create custom roles like
super_editor,site_manager, orshop_managerwith tailored capabilities. Checking for'administrator'locks out legitimate site managers. - Multisite environments: On WordPress Multisite networks, network administrators possess permissions that standard site administrators do not. Capability evaluation remains consistent across network installs.
The robust capability to test for site administration is manage_options:
current_user_can( 'manage_options' )If your editorial team consists of editors and authors who legitimately need frontend editing shortcuts, evaluate content capabilities instead:
<?php
add_filter( 'show_admin_bar', function( bool $show ): bool {
if ( is_admin() || ! is_user_logged_in() ) {
return $show;
}
// Allow users who can edit published posts (Editors, Authors, Admins)
return current_user_can( 'edit_posts' );
} );Why CSS display none is an anti-pattern
A frequent recommendation on quick forums is to add a CSS rule to your theme stylesheet:
/* Anti-pattern: do not use this in production */
#wpadminbar {
display: none !important;
}Hiding the admin bar exclusively with CSS causes several severe problems:
- Wasted asset delivery: WordPress still enqueues
admin-bar.min.css,dashicons.min.css, andadmin-bar.min.js. The browser downloads, parses, and executes scripts for an element that is visually hidden. - The 32-pixel gap bug: When WordPress prepares the toolbar, it outputs an inline style tag attached to
wp_head:
If you applyhtml { margin-top: 32px !important; } @media screen and (max-width: 782px) { html { margin-top: 46px !important; } }display: noneto#wpadminbar, the markup vanishes, but themargin-topon the root<html>element remains active. The page displays an awkward white or grey gap at the top, and fixed headers sit displaced from the viewport boundary. - Accessibility hazards: Screen readers and keyboard navigation tabs still discover hidden links in the DOM unless explicitly suppressed with
visibility: hiddenand proper inert attributes. Users tabbing through the page hit invisible links pointing to administrative profile pages. - Information leakage: The HTML source still exposes internal user IDs, login names, and active WordPress plugin nodes in the frontend response.
Filtering the hook with PHP prevents the markup, inline CSS, and JavaScript libraries from rendering entirely.
Preventing dashboard access via template_redirect and admin_init
Hiding the toolbar only cleans up the frontend presentation. It does not secure the site against unauthorized dashboard access.
Subscribers and customers can still type /wp-admin/ or /wp-login.php into their address bar. By default, WordPress allows subscribers to view the profile screen (/wp-admin/profile.php), revealing backend URLs, plugin notices, and potential attack surfaces.
To enforce separation between frontend users and the dashboard, combine the show_admin_bar filter with redirects on admin_init and template_redirect.
<?php
/**
* Block backend dashboard access for non-administrators
*/
add_action( 'admin_init', 'wppoland_restrict_admin_dashboard_access' );
function wppoland_restrict_admin_dashboard_access(): void {
// Allow AJAX requests to pass through
if ( wp_doing_ajax() ) {
return;
}
// Allow users with manage_options to access the dashboard
if ( current_user_can( 'manage_options' ) ) {
return;
}
// Redirect subscribers and customers to the homepage or custom portal
wp_safe_redirect( home_url( '/my-account/' ) );
exit;
}Notice the critical check for wp_doing_ajax(). Front-end scripts often dispatch asynchronous requests to /wp-admin/admin-ajax.php. If you redirect all non-admins on admin_init without exempting AJAX calls, you break checkout flows, dynamic forms, and filter widgets.
For REST API or frontend profile redirection, you can also listen on template_redirect:
<?php
add_action( 'template_redirect', 'wppoland_redirect_author_archives' );
function wppoland_redirect_author_archives(): void {
// Prevent customer author archives from leaking usernames
if ( is_author() ) {
$author = get_queried_object();
if ( $author instanceof WP_User && in_array( 'customer', (array) $author->roles, true ) ) {
wp_safe_redirect( home_url(), 301 );
exit;
}
}
}Membership plugins and WooCommerce customer portals
Membership software and e-commerce platforms handle user account portals in distinct ways.
WooCommerce account endpoints
WooCommerce creates frontend account endpoints (/my-account/, /my-account/orders/, /my-account/edit-account/). Customers should never encounter the WordPress admin bar or WordPress dashboard.
<?php
add_filter( 'show_admin_bar', function( bool $show ): bool {
if ( is_admin() ) {
return $show;
}
// Always keep toolbar disabled for customer role
$user = wp_get_current_user();
if ( in_array( 'customer', (array) $user->roles, true ) ) {
return false;
}
// Shop managers need to see the toolbar for quick order editing
if ( current_user_can( 'manage_woocommerce' ) ) {
return true;
}
return current_user_can( 'manage_options' );
} );This configuration protects customer checkout sessions while retaining the bar for shop managers reviewing orders and updating inventory from product pages.
Membership platforms (MemberPress, Restrict Content Pro)
Membership platforms often map paying members to the default subscriber role or a custom membership tier. If members see the toolbar:
- They encounter confusing links pointing to
/wp-admin/profile.php. - Third-party caching plugins may mistakenly cache the toolbar for one user and serve it to another if user-specific cache partitioning is misconfigured.
- Sticky navigation bars inside video player pages or lesson modules get obstructed.
Using a Must-Use plugin guarantees that regardless of which theme updates occur, subscribers never load the toolbar.
Custom toolbar node permissions and wp_before_admin_bar_render
Sometimes clients do not want to remove the toolbar completely for editors, but need to remove sensitive, cluttered, or third-party items (such as SEO audits, comments, or update warnings).
Instead of removing the entire bar, manipulate toolbar nodes using the admin_bar_menu action hook:
<?php
/**
* Remove specific toolbar nodes for non-administrators
*/
add_action( 'admin_bar_menu', 'wppoland_clean_toolbar_nodes', 999 );
function wppoland_clean_toolbar_nodes( WP_Admin_Bar $wp_admin_bar ): void {
// Leave the toolbar intact for full administrators
if ( current_user_can( 'manage_options' ) ) {
return;
}
// Remove WordPress logo, comments, and new content shortcuts
$wp_admin_bar->remove_node( 'wp-logo' );
$wp_admin_bar->remove_node( 'about' );
$wp_admin_bar->remove_node( 'wporg' );
$wp_admin_bar->remove_node( 'documentation' );
$wp_admin_bar->remove_node( 'support-forums' );
$wp_admin_bar->remove_node( 'feedback' );
$wp_admin_bar->remove_node( 'comments' );
$wp_admin_bar->remove_node( 'new-content' );
$wp_admin_bar->remove_node( 'updates' );
}This technique allows content contributors to retain their user account toggle and logout link without exposing core software update alerts or administrative quick links.
Deploying via Must-Use plugin vs child theme functions.php
When deciding where to place this code, compare child themes against Must-Use (mu-plugins) packages:
| Factor | Theme functions.php | Must-Use Plugin (wp-content/mu-plugins/) |
|---|---|---|
| Theme switching persistence | Code stops running when theme changes | Remains active across all themes |
| Execution timing | Runs during theme setup | Executes before regular plugins and themes |
| Accidental deactivation | Can be deleted by theme file edits | Cannot be disabled from the WordPress admin UI |
| Version control integration | Tied to frontend theme repo | Can be tracked in infrastructure / core repo |
To deploy as a Must-Use plugin:
- Create a file at
wp-content/mu-plugins/wppoland-admin-bar-policy.php. - Add the following complete production snippet:
<?php
/**
* Plugin Name: WPPoland Admin Bar Policy
* Description: Disable toolbar for non-administrators and restrict backend access.
* Version: 1.0.0
* Author: WPPoland
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// 1. Frontend toolbar filter
add_filter( 'show_admin_bar', static function( bool $show ): bool {
if ( is_admin() ) {
return $show;
}
return current_user_can( 'manage_options' );
}, 10 );
// 2. Dashboard access restriction
add_action( 'admin_init', static function(): void {
if ( wp_doing_ajax() ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
wp_safe_redirect( home_url( '/' ) );
exit;
}
}, 10 );1. The residual margin-top problem
If an earlier developer attempted to fix the toolbar with CSS, your stylesheet may contain hardcoded offsets:
/* Check for hardcoded compensation rules */
body {
padding-top: 32px;
}
.site-header {
top: 32px;
}Inspect the DOM using browser developer tools:
- Verify that
bodydoes NOT have the class.admin-bar. - Verify that the
<html>element hasmargin-top: 0. - Remove manual
top: 32pxrules from theme files. Instead, use standard flexbox or CSS grid flow.
2. Full Site Editing block themes
In modern FSE block themes (such as Twenty Twenty-Four or custom block themes), sticky headers use CSS properties like position: sticky; top: 0;. When the admin bar is removed via PHP, the sticky block naturally snaps to the viewport top without custom CSS overrides.
3. Page builder overrides
Page builders like Elementor or Beaver Builder sometimes include their own sticky header calculations based on the presence of #wpadminbar. Disabling the bar via the show_admin_bar filter prevents builders from injecting artificial header offsets.
Handling user profile preferences and user meta overrides
WordPress provides a built-in user preference setting located inside the user profile screen under the label “Show Toolbar when viewing site”. This setting corresponds to the user meta key show_admin_bar_front.
Understanding how WordPress resolves this preference is essential for maintaining predictable behavior:
- When WordPress evaluates
is_admin_bar_showing(), it first checks the global$show_admin_barvariable. - If the global variable has not been explicitly toggled, it checks the user meta field
get_user_option( 'show_admin_bar_front' ). - Finally, it passes the resulting boolean value through the
show_admin_barfilter.
Because the filter runs last, your filter callback in wppoland_filter_admin_bar_visibility takes precedence over whatever a user selected in their profile screen. If an administrator wishes to allow individual editors or authors to toggle their toolbar while keeping it disabled for subscribers, you can respect the profile meta conditionally:
<?php
/**
* Respect profile preference for editors while disabling for subscribers
*/
add_filter( 'show_admin_bar', function( bool $show ): bool {
if ( is_admin() || ! is_user_logged_in() ) {
return $show;
}
// Subscribers and customers never see the toolbar
if ( ! current_user_can( 'edit_posts' ) ) {
return false;
}
// For authors and editors, check if they explicitly turned it off in their profile
$user_id = get_current_user_id();
$pref = get_user_meta( $user_id, 'show_admin_bar_front', true );
if ( 'false' === $pref ) {
return false;
}
return true;
} );This pattern accommodates editorial staff who prefer an unobstructed reading experience when reviewing long-form articles, while ensuring that public subscribers cannot re-enable the toolbar through automated profile form submissions.
Security considerations and user enumeration
Disabling the toolbar provides significant user experience improvements, but it is essential to understand what it does and does not do from a security perspective:
- Defense in depth: Hiding visual UI elements is not an authorization barrier. Always enforce server-side capability checks in custom endpoints and admin hooks.
- Author archives: WordPress exposes user author archives at URLs like
/?author=1or/author/username/. If a customer or subscriber has an author archive, their username is revealed. Redirect author archives for non-publishing roles usingtemplate_redirect. - REST API user enumeration: By default,
/wp-json/wp/v2/userslists accounts with published posts. Ensure subscribers and private customers are not exposed by restricting this endpoint or disabling public user listings for non-privileged accounts.
<?php
/**
* Restrict the REST API user endpoint to authenticated team members
*/
add_filter( 'rest_endpoints', function( array $endpoints ): array {
if ( isset( $endpoints['/wp/v2/users'] ) ) {
$endpoints['/wp/v2/users'][0]['permission_callback'] = static function(): bool {
return current_user_can( 'list_users' );
};
}
return $endpoints;
} );Applying this REST API filter prevents automated vulnerability scanners from harvesting user account slugs through API scraping.
Caching layers and proxy considerations
High-performance WordPress hosting relies on reverse proxies (such as Nginx FastCGI cache, Varnish, or Cloudflare). Introducing role-dependent HTML output requires strict cache headers:
- Bypass cache for logged-in cookies: Reverse proxies must bypass cache whenever session cookies like
wordpress_logged_in_*are detected in request headers. If a proxy caches a page requested by an administrator, public visitors might receive a page with administrative toolbar placeholders or broken layout offsets. - Cache partitioning: If your architecture caches logged-in user pages (for instance, using WooCommerce customer cache keys), ensure the cache key includes the user role or capability level so that subscriber and administrator responses remain isolated.
- Cache-Control headers: Ensure your login and account endpoints send
Cache-Control: no-cache, must-revalidate, max-age=0to prevent intermediate ISP proxies from storing customer account views.
Verification checklist for production deployments
Before pushing your changes to production, follow this ten-minute QA checklist on a staging environment:
- Test unauthenticated visitors: Open an incognito browser window and navigate to the homepage. Confirm that no
#wpadminbarelement exists, the body does not have.admin-bar, and the<html>element does not havemargin-top: 32px. - Test subscriber accounts: Log in as a subscriber or WooCommerce customer. Confirm that the toolbar is completely absent, header elements remain flush to the top of the browser viewport, and no horizontal scroll occurs.
- Attempt direct dashboard navigation: As a subscriber, navigate directly to
/wp-admin/and/wp-admin/profile.php. Verify that the server redirects the request to your designated landing page (such as/or/my-account/) rather than loading the WordPress admin screens. - Test administrator accounts: Log in as an administrator. Confirm that the toolbar displays correctly on both frontend and backend pages, with all quick-edit and cache-clearing shortcuts operating as expected.
- Verify background AJAX requests: Trigger an AJAX cart update or dynamic search on the frontend. Open browser developer tools and confirm that calls to
admin-ajax.phpreturn HTTP 200 responses rather than 302 redirects. - Verify mobile responsive layouts: Emulate a mobile screen with a width under 782 pixels. Ensure there is no 46-pixel empty bar above the header navigation.
Summary
Disabling the WordPress admin bar for non-administrators transforms your site from a default blogging installation into a professional web portal. Using the show_admin_bar filter alongside capability verification ensures consistent performance and clean layout rendering.
Key implementation checklist:
- Use
add_filter('show_admin_bar', ...)rather than CSSdisplay: none. - Check
current_user_can('manage_options')instead of role strings. - Pair the filter with an
admin_initredirect to block backend access for subscribers. - Whitelist
wp_doing_ajax()so storefront AJAX requests succeed. - Deploy in
wp-content/mu-plugins/for persistence across theme updates.
If you need help configuring advanced access control or tailoring custom portals, explore WordPress development services at WPPoland.







