Forgotten Your Laravel Password? The Complete 2025 Guide to Resetting Passwords (Breeze, Fortify, Jetstream & CLI)
A step‑by‑step, SEO‑friendly guide covering every way to reset a password in Laravel—UI, API, CLI, multi‑auth, and troubleshooting.
Table of Contents
- Quick Fixes
- How Laravel Password Reset Works
- Method 1: UI Flow with Breeze/Jetstream (Blade/Livewire/Inertia)
- Method 2: API Flow with Laravel Fortify (for SPA/Mobile)
- Method 3: Admin/CLI Reset Without Email (Tinker)
- Method 4: Programmatically Send a Reset Link (Controllers/Jobs)
- Method 5: Multi‑Auth Setups (Users/Admins) & Custom Brokers
- Troubleshooting & Common Errors
- Security Best Practices
- FAQs
- Copy‑Paste Snippets
Quick Fixes
- Can’t receive the reset email? Verify
MAIL_MAILER
,MAIL_HOST
,MAIL_PORT
,MAIL_FROM_ADDRESS
, andAPP_URL
in.env
. If you’re using queues, run your worker:php artisan queue:work
. - Need a fast admin override? Use Tinker to set a new password:
php artisan tinker
>>> $u = App\Models\User::where('email','user@example.com')->first();
>>> $u->forceFill(['password' => Hash::make('NewStrong!Pass1')])->save();
- SPA/mobile app? Hit Fortify endpoints:
POST /forgot-password
thenPOST /reset-password
withtoken
,email
,password
,password_confirmation
.
How Laravel Password Reset Works
- Password Broker: The broker manages tokens and emails. Use
Password::broker()
; configure brokers inconfig/auth.php
underpasswords
. - Tokens Table: Modern Laravel stores hashed tokens in the
password_reset_tokens
table withemail
,token
, andcreated_at
columns. - Expiry & Throttle: The token lifetime and request throttling are broker-specific in
config/auth.php
(e.g.,expire
in minutes,throttle
between requests). Defaults are commonly 60 minutes for expiry. - Notifications: Reset emails are sent via
Illuminate\Auth\Notifications\ResetPassword
. You can customize the email content, URL, and mailer.
Method 1: UI Flow with Breeze/Jetstream (Blade/Livewire/Inertia)
If your app already has authentication scaffolding, you often get a ready‑made Forgot Password page and a Reset Password form.
A) Install Breeze (Blade example)
composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install && npm run build
This scaffolds routes like /forgot-password
and /reset-password
with controllers and views.
B) Configure Mail
APP_URL=https://your-app.test
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=xxxx
MAIL_PASSWORD=xxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@your-app.test
MAIL_FROM_NAME="Your App"
Tip: In local development, use log or mailtrap; in production, use a real SMTP (or Postmark, SES, Mailgun).
C) Use the Flow
- Visit
/forgot-password
, enter your email. - Click the link from your inbox.
- Set a new password on
/reset-password
.
D) Customize the Reset Email
In App\Providers\AuthServiceProvider
:
use Illuminate\Auth\Notifications\ResetPassword;
public function boot(): void
{
ResetPassword::toMailUsing(function ($notifiable, $token) {
$url = url(route('password.reset', [
'token' => $token,
'email' => $notifiable->getEmailForPasswordReset(),
], false));
return (new \Illuminate\Notifications\Messages\MailMessage)
->subject('Reset your password')
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', $url)
->line('This link will expire in 60 minutes.')
->line('If you did not request a password reset, no further action is required.');
});
}
Jetstream users (Livewire or Inertia) get the same functionality, powered by Fortify under the hood.
Method 2: API Flow with Laravel Fortify (for SPA/Mobile)
Fortify exposes endpoints you can call from your SPA or mobile client.
Endpoints (default)
POST /forgot-password
– Body:{ email }
. Sends email with reset link.POST /reset-password
– Body:{ token, email, password, password_confirmation }
.
Example cURL
# Request a reset link
curl -X POST https://your-app.test/forgot-password \
-H "Accept: application/json" \
-d "email=user@example.com"
# Reset the password
curl -X POST https://your-app.test/reset-password \
-H "Accept: application/json" \
-d "email=user@example.com" \
-d "token=PASTE_TOKEN_FROM_EMAIL" \
-d "password=NewStrong!Pass1" \
-d "password_confirmation=NewStrong!Pass1"
Using Sanctum for SPA? Ensure you first hit
/sanctum/csrf-cookie
to prime CSRF cookies for stateful requests in browsers.
Method 3: Admin/CLI Reset Without Email (Tinker)
Perfect when the user has no mailbox access or email is misconfigured.
php artisan tinker
>>> use App\Models\User; use Illuminate\Support\Facades\Hash;
>>> $u = User::where('email','user@example.com')->first();
>>> $u->forceFill(['password' => Hash::make('NewStrong!Pass1')])->save();
Force password change on next login (optional design): add a boolean column must_change_password
and redirect those users to a change‑password screen after login.
Avoid updating the password column with plain text. Always hash with
Hash::make()
.
Method 4: Programmatically Send a Reset Link (Controllers/Jobs)
Trigger reset links from an admin panel or background job.
use Illuminate\Support\Facades\Password;
public function sendLink(string $email)
{
$status = Password::sendResetLink(['email' => $email]);
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
Generate the token yourself and craft a custom URL/email:
use Illuminate\Support\Facades\Password;
$token = Password::createToken($user);
$url = url(route('password.reset', ['token' => $token, 'email' => $user->email], false));
// Send $url via your own Mailable/Notification
Method 5: Multi‑Auth Setups (Users/Admins) & Custom Brokers
Define separate brokers for different guards (e.g., users
, admins
). In config/auth.php
:
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'password_reset_tokens',
'expire' => 30,
'throttle' => 60,
],
],
Use a specific broker:
Password::broker('admins')->sendResetLink(['email' => $adminEmail]);
If admins live in a different table/model/provider, ensure the providers
section is configured and your admin model implements CanResetPassword
(via Illuminate\Auth\Passwords\CanResetPassword
trait or Jetstream/Fortify conventions).
Troubleshooting & Common Errors
- No email received
- Check
.env
mail settings andAPP_URL
. - In local, set
MAIL_MAILER=log
and inspectstorage/logs/laravel.log
. - If notifications are queued, make sure a worker is running:
php artisan queue:work
. - Verify
MAIL_FROM_ADDRESS
is allowed by your SMTP provider (SPF/DKIM configured in DNS).
- Check
- Token expired / invalid
- Tokens expire based on broker
expire
value (e.g., 60 minutes). Generate a new link. - Deleting old rows from
password_reset_tokens
can help clean up; Laravel manages this when generating new tokens.
- Tokens expire based on broker
- 419 Page Expired (CSRF)
- Happens in browser forms if CSRF token or session is stale.
- Ensure your frontend uses
@csrf
in forms or hits/sanctum/csrf-cookie
for SPA.
- Wrong link domain or HTTP/HTTPS mismatch
- Fix
APP_URL
and any URL generation logic. Use HTTPS in production.
- Fix
- Multi‑auth using wrong broker
- Calls to
Password::broker()
must match the provider/guard for that user type.
- Calls to
- Horizon/queues not processing
- Check queue name. By default, notifications go to the
default
queue unless overridden inNotification::viaQueues()
.
- Check queue name. By default, notifications go to the
- Missing tokens table
- Ensure you have a migration for
password_reset_tokens
. If using starter kits, runphp artisan migrate
after installation. Otherwise, create a migration withemail
,token
,created_at
.
- Ensure you have a migration for
Security Best Practices
- Use strong passwords and enforce validation rules (length, mixed case, numbers, symbols).
- Keep token expiry short (e.g., 60 minutes) and throttle reset requests.
- Do not disclose whether an email exists—return a generic success message.
- Force re‑authentication for sensitive actions after password changes.
- Invalidate other sessions/devices after a password reset (
Auth::logoutOtherDevices()
if desired). - Log password reset events for auditing, but never log tokens or passwords.
FAQs
How long is a Laravel password reset link valid?
Whatever you set in config/auth.php
under the broker’s expire
key (commonly 60 minutes).
Where are reset tokens stored?
In the password_reset_tokens
table; Laravel stores a hashed token, not the raw token.
Can I reset a password without email access?
Yes—use Tinker (CLI) to set a new hashed password or send a reset link programmatically as an admin.
How do I support separate user types (admins, customers)?
Create multiple brokers in config/auth.php
and call Password::broker('your-broker')
.
What about SPA/mobile apps?
Use Fortify’s /forgot-password
and /reset-password
endpoints; handle CSRF for browser-based SPAs.
Copy‑Paste Snippets
Tinker one‑liner:
php artisan tinker --execute="App\\Models\\User::where('email','user@example.com')->first()->forceFill(['password'=>Illuminate\\Support\\Facades\\Hash::make('NewStrong!Pass1')])->save();"
Send reset link in a controller:
$status = Password::sendResetLink(['email' => request('email')]);
return $status === Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withErrors(['email' => __($status)]);
Generate token & build URL:
$token = Password::createToken($user);
$url = url(route('password.reset', ['token' => $token, 'email' => $user->email], false));
Fortify reset (controller example):
public function reset(Request $request)
{
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed|min:8',
]);
$status = Password::reset(
$request->only('email','password','password_confirmation','token'),
function ($user, $password) {
$user->forceFill([
'password' => Hash::make($password),
])->save();
event(new \Illuminate\Auth\Events\PasswordReset($user));
}
);
return $status === Password::PASSWORD_RESET
? response()->json(['message' => __($status)])
: response()->json(['message' => __($status)], 422);
}
Bonus: Add an Admin “Send Reset Link” Button
- Create an admin action that takes a user’s email and calls
Password::broker('users')->sendResetLink()
. - Show a neutral success message (don’t reveal whether the address exists).
- Log the action (user id, admin id, timestamp), but never the token.
Conclusion
With Laravel’s brokers, starter kits (Breeze/Jetstream), and Fortify endpoints, you have multiple reliable paths to recover from a forgotten password—whether you’re an end user, a support admin, or a developer maintaining a multi‑auth system. Start with the UI flow, fall back to API or CLI when needed, and keep things secure with short expiries, throttling, and solid SMTP/queue setups.
Responses (0 )