In WordPress, understanding where admin information is stored in the database is crucial for tasks such as troubleshooting, customizing user data, or performing advanced user management. WordPress utilizes a structured database system to store various types of data, including user details, roles, and permissions. This data is organized into specific tables that ensure the secure and efficient management of users, including administrators. By knowing which tables to look at, you can gain deeper insights into how WordPress handles user information and how you might interact with it for various administrative tasks.
This comes up more often than you’d think for a topic that sounds purely academic. A plugin conflict locks every admin out of wp-admin and someone needs to reset a password directly in the database. A migration script needs to know exactly which tables hold user data so nothing gets left behind. A site gets compromised and the first question is whether a rogue admin account got created, which means knowing exactly where to look. In every one of those situations, the answer starts in the same two tables.
In WordPress, the admin information, along with other user details, is stored in the following database tables:
1. wp_users
This table stores the basic user information, including the admin’s username, password (hashed), email address, and other essential details. The most relevant fields are:
-
- ID: The unique identifier for the user.
- user_login: The username used to log in.
- user_pass: The hashed password.
- user_email: The email address associated with the user.
- user_registered: The date and time when the user was registered.
A few of the less commonly mentioned columns in this same table are worth knowing about too. user_nicename holds a URL-friendly version of the display name, used in author archive links. user_url stores whatever website URL a user has set on their profile. user_activation_key is used temporarily during password resets and certain account activation flows, it’s normally empty and only populated briefly during that process. And display_name is what actually shows publicly on the site, which can be different from both user_login and user_nicename, a common source of confusion when someone’s looking for a specific username in the table and can’t find it because they’re searching for the display name instead.
2. wp_usermeta

