What is a Signed URL in Laravel?

A Signed URL in Laravel is a secure URL that contains a signature hash. Laravel verifies this signature to ensure the URL:

  • Was not modified
  • Is valid
  • Has not expired (for temporary signed URLs)

This is useful when you want to allow access to specific routes without authentication, but still keep them secure.

Laravel provides:

  • URL::signedRoute()
  • URL::temporarySignedRoute()
  • hasValidSignature()
  • signed middleware

Why Use Signed URLs?

Signed URLs are commonly used for:

  • Email verification links
  • Passwordless login links
  • Secure file downloads
  • Unsubscribe email links
  • Payment success verification
  • Invitation systems
  • Temporary document access

How Signed URLs Work

Laravel adds two parameters to the URL:

?expires=1746680000&signature=xxxxxxxx

Laravel then checks:

  • Is the signature correct?
  • Has the URL expired?
  • Was the URL modified?

If validation fails → Laravel returns 403 Invalid Signature.

Basic Signed URL Example

Route

Route::get('/profile/{id}', function ($id) {
return "Secure Profile Access: " . $id;
})->name('profile.view');

Generate Signed URL

use Illuminate\Support\Facades\URL;

$url = URL::signedRoute('profile.view', [
'id' => 10
]);

echo $url;

Generated URL:

http://example.com/profile/10?signature=abc123

Validate Signed URL

Method 1 — Inside Controller

use Illuminate\Http\Request;

public function show(Request $request, $id)
{
if (! $request->hasValidSignature()) {
abort(403, 'Invalid or Expired URL');
}

return "Valid Signed URL for User " . $id;
}

Method 2 — Using Middleware (Recommended)

Route::get('/profile/{id}', function ($id) {
return "Secure Profile";
})->middleware('signed');

Laravel automatically validates the signature.

Temporary Signed URL Example

Temporary signed URLs expire automatically.

Perfect for:

  • Download links
  • One-time access
  • Secure invitations

Generate Temporary Signed URL

use Illuminate\Support\Facades\URL;
use Carbon\Carbon;

$url = URL::temporarySignedRoute(
'download.file',
Carbon::now()->addMinutes(30),
['file' => 15]
);

echo $url;

This link expires after 30 minutes.

Route

Route::get('/download/{file}', function ($file) {
return "Downloading File ID: " . $file;
})->name('download.file')->middleware('signed');

Real-Time Example 1 — Email Verification

Laravel uses signed URLs internally for email verification.

Generate Verification Link

$url = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
[
'id' => $user->id,
'hash' => sha1($user->email),
]
);

Verification Route

Route::get('/email/verify/{id}/{hash}', function () {
return "Email Verified";
})->middleware(['signed'])->name('verification.verify');

Real-Time Example 2 — Secure PDF Download

Allow users to download invoices without login.

Generate URL

$url = URL::temporarySignedRoute(
'invoice.download',
now()->addMinutes(10),
['invoice' => 101]
);

Route

Route::get('/invoice/{invoice}', function ($invoice) {

$path = storage_path('app/invoices/sample.pdf');

return response()->download($path);

})->middleware('signed')->name('invoice.download');

Real-Time Example 3 — Passwordless Login

Users click an email link and login automatically.

Generate Login Link

$url = URL::temporarySignedRoute(
'magic.login',
now()->addMinutes(5),
['user' => $user->id]
);

Route

use Illuminate\Support\Facades\Auth;

Route::get('/magic-login/{user}', function ($user) {

$user = App\Models\User::findOrFail($user);

Auth::login($user);

return redirect('/dashboard');

})->middleware('signed')->name('magic.login');

Real-Time Example 4 — Unsubscribe Email Link

Very common in email marketing systems.

Generate Link

$url = URL::temporarySignedRoute(
'unsubscribe',
now()->addDays(7),
['email' => $subscriber->email]
);

Route

Route::get('/unsubscribe', function (Request $request) {

$email = $request->email;

// Update subscriber status

return "You have unsubscribed.";

})->middleware('signed')->name('unsubscribe');

Real-Time Example 5 — Invitation System

Invite users securely to a project/team.

Generate Invitation Link

$url = URL::temporarySignedRoute(
'team.invite',
now()->addDays(2),
[
'team' => 5,
'email' => 'user@example.com'
]
);

Route

Route::get('/team/invite', function (Request $request) {

return "Invitation Accepted";

})->middleware('signed')->name('team.invite');

Ignoring Parameters in Signature Validation

Sometimes frontend apps add extra query parameters.

Laravel allows ignoring them.

Example

if (! $request->hasValidSignatureWhileIgnoring(['page'])) {
abort(403);
}

Custom Invalid Signature Page

In bootstrap/app.php

use Illuminate\Routing\Exceptions\InvalidSignatureException;

->withExceptions(function ($exceptions) {

$exceptions->render(function (
InvalidSignatureException $e,
$request
) {

return response()->view(
'errors.link-expired',
[],
403
);

});

})

Security Best Practices

Always Use Temporary Signed URLs

Prefer:

URL::temporarySignedRoute()

Instead of permanent signed URLs.

Use HTTPS

Signed URLs should always be served over HTTPS.

Keep Expiration Short

Examples:

  • 5 minutes
  • 15 minutes
  • 1 hour

Avoid long expiration periods.

Never Expose Sensitive Data

Bad:

/user?password=123456

Good:

/user?id=10

Common Errors

1. Invalid Signature

Causes:

  • URL modified
  • Wrong APP_URL
  • Missing parameters

2. Expired Link

Temporary signed URL expired.

3. APP_URL Mismatch

Ensure .env has correct URL:

APP_URL=https://yourdomain.com

Difference Between Signed URL and Auth Middleware

FeatureSigned URLAuth Middleware
Requires LoginNoYes
Secure AccessYesYes
Expiration SupportYesNo
Email Link FriendlyYesNo
Temporary AccessExcellentLimited

Best Use Cases

Use CaseRecommended
Email Verification
File Downloads
Magic Login
Payment Callback
Public API Security
Admin Dashboard