2

To achieve that unauthenticated users cannot view images just by guessing the URL (e. g. http://www.test.com/images/123.jpg I store all images outside the public directory and offer a URL that accepts the unique-id of the picture and checks whether the user is authenticated:

// Laravel code behind http://www.test.com/image/5
public function getimage($uid) {

    if(Auth::check()) {

        $filename = Picture::findorfail($uid)->filename; // e. g. '123.jpg'
        return response()->download(storage_path('images/' . $filename), null, [], null);

    } else {

        return response()->download('images/no_access.jpg', null, [], null);

    }

}

Thus, an authenticated user gets the image '123.jpg' and a non-authenticated user gets the image 'no_access.jpg' which is just a red text 'No access' on a white background.

Everything works perfect, as long as I manually hard-clear the cache of my browser (Chrome in my case) after logging out.

But if

  • I login and access the image via http://www.test.com/image/5 then I get the image '123.jpg' (correct until here)
  • then logout and call http://www.test.com/image/5 once more then I should get the 'no_access.jpg' but because of the browser cache I get the protected image '123.jpg' (cache overrides the authorization check)

I already tried <meta http-equiv="expires" content="0"> but without any success. Agian, if I hard-clear the cache, everything is perfect - but normal users wouldn't do that.

How do I tell the browser to not cache?

Thanks in advance!

Steevie
  • 630
  • 1
  • 6
  • 19
  • You can add allways a unique index, to the image links, so the browser will cache that unique index, like this `http://www.test.com/image/5?dfDf23`, but it must be different on each request. – JOUM Sep 08 '16 at 18:54
  • Splendid workaround! Thanks so much ... but I still wonder whether there is any way that the server triggers a browser-cache clearing ( e. g. upon logging out). – Steevie Sep 08 '16 at 19:06
  • `browser-cache clearing` you want to put the finger on users browser? ;) – JOUM Sep 08 '16 at 19:08
  • You could also send a header telling the browser to not cache it. Something like `header('Cache-Control: no-store, no-cache, private');` – Charlotte Dunois Sep 08 '16 at 19:10
  • @JOUM: Actually, as a user, I would be happy if my browser wouldn't cache data from a secured site. Sorry for this illusion. – Steevie Sep 08 '16 at 19:11
  • @Charlotte Dunois Ops! :) – JOUM Sep 08 '16 at 19:11
  • @JOUM huh?..... – Charlotte Dunois Sep 08 '16 at 19:13
  • @Steevie What once is on a user client is not anymore under control (maybe streams) but like C. D. above points out, you can allway try to comunicate :) – JOUM Sep 08 '16 at 19:13
  • Thank you both for the insight. Keeping fingers crossed when communicating ;-) – Steevie Sep 08 '16 at 19:16

3 Answers3

3

Try putting a random variable on the end of the url

http://www.test.com/images/123.jpg?{{rand()}}
Severian
  • 427
  • 3
  • 18
  • Splendid workaround! Thanks so much ... but I still wonder whether there is any way that the server triggers a browser-cache clearing ( e. g. upon logging out). – Steevie Sep 08 '16 at 19:06
2

You can prevent the browser from caching the images, but you have to create an .htaccess file and add the following to it:

<filesMatch "\.(jpg|png)$">
  FileETag None
  <ifModule mod_headers.c>
     Header unset ETag
     Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
     Header set Pragma "no-cache"
     Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
  </ifModule>
</filesMatch>

Hope this helps :)

Jade Kallo
  • 110
  • 1
  • 10
1

With the idea of @CharlotteDunois I did some testing and figured out that in Laravel 5.3 the following works in all my use-cases:

return response()->download(
    storage_path('images/' . $filename),
    null,
    [ 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0' ],
    null
);

The third argument represents the set of headers. Be careful to use the header-name (e. g. 'Cache-Control') as the array-key and the header-value (e. g. 'no-cache') as the array-value. There are proposed solutions on the internet that say ['Cache-Control: no-cache'] ... that is wrong! You have to use ['Cache-Control' => 'no-cache']. Good luck.

Thanks for all the input!

Steevie
  • 630
  • 1
  • 6
  • 19