Hi there,
a client of mine made the mistake of taking the media-hash-url of a file for generating a (static) QR-Code, that links to this file.
the url has this format:
https://www.domain.de/media/pages/page-name/a50f66a47f-1726776222/file-name.pdf
After a server move and an update to Kirby 5 the hash-url has changed, and the QR-Codes on about 10000 print-flyers no longer work. 
Is there any possibility to e.g. have a Kirby route that redirects the old media-hash-url to the permalink of the file?
Would be great if someone could help!
thanks, Matthias
No, I don’t think this is possible, because the media route cannot be overwritten.
What you could do though is to catch and redirect the url before Kirby/PHP kicks in, i.e., in your server config (or .htaccess on Apache).
Hi Matthias!
We faced a similar issue today. The problem with using permalinks here is that they still redirect to the hash URL, so visitors would see it in the address bar and inadvertently copy it for their own links. This of course could break at any time (and so it did for our client). Our solution was the following:
- Serve downloads at a stable URL (
/downloads/$filename) with a route in config.php:/** Stable download URLs */
'routes' => [
[
'pattern' => 'downloads/(:any)',
'action' => function (string $filename) {
$file = page('page://<page-uuid>')?->file($filename);
if (!$file) return false;
header('Content-Type: ' . $file->mime());
header('Content-Length: ' . $file->size());
readfile($file->root());
exit;
}
]
],
- Redirect all hash URL requests to the new stable path with
.htaccess:# redirect all media download URLs with hash to stable /downloads/ path
RewriteRule ^media/pages/page-name/[^/]+/(.+)$ /downloads/$1 [R=301,L]
This will also catch any dead links as long as the filename hasn’t changed. Unfortunately, we couldn’t use another route or Retour for this, as Kirby’s internal route for /media/(:any)/(:any)/(:any)/(:any) is prioritized (as @pixelijn pointed out).
- Additionally, you could surface these stable URLs via Panel blueprints:
text: "{{ kirby.url }}/downloads/{{ file.filename }}"
and templates:url('downloads/' . $file->filename())
But this is optional if you’re already intercepting all requests from the hash URLs.
This might be a bit overkill if only one link is affected, but we wanted to make sure this doesn’t happen again. Also be aware that this will make download links much more predictable. But this may be exactly what you want.
I hope this helps!
Best regards,
Gregor
Thanks a lot for this Great idea!