Laravel has a dedicated, built-in validation rule specifically for the "type your password twice" pattern — no custom validator or manual field comparison needed, as long as one specific naming convention is followed.
1. The basic setup
$request->validate([
'password' => 'required|min:8|confirmed',
]);
<input type="password" name="password">
<input type="password" name="password_confirmation">
2. The naming convention that makes it work
The confirmed rule requires the confirmation field to be named exactly {field}_confirmation — for a field called password, that's specifically password_confirmation, not confirm_password, password_confirm, or any other variant. This is the single most common reason the rule seems to "not work": if the confirmation input is named anything other than this exact pattern, the rule has nothing to compare against and the validation silently never triggers — no error is thrown for the misnamed field, it just never actually checks anything.
3. It works on any field, not just "password"
$request->validate([
'email' => 'required|email|confirmed',
]);
<input type="email" name="email">
<input type="email" name="email_confirmation">
Despite the name "confirmed" and its most common use case, the rule isn't password-specific — it applies the same {field}_confirmation naming pattern to confirm any field, including email addresses or any other value worth double-entry confirmation.
4. Combining with other rules
$request->validate([
'password' => [
'required',
'confirmed',
\Illuminate\Validation\Rules\Password::min(8)
->mixedCase()
->numbers()
->symbols(),
],
]);
Laravel's dedicated Password validation rule object (as opposed to the plain string-based rules) provides a fluent way to require specific password complexity — minimum length, mixed case, at least one number, at least one symbol — and composes normally alongside confirmed in the same rules array.
5. The confirmation field itself doesn't need its own explicit rule
Only the primary field needs confirmed added to its rule set — password_confirmation itself doesn't need (and shouldn't have) its own separate validation rule entry; it's read implicitly by the confirmed rule attached to password, purely by its matching naming convention.
6. What the error actually looks like, and customizing it
$request->validate([
'password' => 'required|confirmed',
], [
'password.confirmed' => 'The two passwords you entered do not match.',
]);
The default error message ("The password confirmation does not match.") is reasonable but generic — passing a custom messages array as the second argument to validate(), keyed as {field}.{rule}, overrides the wording for this specific field-and-rule combination without affecting confirmed validation anywhere else in the app.