Show me the code where the expires is included in the signature?
You won't find anything like that...
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I'm playing with Laravel's signed URLs and they are awesome. I really like the feature of expiring signed URLs, but there's one thing that I'd like to change.
An example of a signed URL with expiration time is:
/foo?expires=1634335939&signature=31ee9463d1f3d0584d435e6bde2bfa66d7dcde3ee1f998b270e62d1a7584f356
I'm not sure why it was decided to include the expires in the query string, I don't like that. This value is being included in the signature anyway (obviously), so showing it to the public doesn't make much sense and I'd prefer to keep it private.
Can temporarySignedRoute() be configured to hide the expires query param?
Show me the code where the expires is included in the signature?
You won't find anything like that...
@MichalOravec I mean that the signature validates the expiration time, so you can't tamper with it by editing the timestamp in the URL.
@MichalOravec Theexpires query param is included in the signature hash
public function signedRoute($name, $parameters = [], $expiration = null, $absolute = true)
{
$this->ensureSignedRouteParametersAreNotReserved(
$parameters = Arr::wrap($parameters)
);
if ($expiration) {
//here it is added to the $parameters
$parameters = $parameters + ['expires' => $this->availableAt($expiration)];
}
ksort($parameters);
$key = call_user_func($this->keyResolver);
// and here the URL & query parameters are hashed
return $this->route($name, $parameters + [
'signature' => hash_hmac('sha256', $this->route($name, $parameters, $absolute), $key),
], $absolute);
}
@tykus small correction: it's not a hash. It's encrypted.
@RoboRobok really, can you decrypt it?
@RoboRobok you didnt believe me whenever I posted the same code???
@tykus I've been watching a movie when you posted, so didn't look at it. Please don't kill me.
@tykus Yes, you're right, when I looked at this method, I was obviously blind.
@RoboRobok not trying to kill you - you called me out with incorrect information
@tykus small correction: it's not a hash. It's encrypted.
🤷♂️
@tykus I didn't call out anybody, I just really thought it was encrypted.
The expires parameter needs to be in the URL. The temporary signed route is technically the same as a normal signed route, just the expires parameter is checked by laravel. You can't get the time from the signature, the time is just a part of the URL.
If you want to hide it, you need to do it by yourself. e.g. store the hash with the expire time in the database and set there an expire date. But out of the box you need the parameter
@Funfare you actually can get the expiration timestamp back, because the signature is encrypted, not hashed.
@RoboRobok not encrypted, hashed.
Laravel allows you to easily create "signed" URLs to named routes. These URLs have a "signature" hash appended to the query string which allows Laravel to verify that the URL has not been modified since it was created.
@RoboRobok There you are wrong. Look into the code, laravel generates a hash and is not encypting it
return $this->route($name, $parameters + [
'signature' => hash_hmac('sha256', $this->route($name, $parameters, $absolute), $key),
], $absolute);
hash_hmac is a hash function, so you cannot decrypt it.
@Funfare okay, I must have confused it with something else. I experimented and I got different signatures after changing the app key. And the app key is normally only used for encryption. Sorry about that. If it's hashed, then it makes more sense.
@RoboRobok hmac is a hash function based on a secret key, so this is why the signature changes when you change the app key. (and so no one else can create the hash of an url that is valid on your page)
@Funfare do you know if any other hashes in Laravel use the app key? I know that passwords don't.
@RoboRobok the app key is for encryption data.
Edit:
@jlrdw yes, that's why I got confused when I learned that signatures are being hashed with it.
Edit: I have seen that tweet a few days ago :)
okay, I must have confused it with something else. I experimented and I got different signatures after changing the app key. And the app key is normally only used for encryption. Sorry about that. If it's hashed, then it makes more sense.
@RoboRobok That’s how hashing with a salt works. Every time you hash the same plaintext value, you’ll get a different hash. Try hashing the same password: you’ll get different hashes in your database. The hash didn’t change because you changed the app key; the hash changed because it would have any way.
And it helps to have your facts right before you start calling people out and telling them they’re wrong on things like hashing versus encryption. Sounds like you could do with reading up on the basics of hashing too given you don’t seem to know the same value can produce different hashes.
Back to the question at hand, the expires parameter is required because it forms part of the signature. If it wasn’t there, then the signature would just be of the URL and would then be never-expiring. But by checking the signature, Laravel can safely assume the given expires value hasn’t been tampered with via the client (otherwise the signature wouldn’t match) and therefore rely on it as to whether the timestamp has passed and URL expired.
@martinbean you missed the point. Nobody here confuses encryption with hashing as a concept. Like I said, app key in Laravel wasn't supposed to have anything to do with hashing, yet it's used as salt for signature hashing. And that's quite strange. Why isn't salt generated similarily to password hashing in this case?
@RoboRobok what makes you say the app key is used?
@Snapey change the app key and the signature is no longer valid.
OK, I wanted to make sure you were not thinking this because you observed that when you changed the app.key and the signature changed
If using the app.key is an issue for you;
https://dyrynda.com.au/blog/customising-laravels-url-signing-key
But the point of this thread is that the signature is only making sure the rest of the URL has not been tampered with. If you want to hide the expiry in some way then use a signed URL without the expiry time and do your own check in some other way. For instance, it it were an invite to perform some action, you should be able to check when that action was created and then check the difference to current time.
@Snapey I'm a little confused about the first paragraph.
I must have confused it with something else. I experimented and I got different signatures after changing the app key.
You will get a different signature every time irrespective of changing the app.key
@Snapey hmm, I don't think so though. Can you try it and confirm? Because my yesterday's tests don't confirm it.
@RoboRobok You are correct. This is using a HMAC function which will always return the same signature given the same inputs, and is used for message authentication where both parties know the same shared secret (in this case the app.key)
https://en.wikipedia.org/wiki/HMAC
Michael's post discusses setting your own shared secret so that two apps can check the validity of the signature without sharing the app.key
@Snapey thanks for confirming. Yeah, so also having expires in the query string makes perfect sense now, as I learned that the signature is being hashed and not encrypted. Expiration date is still part of the payload for hashing (to prevent manually changing it), but we can't read it back from the signature to validate if the URL expired or not. It all makes perfect sense now.
@RoboRobok Would have made perfect sense if you had just taken 5 minutes to read the source code too, instead of constantly asking people to “confirm” whilst also incorrectly telling people they’re wrong.
@martinbean well, I also corrected some people "correctly" in this thread.
@RoboRobok Well, even a stopped watch is right twice a day…
@martinbean I see you still can't get over the discussion from my another thread.
@RoboRobok You’re the one that’s just brought it up…?
Please or to participate in this conversation.