You are correct: Laravel Fortify registers its own routes internally, and by default, the email verification route uses the signed middleware, which expects an absolute URL signature. When you proxy or change the host, the signature validation fails.
To solve this, you need to override the default Fortify email verification route and register your own with the signed:relative middleware.
Here’s how you can do it:
1. Disable Fortify's Default Email Verification Routes
In your FortifyServiceProvider, inside the boot method, you can disable Fortify's default routes by setting the FORTIFY_ROUTES environment variable to false before Fortify registers its routes:
public function boot()
{
// Disable Fortify's default routes
Fortify::ignoreRoutes();
// Register your custom routes here...
}
2. Register Your Own Email Verification Route
Now, in your routes/web.php or a custom routes file, register the verification route yourself, using signed:relative:
use Laravel\Fortify\Features;
use Laravel\Fortify\Http\Controllers\VerifyEmailController;
if (Features::enabled(Features::emailVerification())) {
Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
->middleware([
config('fortify.auth_middleware', 'auth').':'.config('fortify.guard'),
'signed:relative',
'throttle:6,1',
])
->name('verification.verify');
}
- Adjust the
throttlemiddleware as needed (the default is6,1for 6 attempts per minute).
3. Make Sure the Frontend Generates Relative Signed URLs
When generating the verification URL, ensure you use Laravel’s URL::temporarySignedRoute() with a relative path. If you’re generating the link in the backend, you can do:
$url = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->getKey(), 'hash' => sha1($user->getEmailForVerification())]
);
This will generate a URL that works with the signed:relative middleware.
4. Clear Route Cache
If you’re using route caching, don’t forget to clear it:
php artisan route:clear
Summary:
- Call
Fortify::ignoreRoutes()in your service provider. - Register your own
/email/verify/{id}/{hash}route with thesigned:relativemiddleware. - Ensure your frontend or backend generates relative signed URLs.
This will allow your email verification to work correctly even when proxied or accessed via a different host.
Let me know if you need a full code example or further clarification!