Opened 5 years ago
Last modified 11 days ago
#52976 new defect (bug)
user emails comparison should be case insensitive
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | Awaiting Review | Priority: | normal |
| Severity: | normal | Version: | 4.3 |
| Component: | Users | Keywords: | good-first-bug has-patch has-screenshots has-unit-tests |
| Focuses: | administration, coding-standards | Cc: |
Description (last modified by )
In user.php for WordPress 5.7, email update comparisons are case sensitive. Is there a specific reason for this? Because emails are case insensitive. Here is the line that does that:
if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] )
Can the function:
strcasecmp
be used instead? The problem is that there is a plugin that uses the function:
wp_update_user
And it would send a notification for email change even if it was the casing of the characters only.
Thanks for your time and consideration
Attachments (4)
Change History (26)
#2
in reply to:
↑ 1
@
5 years ago
Hello @dd32,
Thanks for your response. The plugin that uses wp_update_user does not alter the casing. It simply passes the email address to it and does not perform any checks on the similarity of the old and new addresses.
Ok, I have seen your note in https://tools.ietf.org/html/rfc5321 now. I am not sure how to take this further, so feel free to close the ticket if you prefer to stick to the RFC.
#3
@
5 years ago
- Description modified (diff)
- Version changed from 5.7 to 4.3
Related: #32158
strcasecmp was used for the wp_insert_user function (but not wp_update_user)
#4
@
5 years ago
- Keywords needs-patch good-first-bug added
Just noting the locations I could find that use != for email comparisons:
wp-includes/user.php: if ( $user_email !== $old_user_data->user_email || $user_pass !== $old_user_data->user_pass ) {
wp-includes/user.php: if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
wp-admin/user-edit.php: if ( $new_email && $new_email['newemail'] != $current_user->user_email && $profileuser->ID == $current_user->ID ) :
wp-admin/index.php: $hide = ( 0 === $option || ( 2 === $option && wp_get_current_user()->user_email !== get_option( 'admin_email' ) ) );
wp-admin/includes/class-wp-screen.php: if ( 2 === $welcome_checked && wp_get_current_user()->user_email !== get_option( 'admin_email' ) ) {
wp-admin/options-general.php:if ( $new_admin_email && get_option( 'admin_email' ) !== $new_admin_email ) :
And for completeness, those which use strcasecmp():
wp-includes/capabilities.php: if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
wp-includes/pluggable.php: if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
wp-includes/pluggable.php: if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
wp-includes/user.php: if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
wp-admin/user-edit.php: <?php if ( 0 !== strcasecmp( $profileuser->user_email, get_site_option( 'admin_email' ) ) || ! is_super_admin( $profileuser->ID ) ) : ?>
#5
@
5 years ago
Hi, I found some other locations that use !== and === for emails comparisons.
wp-admin\includes\class-wp-automatic-updater.php:
if ( $notified && get_site_option( 'admin_email' ) === $notified['email'] && $notified['version'] == $item->current ) {
wp-admin\includes\class-wp-automatic-updater.php:
if ( $n && 'fail' === $n['type'] && get_site_option( 'admin_email' ) === $n['email'] && $n['version'] == $core_update->current ) {
wp-admin\includes\misc.php:
if ( get_option( 'admin_email' ) === $value || ! is_email( $value ) ) {
wp-admin\network\settings.php:
if ( $new_admin_email && get_site_option( 'admin_email' ) !== $new_admin_email ) :
wp-includes\ms-functions.php:
if ( get_site_option( 'admin_email' ) === $value || ! is_email( $value ) ) {
Because I'm new to contributing to WordPress, should I create a patch that uses strcasecmp() for email comparisons, or should I wait until it's clear that we need case insensitive email comparison?
#7
@
4 years ago
Hello,
I've updated the initial patch because it wasn't compatible with the latest WP version
This ticket was mentioned in PR #4751 on WordPress/wordpress-develop by @adamsilverstein.
3 years ago
#9
Trac ticket: https://core.trac.wordpress.org/ticket/52976
This ticket was mentioned in PR #9196 on WordPress/wordpress-develop by PANawkar.
10 months ago
#10
Trac ticket:
#11
@
4 months ago
I reviewed PR #9196 and the approach using strcasecmp() for case-insensitive email comparisons looks correct. This aligns with how other parts of WordPress Core already handle email comparisons (as noted in comment 4 by @dd32).
The fix properly addresses the issue where users receive unnecessary "email changed" notifications when only the letter casing differs (e.g., User@… vs user@…).
While RFC 5321 technically allows case-sensitive local parts, virtually all mail servers treat emails as case-insensitive. This change improves user experience without breaking compatibility.
+1 for committing this patch. The PR has been open for 7 months and would benefit from a review from a committer.
@showravhasan commented on PR #9196:
4 months ago
#12
Reviewed the code changes. The approach using strcasecmp() for case-insensitive email comparisons is correct and consistent with how other parts of WordPress Core already handle email comparisons.
LGTM 👍
Left a comment on the Trac ticket as well: https://core.trac.wordpress.org/ticket/52976
#13
@
4 months ago
- Keywords has-screenshots added; needs-testing removed
Reproduction Report
Description
This report validates whether the issue can be reproduced.
Environment
- WordPress: 7.0-alpha-61500
- PHP: 8.3.30
- Server: PHP.wasm
- Database: WP_SQLite_Driver (Server: 8.0.38 / Client: 3.51.0)
- Browser: Chrome 143.0.0.0
- OS: macOS
- Theme: Twenty Twenty-One 2.7
- MU Plugins: None activated
- Plugins:
- Test Reports 1.2.1
Steps to Reproduce
Use playground:
https://playground.wordpress.net/?php=8.3&wp=trunk&networking=yes
Method1 - Manually
- WordPress dashboard > Users > View your user email (e.g. admin@…)
- Click username and change email to ADMIN@…
- Profile updates but no notification is triggered. Email is visible with the uppercase version.
- Change it to a different email such as user@… and notification is triggered.
Method2 - Programmatically
- Install any classic theme
- Add the following code to functions.php
// Get a test user
$user_id = 1;
$user = get_userdata($user_id);
$original_email = $user->user_email; // e.g., "admin@localhost.com"
// Update with same email but different case
$result = wp_update_user([
'ID' => $user_id,
'user_email' => strtoupper($original_email) // "ADMIN@LOCALHOST.COM"
]);
- Visit Users page and check the user email.
- Email is updated but no notification is triggered.
Actual Results
- ❌ Email notification is not triggered with the uppercase version.
Supplemental Artifacts
Method1 - Manually:
Users screen after manually updating the email to uppercase version (notification NOT triggered)

Users screen after manually updating the email to a different email (notification triggered)

Method2 - Programmatically:
Users screen after changing email programmatically (notification NOT triggered)

@
5 weeks ago
Refreshed patch for current trunk. Uses strcasecmp() for case-insensitive email comparison in both the activation key reset (line 2550) and email change notification (line 2766). 2 unit tests. All 124 user tests pass.
#16
@
5 weeks ago
Hi @motylanogha, I tried to reproduce the bug against the trunk again, but similarly to my previous bug reproduction test, changing lowercase to uppercase email did not trigger the notification. Am I missing something?
#17
@
5 weeks ago
Hi @ozgursar,
What you observed is the expected, buggy behaviour on the current trunk, and that's exactly the issue this ticket aims to fix.
Right now, on trunk, the comparison at line 2766 uses strict !==, which treats admin@… and ADMIN@… as different strings. However, the way the code flows works, the email gets silently updated without properly triggering the confirmation/notification email. So the user ends up with a changed email, and no confirmation was ever sent, which is a problem.
The patch in 52976.2.diff fixes this by using strcasecmp() so that case-only differences are treated as "same email"; no update, no notification needed. This is the correct behaviour since email addresses are case-insensitive per RFC 5321, and virtually all mail servers treat them that way.
To verify the fix works, you'd need to apply the patch to your local trunk checkout and then confirm that:
- Changing admin@… -> ADMIN@… is treated as no change (no notification, no update).
- Changing admin@… -> different@… still properly triggers the confirmation email.
The 2 unit tests included in the patch cover both scenarios.
#18
@
5 weeks ago
- Keywords reporter-feedback removed
Thanks for the clarification @motylanogha . I'll do the patch testing accordingly.
#19
@
5 weeks ago
Patch Testing Report
Patch Tested: https://core.trac.wordpress.org/attachment/ticket/52976/52976.2.diff
Environment
- WordPress: 7.1-alpha-20260406.185701
- PHP: 8.3.30
- Server: PHP.wasm
- Database: WP_SQLite_Driver (Server: 8.0.38 / Client: 3.51.0)
- Browser: Chrome 146.0.0.0
- OS: macOS
- Theme: Twenty Fourteen 4.4
- MU Plugins: None activated
- Plugins:
- Email Logger
- Test Reports 1.2.1
Steps taken
Install Email Logger plugin from https://make.wordpress.org/test/files/2026/02/wp-email-logger.zip
Method1 - Manually
- Change your user's email from admin@… to ADMIN@…
- Observe email notification that contains the email change
- Apply patch and repeat the steps 1-2
- Observe email notification is not triggered
- ✅ Patch is solving the problem
Method2 - Programmatically
- Install any classic theme
- Add the following code to your active theme's
functions.php
$user_id = 1; // ID of the user you are testing
$user = get_userdata($user_id);
$original_email = $user->user_email; // e.g., "admin@localhost.com"
$result = wp_update_user([
'ID' => $user_id,
'user_email' => strtoupper($original_email) // "ADMIN@LOCALHOST.COM"
]);
- Visit Users page and observe the user's email
- Check email logs and observe the email change notification sent
- Apply patch and repeat the steps with a new user
- Observe that no notification email sent even though email changes.
- ✅ Patch is solving the problem
In both Method 1 and Method 2, changing the email in any way other than capitalization successfully triggers the email change notification.
Expected result
- We expect no notifications to be triggered if only the letter case changes.
Additional Notes
- Other locations that use
!==and===for emails comparisons should also be checked.
#20
@
5 weeks ago
- Focuses administration coding-standards added
Tested the patch: https://core.trac.wordpress.org/attachment/ticket/52976/52976.2.diff
Environment:
- WordPress: 7.0-RC2
- PHP: 8.3.30
- Browser: Chrome
- Database: MySQL 8.4.8
- OS: Ubuntu
Testing Method:
- Update the user's email from user@… to different case variations such as:
- USER@…
- User@…
- User@…
- Check if an email notification is triggered for this change
- Apply the patch
- Repeat the same steps with the above variations
- Verify that no email notification is triggered after applying the patch
Also tested programmatically using the wp_update_user() function, and it behaves as expected.
Result:
This confirms that the patch resolves the issue. If the update only modifies the letter case, it should not trigger any notification.
This ticket was mentioned in PR #11697 on WordPress/wordpress-develop by @motylanogha.
11 days ago
#21
Email addresses are case-insensitive in their domain part (per RFC 5321) and treated as such by virtually all mail providers in the local part too. Previously, comparing user_email with !== could:
- Reset
user_activation_keyeven when only the case of an email changed - Trigger the email-change notification for a case-only "change"
This PR replaces those strict comparisons with strcasecmp().
Tests cover:
- Case-only email change does not fire
send_email_change_email - Genuinely different email change still fires the notification
- Case-only email change does not clear
user_activation_key
Complements GH-9196 with proper PHPUnit coverage. Narrower scope than GH-9196 (focused on user.php only); happy to expand to admin/multisite call sites in a follow-up if reviewers prefer one bundled change.
#22
@
11 days ago
Tested PR https://github.com/WordPress/wordpress-develop/pull/11697
I created a user with testcase@example.com, set user_activation_key to key, and then updated the email to TestCase@Example.COM.
Before the patch, the send_email_change_email flow fired for this case-only change and the activation key was cleared.
After the patch, the notification flow does not fire and the activation key remains key.
Patch tests well for the case-only email change scenario.


Hi @asaifm and welcome to Trac!
Updating this to use lower-case comparisons (Just throw it through strtolower() IMHO) seems reasonable, however.. a plugin altering the user email address to lower case does seem unexpected and potentially a bigger bug than this is.
Just as a note,
user@example.comandUSER@example.comcould technically be different users, as the email standard delegates that to the mail servers, however in reality no mail servers that I'm aware of have case sensitive handling..