Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Jacotheron's avatar

How to approach streaming a video only to logged in users

In essence I am developing an application where users will be able to buy videos (typically concerts and other shows, professionally edited). I need to ensure that only users that bought the video is able to watch it (but not download it*).

My setup: the website will run on AWS, EC2 instances (load balanced), video files (and other media) stored in S3 containers. Provisioning with Laravel Forge.

Typically I would approach this by having Laravel simply check with middleware and a controller that everything is fine before starting to stream the content of the file with the correct headers. I am however worried that due to the large file sizes (possibly a few GB for a show), and the fact that a request is tied up for a long time, it may prove to be inefficient and reduce the number of people that may be able to view a video at the same time.

Obviously if I don't secure the video file's location, someone might be able to access it directly for download.

Is there a way to have the S3 container query the application to determine if the person accessing it, may receive the file? How are similar applications built?

*As to the download restriction. In essence I want to make it hard for someone to just download the video (and possibly distribute as illegal copies). I realize the fact that someone should be able to watch it, means they will get the content and it can't be 100% secured. That said, I recently came across a website, which had videos, where I was unable to download it, even though they used the video html tag (somehow the url expired immediately, but the video was able to play) -> unfortunately I do not remember the website, and my history was lost so no way to get back to it. In essence I want something where someone with novice skills, still can't access the video directly.

0 likes
5 replies
Niush's avatar

Instead of streaming file through Laravel, you should generate a temporary S3 URL of the video file. And, let AWS handle the streaming directly to the browser.

https://laravel.com/docs/9.x/filesystem#temporary-urls

Regarding the protection, probably the website you saw, had blob in video tag, which essentially is streaming a video chunk by chunk. Using services like this:

https://docs.aws.amazon.com/AmazonS3/latest/userguide/tutorial-s3-cloudfront-route53-video-streaming.html

The video is not protected. Tools like VLC can easily stream and download them.

Jacotheron's avatar

Thank you very much for this information It is exactly what I needed.

martinbean's avatar
Level 80

@jacotheron Hi! So, I run a video on demand platform that does exactly what you’ve described (premium videos that users need a subscription or active rental to be able to watch).

Unfortunately, temporary signed URLs aren’t going to cut it. If you’re just generating a temporary URL for an MP4 file, then that MP4 file is sent to the browser and the user can just right-click on the <video> element and download the video. It’s also probably going to be a poor experience if you’re trying to download an MP4 on anything less than a nice, fast broadband connection so you’ll want to look into creating HLS playlists for your videos.

A HLS playlist is where the video is broken in segments, and also encoded at various bitrates. For example, if you’ve ever watched a video on YouTube or Netflix and saw the quality dip for a few seconds, it’s because the stream has switched to a lower bitrate due to low bandwidth or network congestion. The player will temporarily switch to a lower-bitrate stream to avoid stuttering or buffering.

For protecting streams, you have a couple of options. I did use AWS Elastic Transcoder and started looking at MediaElements, but it got hella-expensive and was also hella-slow as MediaElements created each encoding synchronously rather than asynchronously, so I was waited absolutely ages for a single video to be processed. In the end, I moved my transcoding pipeline to Mux. So now users of my platform still upload files and those raw files still go in a bucket in my S3 account, but then they’re picked up and processed by Mux. I then get a webhook from Mux telling me when a video is ready (or if it failed). Mux then allow you to create URLs signed with JWTs that can generate protected stream URLs that are also restricted by domain.

If you’re interested in building a protected video streaming website like I’ve described above then I am actually in the process of recording a course to build just that, which should be ready in the next month or so. It’ll build essentially a Netflix or Laracasts, using Mux for processing video files and Stripe for subscriptions.

I have a mailing list set up for it if it sounds of interest and you’d like to subscribe to updates: https://twitter.com/martinbean/status/1490707910963433474

Jacotheron's avatar

@martinbean This is very valuable information you gave here. You described the route I was planning on using (basically have only 3 quality levels for video to not take up too much space etc).

Typically the videos for my client's platform would get initial interest, but it would die down quickly from there (think a production company that made a video of a school concert - only the parents are interested). Due to being concerts, the video length is quite long (typically around 2 hours), however after a few weeks it would only get a few views per month. A feature that we wanted from the start, is that after a while, the video is made available to download by those who bought it (in essence this platform is to replace what DVDs did previously - being cheap way to distribute the concerts, where the buyers actually get to own the concert; but reduce risk of piracy/copying while it is still being sold). On the platform the people will buy the show containing the video, and then can view it.

That said, Mux seems like a very good option. I am still going through all of the documentation to ensure it will work as we expect, but I also wrote a very simple Laravel wrapper for it (https://packagist.org/packages/jacotheron/laravel-mux) - my first package, but pull requests are welcome. In short it registers a config file (where the api key and secret can be pulled from the ENV file), and then wrap the api calls into 2 service files (videos and data). These services are mostly based on the examples from Mux, but rewritten to allow simple chaining of methods - no need for a lot of variables in your code, just to set everything up.

I have subscribed to your mailing list and look forward to your course.

Thank you once again.

martinbean's avatar

@Jacotheron No problem! Mux should be able to help with that.

If you did want to offer downloads then I imagine you’d be able to prepare an MP4 download but also inject some metadata, i.e. a user ID, so that if videos did end up somewhere you didn’t want them you’d be able to trace it back to a specific customer.

Please or to participate in this conversation.