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

Walkthrough

Space play step click any line
Three takeaways
  1. 1Signing the path and expiry together with a secret key makes a URL impossible to forge or extend without that key.
  2. 2Embedding an expiry timestamp in the signed payload turns a link into a self-limiting credential.
  3. 3Comparing signatures with a constant-time function like hash_equals defends against timing attacks.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building signed, expiring download URLs in PHP in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code