Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel URL Signer
https://github.com/spatie/laravel-url-signer
Admin
Laravel URL Signer creates signed URLs with expiration timestamps for protecting routes and creating
...
Tokens:
3,955
Snippets:
31
Trust Score:
8.5
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
97.2
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel URL Signer Laravel URL Signer is a package that creates URLs with a limited lifetime by adding an expiration timestamp and a cryptographic signature. This enables secure, time-limited access to protected resources without requiring user authentication, making it ideal for temporary download links, email verification URLs, and cross-application secure communication. Unlike Laravel's native route signing, this package allows signing any URL (not just routes in your app), uses a separate signing secret independent of the app key, and enables easy URL sharing between different applications. The package provides a facade for signing and validating URLs, middleware for protecting routes, and an artisan command for generating signature keys. ## Installation Install the package via Composer and generate a signature key for secure URL signing. ```bash # Install the package composer require spatie/laravel-url-signer # Generate a signature key (adds URL_SIGNER_SIGNATURE_KEY to .env) php artisan generate:url-signer-signature-key # Or display the key without modifying files php artisan generate:url-signer-signature-key --show # Skip if key already exists php artisan generate:url-signer-signature-key --always-no # Force overwrite existing key php artisan generate:url-signer-signature-key --force # Optionally publish the config file php artisan vendor:publish --tag="url-signer-config" ``` ## Configuration Configure the signature key, default expiration time, and URL parameter names in the config file. ```php // config/url-signer.php return [ /* * This string is used to generate a signature. You should * keep this value secret. */ 'signature_key' => env('URL_SIGNER_SIGNATURE_KEY'), /* * The default expiration time of a URL in seconds. * Default: 24 hours (60 * 60 * 24 = 86400 seconds) */ 'default_expiration_time_in_seconds' => 60 * 60 * 24, /* * These strings are used as parameter names in a signed URL. */ 'parameters' => [ 'expires' => 'expires', 'signature' => 'signature', ], ]; ``` ## UrlSigner::sign() Signs a URL by adding expiration and signature query parameters. Accepts an optional expiration time as a DateTime object or integer (seconds). Returns the signed URL string with `expires` and `signature` parameters appended. ```php use Spatie\UrlSigner\Laravel\Facades\UrlSigner; // Sign with default expiration (24 hours from config) $signedUrl = UrlSigner::sign('https://myapp.com/protected-route'); // Output: https://myapp.com/protected-route?expires=1699123456&signature=abc123... // Sign with custom expiration using Carbon/DateTime $signedUrl = UrlSigner::sign('https://myapp.com/protected-route', now()->addDays(30)); // Valid for 30 days $signedUrl = UrlSigner::sign('https://myapp.com/protected-route', now()->addMinutes(5)); // Valid for 5 minutes $signedUrl = UrlSigner::sign('https://myapp.com/protected-route', now()->addHours(2)); // Valid for 2 hours // Sign with expiration in seconds (integer) $signedUrl = UrlSigner::sign('https://myapp.com/protected-route', 300); // Valid for 300 seconds (5 minutes) $signedUrl = UrlSigner::sign('https://myapp.com/protected-route', 60 * 60); // Valid for 1 hour // Sign with a custom signature key (for cross-app signing) $signedUrl = UrlSigner::sign( 'https://external-app.com/webhook', now()->addHours(1), 'shared-secret-key-between-apps' ); // Sign URLs with existing query parameters $signedUrl = UrlSigner::sign('https://myapp.com/download?file=report.pdf', now()->addMinutes(30)); // Output: https://myapp.com/download?file=report.pdf&expires=1699123456&signature=abc123... ``` ## UrlSigner::validate() Validates a signed URL by checking if the signature matches and the URL has not expired. Returns `true` if the URL is valid, `false` if the signature is invalid or the URL has expired. ```php use Spatie\UrlSigner\Laravel\Facades\UrlSigner; // Basic validation $isValid = UrlSigner::validate('https://myapp.com/protected-route?expires=1699123456&signature=abc123'); // Returns: true or false // Validate in a controller public function download(Request $request) { $isValid = UrlSigner::validate($request->fullUrl()); if (!$isValid) { abort(403, 'Invalid or expired URL'); } // Proceed with download... return response()->download($file); } // Validate with a custom signature key $signedUrl = 'https://external-app.com/webhook?expires=1699123456&signature=abc123'; $isValid = UrlSigner::validate($signedUrl, signatureKey: 'shared-secret-key-between-apps'); // Returns: true if signed with the same key $isWrong = UrlSigner::validate($signedUrl, signatureKey: 'wrong-secret'); // Returns: false // Check expiration behavior $signedUrl = UrlSigner::sign('https://spatie.be', now()->addSeconds(2)); sleep(1); UrlSigner::validate($signedUrl); // Returns: true (still valid) sleep(2); UrlSigner::validate($signedUrl); // Returns: false (expired) ``` ## ValidateSignature Middleware Protects routes by automatically validating the signed URL. Returns a 403 Forbidden response if the URL signature is invalid or expired. Can be extended to customize the error handling behavior. ```php // Register middleware in app/Http/Kernel.php (Laravel 10 and below) protected $routeMiddleware = [ // ... other middleware 'signed-url' => \Spatie\UrlSigner\Laravel\Middleware\ValidateSignature::class, ]; // Or in bootstrap/app.php (Laravel 11+) ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'signed-url' => \Spatie\UrlSigner\Laravel\Middleware\ValidateSignature::class, ]); }) // Apply middleware to routes Route::get('protected-route', fn () => 'Hello secret world!') ->middleware('signed-url'); Route::get('download/{file}', [DownloadController::class, 'show']) ->middleware('signed-url') ->name('download'); // Group protected routes Route::middleware('signed-url')->group(function () { Route::get('secure-file/{id}', [FileController::class, 'download']); Route::get('verify-email/{user}', [EmailController::class, 'verify']); Route::get('reset-password/{token}', [PasswordController::class, 'reset']); }); // Generate signed URLs for protected routes $downloadUrl = UrlSigner::sign(route('download', ['file' => 'report.pdf']), now()->addHours(24)); // User can access this URL for 24 hours // Custom middleware extending ValidateSignature namespace App\Http\Middleware; use Spatie\UrlSigner\Laravel\Middleware\ValidateSignature; class CustomSignatureValidation extends ValidateSignature { protected function handleUnsignedUrl($request) { // Custom handling instead of abort(403) return redirect()->route('login') ->with('error', 'This link has expired. Please request a new one.'); } } ``` ## Cross-Application URL Signing Sign and validate URLs between different applications using a shared secret key, enabling secure communication without exposing internal authentication mechanisms. ```php use Spatie\UrlSigner\Laravel\Facades\UrlSigner; // Application A: Generate signed URL for Application B $sharedSecret = config('services.app_b.signing_secret'); $webhookUrl = UrlSigner::sign( 'https://app-b.com/api/webhook/order-created', now()->addMinutes(5), $sharedSecret ); // Send webhook to Application B Http::post($webhookUrl, [ 'order_id' => 12345, 'amount' => 99.99, ]); // Application B: Validate incoming webhook public function handleWebhook(Request $request) { $sharedSecret = config('services.app_a.signing_secret'); if (!UrlSigner::validate($request->fullUrl(), signatureKey: $sharedSecret)) { return response()->json(['error' => 'Invalid signature'], 403); } // Process the webhook... $data = $request->all(); return response()->json(['status' => 'received']); } // Sign external download links $fileServiceSecret = config('services.file_service.secret'); $signedDownloadUrl = UrlSigner::sign( "https://files.example.com/download/{$fileId}", now()->addHours(1), $fileServiceSecret ); return view('files.show', ['downloadUrl' => $signedDownloadUrl]); ``` ## Practical Use Cases Generate temporary download links, email verification URLs, and password reset links with automatic expiration for enhanced security. ```php use Spatie\UrlSigner\Laravel\Facades\UrlSigner; // Temporary file download link (valid for 1 hour) class FileController extends Controller { public function generateDownloadLink(File $file) { $signedUrl = UrlSigner::sign( route('files.download', $file), now()->addHour() ); return response()->json(['download_url' => $signedUrl]); } public function download(File $file) { // Route is protected by 'signed-url' middleware return response()->download(storage_path("app/{$file->path}")); } } // Email verification link (valid for 24 hours) class EmailVerificationController extends Controller { public function sendVerificationEmail(User $user) { $verificationUrl = UrlSigner::sign( route('verify.email', ['user' => $user->id]), now()->addDay() ); Mail::to($user)->send(new VerifyEmail($verificationUrl)); } } // Password reset link (valid for 60 minutes) class PasswordResetController extends Controller { public function sendResetLink(Request $request) { $user = User::where('email', $request->email)->first(); $token = Str::random(60); $resetUrl = UrlSigner::sign( route('password.reset', ['token' => $token, 'email' => $user->email]), now()->addMinutes(60) ); Mail::to($user)->send(new ResetPasswordMail($resetUrl)); } } // Shareable content link (valid for 7 days) $shareableLink = UrlSigner::sign( route('content.shared', ['id' => $content->id]), now()->addWeek() ); // API endpoint with temporary access $temporaryApiAccess = UrlSigner::sign( "https://api.myapp.com/reports/{$reportId}/export", now()->addMinutes(15) ); ``` ## Summary Laravel URL Signer provides a robust solution for creating time-limited, cryptographically signed URLs in Laravel applications. The primary use cases include protecting sensitive downloads, generating secure email verification and password reset links, creating shareable content URLs with automatic expiration, and enabling secure cross-application communication through signed webhooks and API calls. Integration is straightforward: install the package, generate a signature key, and use the `UrlSigner` facade to sign and validate URLs. For route protection, register the `ValidateSignature` middleware and apply it to any routes requiring signed access. The package seamlessly integrates with Laravel's routing system, supports custom signature keys for multi-application scenarios, and allows customization of expiration times and URL parameter names through the configuration file.