This table stores additional metadata for each user, including the admin. This metadata includes user roles, capabilities, and any other custom information stored about the user. Some of the key fields include:
- user_id: The ID of the user, which links to the ID in the wp_users table.
- meta_key: The name of the meta field.
- meta_value: The value of the meta field.
For example, the admin role is stored in the wp_usermeta table with the meta_key of wp_capabilities, where the meta_value would indicate the role (e.g., a:1:{s:13:”administrator”;b:1;}).
Understanding this table’s structure matters because it’s a genuinely different shape than wp_users. Where wp_users is a normal table with one row per user and a fixed set of columns, wp_usermeta is a key-value table, meaning a single user can have dozens or even hundreds of rows in wp_usermeta, one for each piece of metadata attached to them. That’s why role information lives here rather than in wp_users itself: roles and capabilities are extensible, plugins and custom code frequently add their own meta keys, and a rigid fixed-column table wouldn’t accommodate that. The tradeoff is that querying wp_usermeta directly for something specific, like finding every user with a particular capability, requires filtering on meta_key and meta_value rather than a simple column lookup, which is slower at scale than a dedicated column would be.
The wp_capabilities Meta Key in More Detail
Since role and permission checks are one of the more common reasons someone goes digging into these tables, it’s worth understanding the wp_capabilities value format a bit more closely. WordPress stores it as serialized PHP data, which is why it looks like a:1:{s:13:”administrator”;b:1;} rather than a plain word. Breaking that down: a:1 means it’s an array with one entry, s:13:”administrator” is a string of 13 characters reading “administrator,” and b:1 means the boolean value attached to that key is true. A user with multiple roles would show multiple entries in that same serialized array. This matters practically because editing this value directly through a database tool requires preserving the exact serialization format, string lengths included, a common source of a broken site is someone manually editing a username or role value in a serialized string without recalculating the character count that PHP’s serialization format depends on.
A Note on the wp_ Table Prefix
The examples above use the default wp_ prefix, but plenty of production sites change this during installation as a basic security hardening step, meaning your actual table names might be something like wp7x2_users and wp7x2_usermeta instead. If you’re looking at your own database and not finding tables named exactly wp_users and wp_usermeta, check your site’s wp-config.php file for the $table_prefix variable, that tells you the actual prefix in use before you assume something’s missing or corrupted.
How This Differs on Multisite
If you’re running WordPress Multisite, the picture gets a bit more layered. wp_users and wp_usermeta remain shared network-wide, a single user account can belong to multiple sites within the network, but per-site roles are stored using meta keys that include the specific site’s table prefix, something like wp_2_capabilities for site ID 2 rather than a plain wp_capabilities. That’s a meaningful distinction if you’re troubleshooting a permissions issue on a specific subsite within a multisite network, since checking only the base wp_capabilities key will miss the site-specific role data that actually governs what that user can do on that particular subsite.
Common Reasons People Go Looking for This
Locked out of wp-admin entirely
If every admin account is somehow inaccessible, a forgotten password with no working email reset, a plugin conflict breaking the login screen, the direct-database route is the fallback: locate the account’s row in wp_users by user_login or user_email, and update user_pass using a properly hashed value rather than plain text, since WordPress won’t recognize a plain-text password stored in that column. Most database tools like phpMyAdmin have an MD5 function built into the update interface for this specific reason, though it’s worth confirming your WordPress version’s expected hashing scheme, since it’s changed across major versions.
Auditing for unauthorized admin accounts
After a suspected compromise, checking wp_usermeta for every row with meta_key wp_capabilities (or your multisite-specific equivalent) and a value containing “administrator” gives you a direct list of every account with admin-level access, which is a more reliable check than trusting the Users screen in wp-admin, since a sufficiently sophisticated compromise can sometimes hide accounts from the normal admin list view.
Bulk user data cleanup or migration
Migrating users between sites, merging duplicate accounts, or cleaning up orphaned metadata left behind by a deactivated plugin all involve working directly with these two tables, and understanding the ID relationship between them (wp_usermeta.user_id referencing wp_users.ID) is the foundation for doing any of that safely without leaving dangling references behind.
What Happens to These Tables When a User Is Deleted
Deleting a user through wp-admin prompts WordPress to ask what should happen to that user’s content, attribute it to another existing user, or delete it entirely alongside the account. Behind that prompt, WordPress removes the corresponding row from wp_users and all associated rows from wp_usermeta for that user_id, and updates or removes the post_author references in wp_posts depending on the choice made. Deleting a user directly through raw SQL instead skips that entire decision process, and it’s easy to leave orphaned wp_usermeta rows or dangling post_author references pointing at a user_id that no longer exists in wp_users. This is one of the clearer examples of why the WordPress admin interface or WP-CLI’s wp user delete command, which handles all of this cleanup correctly, is the safer route over a manual DELETE statement against a single table.
A Word of Caution Before Editing Directly
Editing these tables directly through phpMyAdmin or a similar tool bypasses WordPress’s normal validation, sanitization, and hook system entirely. That’s sometimes necessary, the wp-admin lockout scenario above being the clearest example, but it comes with real risk: a malformed serialized value in wp_usermeta, an incorrectly hashed password, or a broken foreign-key relationship between the two tables can produce subtle bugs that are harder to diagnose than the original problem. Always take a full database backup before making a direct edit, and where possible, prefer WordPress’s own admin interface or WP-CLI commands (wp user update, for instance) over raw SQL, since those tools handle the serialization and validation correctly on your behalf.
How Roles Differ From Capabilities
It’s worth being precise about a distinction that gets blurred a lot in casual WordPress discussion: a role and a capability aren’t the same thing, even though the wp_capabilities meta key stores role information. A role, Administrator, Editor, Author, Contributor, Subscriber by default, is really just a named bundle of individual capabilities, edit_posts, publish_posts, manage_options, and so on. When WordPress checks whether a user can perform a given action, it’s actually checking for a specific capability, not the role name itself, the role is just a convenient shorthand bundle. This matters for anyone customizing permissions, since it’s entirely possible to grant or remove individual capabilities from a role, or from a specific user directly, without changing which named role they’re assigned. That fine-grained capability data lives in the same serialized wp_capabilities value in wp_usermeta, it’s just structured to reference a role name that then maps to a broader capability set defined elsewhere in WordPress’s core role definitions.
Using WP-CLI Instead of Raw SQL
For most of the tasks that might otherwise send you into these tables directly, WP-CLI offers a safer path that still gets the job done from the command line rather than the wp-admin UI. wp user list -role=administrator gives you the audit-for-admin-accounts result without touching SQL. wp user update <user> -user_pass=newpassword resets a password with correct hashing handled automatically. And wp user meta list <user_id> shows every meta row for a given user, which is often faster and less error-prone than writing a manual SQL query against wp_usermeta, especially if you’re not entirely comfortable working with raw serialized PHP data by hand.
Related Tables Worth Knowing About
wp_users and wp_usermeta are the core two, but a few adjacent tables come up regularly in the same context.
wp_options
This is where site-wide settings live, including the site’s primary admin_email option, which is different from any individual user’s user_email in wp_users. It’s a common point of confusion during a support investigation: the site-level notification email and a specific admin user’s login email are stored in entirely separate places and can legitimately be different values.
wp_posts
Every post and page has a post_author column that stores a wp_users.ID value, connecting content back to the user who created it. If you’re investigating who authored a specific piece of content, or cleaning up authorship after removing a user account, this is the table that holds that relationship, not wp_users or wp_usermeta directly.
wp_usermeta rows added by plugins
Plugins routinely add their own custom meta keys to wp_usermeta rather than creating entirely new tables, since it’s a simpler way to attach extra data to an existing user record. A membership plugin might store subscription status this way, a forum plugin might store post counts and signatures, a security plugin might store two-factor authentication secrets. This is exactly why wp_usermeta tends to accumulate a large number of rows per user on an actively developed site, and it’s also why deactivating a plugin without properly uninstalling it often leaves orphaned meta rows behind that nothing ever cleans up automatically.
GDPR and Data Export Considerations
If your site needs to comply with data privacy regulations, GDPR being the most commonly referenced, understanding exactly where user data lives becomes more than a curiosity. WordPress core actually ships with built-in tools for this specific purpose, found under Tools > Export Personal Data and Tools > Erase Personal Data in wp-admin, and those tools are built to pull from wp_users, wp_usermeta, and any properly registered custom data sources that plugins have hooked into the personal-data-export system. The key phrase there is “properly registered”: a plugin storing sensitive user data in a custom table or an unregistered usermeta key without hooking into WordPress’s privacy export/erasure filters won’t get picked up by that built-in tool, which is worth checking directly with any plugin handling sensitive user data if compliance is a real concern for your site.
Performance at Scale
On a large site with tens of thousands of users, wp_usermeta specifically can become a meaningful performance factor, since its key-value structure means the table grows far faster than wp_users does, potentially into millions of rows even on a mid-sized membership site once you account for every plugin adding its own meta keys per user. Queries filtering on meta_key and meta_value benefit significantly from the table’s existing indexes, but a poorly written custom query that scans meta_value with a wildcard search, for example, can be genuinely slow at that scale. If you’re building custom functionality that queries usermeta heavily, it’s worth reviewing WordPress’s own WP_User_Query class, which is built to construct these queries efficiently, rather than writing raw SQL against wp_usermeta by hand and potentially missing an indexing consideration a more experienced query would account for.
Security Hardening Around These Tables
Since wp_users and wp_usermeta hold your site’s most sensitive account data, a few standard hardening practices are worth mentioning specifically in this context, beyond the general “keep WordPress updated” advice. Changing the default wp_ table prefix during installation, mentioned above, is a minor obscurity measure rather than real security, but it does block some of the more naive automated SQL injection attempts that assume default table names. More meaningfully, restricting direct database access, disabling or heavily restricting phpMyAdmin access on production, using strong unique database credentials, and enabling two-factor authentication at the WordPress login level all reduce the practical risk of these tables being accessed or modified by anyone other than an authorized admin.
Frequently Asked Questions
Can I add custom fields to wp_users directly instead of using wp_usermeta?
Technically you could alter the wp_users table schema to add new columns, but this isn’t the recommended approach. Core WordPress updates, and many plugins, assume wp_users retains its standard column structure, and a custom column addition risks being overwritten, ignored, or causing an unexpected conflict during a core update. The wp_usermeta key-value approach exists specifically so custom data can be added without touching the core table schema, which is the safer and more forward-compatible path for anything beyond the standard fields.
Why does a single user sometimes have 50 or more rows in wp_usermeta?
This is normal on an actively developed site with several plugins installed, each meta key is its own row, so a user’s admin color scheme preference, dismissed admin notices, plugin-specific settings, role capabilities, and a dozen other small pieces of data each take up a separate row. It’s not a sign of a problem by itself, though a genuinely excessive count, into the hundreds for a single user, can sometimes indicate orphaned data from an uninstalled plugin that never cleaned up after itself.
Is user_pass ever stored as plain text?
No, WordPress always stores a hashed version of the password, never the plain text itself, which is exactly why a direct database edit of this field requires generating a properly hashed value rather than just typing the new password in as-is. If a database export or backup shows what looks like a readable password in that column, something is wrong with the setup and worth investigating immediately, since that would represent a serious and unusual security failure rather than expected WordPress behavior.
Interesting Reads:
What Is The HTML For Fonts In WordPress