php
45 lines · 7 steps
Building signed, expiring download URLs in PHP in Laravel
An HMAC-signed URL scheme that lets you hand out time-limited download links no one can tamper with.
Explained by
highlit
1<?php
2
3namespace App\Services;
4
5use Illuminate\Support\Carbon;
6
7class SignedDownloadUrl
8{
9 private string $key;
10
11 public function __construct(string $key)
12 {
13 $this->key = $key;
14 }
15
16 public function make(string $path, int $ttlSeconds = 300): string
17 {
18 $expires = Carbon::now()->addSeconds($ttlSeconds)->getTimestamp();
19
20 $payload = [
21 'path' => $path,
22 'expires' => $expires,
23 ];
24
25 $payload['signature'] = $this->sign($path, $expires);
26
27 return '/download?' . http_build_query($payload);
28 }
29
30 public function verify(string $path, int $expires, string $signature): bool
31 {
32 if (Carbon::now()->getTimestamp() > $expires) {
33 return false;
34 }
35
36 return hash_equals($this->sign($path, $expires), $signature);
37 }
38
39 private function sign(string $path, int $expires): string
40 {
41 $data = $path . '|' . $expires;
42
43 return hash_hmac('sha256', $data, $this->key);
44 }
45}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Signing the path and expiry together with a secret key makes a URL impossible to forge or extend without that key.
- 2Embedding an expiry timestamp in the signed payload turns a link into a self-limiting credential.
- 3Comparing signatures with a constant-time function like hash_equals defends against timing attacks.
Related explainers
php
<?php final class RememberMeCookie {
Signed remember-me cookies in PHP
authentication
hmac
cookies
Intermediate
8 steps
php
<?php namespace App\Http\Middleware;
Caching HTTP responses with Laravel middleware
middleware
caching
http
Intermediate
8 steps
php
<?php namespace App\Validation;
Building a reusable address form validator in PHP
validation
error-accumulation
regex
Intermediate
9 steps
php
<?php namespace App\Http\Controllers;
A cached autocomplete endpoint in Laravel
caching
validation
query-ranking
Intermediate
8 steps
php
<?php declare(strict_types=1);
Normalizing human names in PHP
unicode
text-normalization
transliteration
Intermediate
8 steps
php
<?php namespace App\Http\Controllers;
Handling Stripe webhooks in Laravel
webhooks
signature-verification
dependency-injection
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-signed-expiring-download-urls-in-php-in-laravel-explained-php-2ed2/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.