Hosting and media

File Protection

Keep paid media from being downloaded or shared

If you sell content, or just don't want your media copied around the internet, this is the page to read. It explains every file protection feature in RamTV: what each one protects, what it costs your server, how it behaves with third-party storage, downloads and offline playback, and where its limits are.

You don't need all of it. Most sites pick one of the setups in the next section, turn on two or three settings, press a Test button and are done. The rest of the page explains why, so you can choose with your eyes open.

Please read this first
No website can stop someone from copying what they are allowed to play. A visitor who can watch or listen can also record their screen or their speakers. File protection stops people from downloading or sharing your files without paying. It does not stop recording. That goes for every streaming site, including the big ones.

The short version

RamTV has three pieces that work together:

  • Protect files keeps chosen media files in a private folder on your server. They can only be reached through short-lived links, which RamTV gives to visitors who are allowed to play or download them.
  • Delivery mode decides who sends those private files to the visitor: PHP, Apache or nginx. PHP works everywhere; Apache and nginx are much lighter.
  • HLS encryption cuts a file into small encrypted pieces. The pieces can sit on any storage, even a public one, because the key that unlocks them stays on your server and is only given to visitors who are allowed to play.

How each setup holds up:

Your setupCan someone download the file without paying?Server loadGood for
Normal file, your server, Protect files OffYes, anyone who has the linkLowestFree content
Normal file, your server, Protect files On, PHP deliveryNoHigh: PHP streams every byteSmall sites and audio
Normal file, your server, Protect files On, Apache or nginx deliveryNoLowSelling content from your own server
Normal file on third-party storageYes: storage links are publicNone on your serverFree content you want off your server
HLS encrypted, on your server or third-party storageThe pieces can be copied, but they don't play without the keyLowSelling content from third-party storage
  • Free content only: leave Protect files Off. Use any storage.
  • Selling content, files on your own server: set Protect files to Premium ( priced ) files only or All files, then switch delivery to Apache signed links or nginx secure_link and press Test.
  • Selling content, files on third-party storage: turn on HLS Encryption. The encrypted pieces go to your storage, and the keys stay on your server.
  • Mixed: HLS for large videos, Protect files for everything else. The two work side by side.

Protect files

Open Admin → Setting → Files → Upload Setting. The Audio group and the Video group each have a Protect files setting with three choices:

ChoiceWhat it protects
OffNothing. Files are stored and served normally. This is the default.
Premium ( priced ) files onlyMedia files of items that have a price at the moment the file is processed. With the TV Shows plugin, an episode counts as priced when the episode itself or its TV show has a price.
All filesEvery uploaded audio or video source, priced or free. Use this when access depends on something other than a price, such as a subscription plan or a user role.

Protected files are moved into a private folder, files/protected, on your own server. Nobody can open a file there directly. A visitor gets a link only when RamTV has checked that they can play or download the item, and the link stops working after a while.

Good to know

  • The decision is made once, when the file is processed. If you add a price to an item later, the files it already has stay unprotected. Changing the setting later doesn't move files that were already uploaded, in either direction. Files that are already protected stay protected and keep playing.
  • Only media sources are protected. Covers, avatars, ads videos and page-builder media are never moved into the private folder, whatever you choose.
  • Previews are never protected. They are meant to be free. Trailers are never protected either.
  • HLS encrypted files are not moved. Their protection is the key. See the HLS section below.
  • Protected files always stay on your server, even if you have set a third-party storage for audio or video. Plan your disk space for that.

Different rules for different item types

Further down the same Upload Setting page there is a group for every item type that has media sources, named after the item and the file, for example “… Source - File - video”. Each group has its own Protect files rule. Leave it on Default to follow the Audio or Video setting, or pick a different choice for just that item type.

How protected files reach visitors

Once a file is protected, something has to check each visitor's link and send the file. You choose what in Admin → Setting → Files → Upload Setting → Protected files → Serve protected files with.

ModeWho sends the fileServer needsStream links lastDownload links last
PHP ( protector.php )PHP, for the whole length of the streamNothing extra. Works everywhere.20 minutes, extended while playing. Locked to the visitor's browser.24 hours. Locked to the visitor's IP address and browser.
Apache signed linksApacheApache 2.4.26 or newer with mod_rewrite and mod_version, the shipped .htaccess file, and the cronjobTwice the length of the media, at least 3 and at most 12 hours24 hours
nginx secure_linknginxnginx's secure_link module (included in most builds) and the rules belowTwice the length of the media, at least 3 and at most 12 hours24 hours

PHP ( the default )

protector.php checks every request and streams the file itself. It needs no setup and works on every host. The cost: every visitor who is playing something holds one PHP worker for as long as they play. That's fine for audio and a handful of viewers. With long videos and many viewers at once, PHP runs out of workers and the whole site slows down. When that happens, switch to Apache or nginx.

RamTV writes a tiny token file for each link, and Apache checks it and sends the file itself, with no PHP involved. It's as fast as serving a normal file.

  • The rules are in files/protected/.htaccess, which ships with RamTV. Nothing needs to be added to your Apache configuration.
  • Links end on the first cronjob run after they expire, because the cronjob deletes expired tokens every minute. Your cronjob must run every minute (see Server setup). If the cronjob stops, links stop expiring.
  • If Apache can't check a link itself (an older Apache, or a host that disables the needed module), the same link falls back to protector.php automatically. Nothing breaks; you just don't get the speed. The Test button tells you which one answered.
  • Signed links are not tied to an IP address or browser. That's what lets Apache serve them without PHP. They are short-lived instead.

For reference, this is the shipped files/protected/.htaccess. If it goes missing, restore it exactly:

Options -Indexes
RewriteEngine On
RewriteRule ^\.sl(/|$) - [F,L]
<IfModule mod_version.c>
<IfVersion >= 2.4.26>
RewriteCond expr "%{REQUEST_FILENAME} . '?' . %{QUERY_STRING} =~ m#^(.+/protected)/[^?]*\?(?:.*&)?t=([a-f0-9]{32})(?:&|$)# && -f $1 . '/.sl/' . md5( $2 . %{REQUEST_URI} )"
RewriteRule ^ - [L]
</IfVersion>
</IfModule>
RewriteCond %{QUERY_STRING} (^|&)t=[a-f0-9]{32}(&|$)
RewriteRule ^ ../../protector.php [L,QSA]
RewriteRule ^(.*)$ ../../protector.php?path=$1 [NC,L,QSA]

nginx checks each link's signature and expiry itself and sends the file. There's no token file and no cronjob involved.

  1. Select nginx secure_link and save. RamTV generates a secret the first time.
  2. The setting's tip now shows your nginx configuration with your secret filled in. Copy it into your site's server block, above the PHP location, and reload nginx.
  3. Press Test.

The configuration looks like this, with YOUR_SECRET replaced by your own secret:

location ^~ /files/protected/ {
    if ($arg_s = "") { rewrite ^/files/protected/(.*)$ /protector.php?path=$1 last; }
    secure_link $arg_s,$arg_e;
    secure_link_md5 "$secure_link_expires$uri YOUR_SECRET";
    if ($secure_link = "")  { return 403; }
    if ($secure_link = "0") { return 410; }
}
location ^~ /files/hls_keys/ {
    location ~ "^/files/hls_keys/(?<hls_key_name>[a-f0-9]{32})\.key$" { rewrite ^ /hls_key.php?name=$hls_key_name last; }
    deny all;
}
  • The second block protects HLS keys. Every nginx site needs it, whatever delivery mode you use, as soon as HLS Encryption is on.
  • If you copied this block from an earlier version of this page, replace it with the one above. The older rule never passed the key's name on to hls_key.php, so HLS items could not play on nginx.
  • An expired link answers 410 and a forged one 403.
  • Changing the secret ends every link already issued. People who are playing something just press play again.
  • nginx never reads .htaccess files. On nginx these rules are the only thing keeping both folders private. The files/protected rule is also needed in PHP mode. See Nginx for the rest of the rules RamTV needs.

The Test button

The Test button next to Serve protected files with tests the mode that's selected, before you save. It creates a throwaway file in the protected folder, fetches it through a real link, and then tries a forged link. You'll see one of these results:

  • The web server checked the link and served the file itself: all set, save.
  • The link works, but protector.php served it: safe, but still going through PHP. On Apache, check your version and modules. On nginx, check that the rules are in the right place and that nginx was reloaded.
  • A tampered link was not refused: your protected folder is not private. Don't use that mode until the test passes.
  • This server could not reach its own address: some hosts block a site from calling itself. Test by opening a protected item in your browser instead.
If the .htaccess file is missing, nothing is protected
On Apache, files/protected/.htaccess and files/hls_keys/.htaccess are what keep those folders private. Many FTP clients hide files whose names start with a dot, so they are easy to leave behind when you upload an update or move servers. After any move, check that both files exist, then press Test.

HLS encryption

HLS Encryption (Audio and Video groups in Admin → Setting → Files → Upload Setting) uses FFmpeg to cut each file into small pieces, 20 seconds for audio and 60 seconds for video, and locks every piece with AES-128 encryption. RamTV's player downloads the pieces and unlocks them as it plays. The pieces are useless without the key. Set up FFmpeg first.

How the key is protected

  • Every file gets its own key, kept on your server in files/hls_keys. The key never goes to third-party storage.
  • The key is only handed out to a visitor who was just allowed to play or download the item, and only to the same IP address they asked from. A copied key link doesn't work for anyone else.
  • Access lasts 3 hours and is extended automatically while the visitor keeps playing. Access for a download lasts 24 hours.
  • The player asks for the key again for every piece. When access ends, playback stops within about one piece.

Why HLS is the answer for third-party storage

The encrypted pieces and the playlist can go to any storage, including FTP and S3-compatible buckets that serve files publicly. Anyone can download the pieces, and nobody can play them without the key from your server. You get third-party bandwidth and protection, with nothing streamed through PHP.

The storage has to allow your site to load the playlist and pieces from the browser (CORS). See Storage. Some providers, such as Bunny, need .m3u8 and .ts allowed explicitly.

Things to know before you turn it on

  • Set your final address first. Each playlist remembers your site's address, including http:// or https://, at the moment the file was encoded, and loads its key from there. If you later change domain, keep the old domain pointing at the same site. If you move from http to https, browsers will block the old http:// key addresses. Put your site on its final domain, with HTTPS, before you encode.
  • Back up files/hls_keys. If you lose the keys, every HLS file made with them becomes unplayable, and nobody can recover them. Copy the folder whenever you move servers.
  • Only new uploads are encrypted. Turning HLS on doesn't re-encode existing files.
  • HLS files can't be downloaded as normal files. Visitors can still save them for offline playback inside your site (see below). To also offer a normal file, tick HLS Encryption - Keep real files for download. That keeps an unencrypted copy, which uses more storage. The copy is protected only if Protect files applies to it.
  • Shared IP addresses: people behind the same IP address (an office, a school, some mobile networks) share that address. Someone there who has been given the key could pass it to someone else behind the same address. This is rarely a problem in practice.
  • Changing networks: if a visitor's IP address changes while playing (for example, going from Wi-Fi to mobile data), playback may stop. Pressing play again fixes it.
  • Casting: casting to a device on the same network works, because the device uses the same public IP address.
  • Embeds: HLS items can be embedded on other sites and still play.

Third-party storage and protection

FTP and S3-compatible storages serve files from public addresses. That's what makes them cheap and fast, and it also means RamTV can't put a check in front of them. You set buckets to public when you connect them (see Storage).

  • Normal files on third-party storage are public. Anyone who has a file's address can download it. The address can be found in the browser's developer tools. That's fine for free content. For paid content it's your call; we don't recommend it.
  • Protect files overrides the storage choice. A file that gets protected is kept on your own server, even if audio or video is set to go to a storage. Your storage won't receive those files.
  • To protect files and keep them on a storage, use HLS. This is the setup we recommend.

RamTV doesn't use the storage provider's own signed links (such as S3 pre-signed links). HLS is the supported way to protect files on a storage.

Rentals

A rental gives access for a limited time. See Purchases & Rentals for how to set it up. Here's what happens to the file when time runs out:

SetupWhen the rental ends
HLS encryptedKey access ends exactly when the rental does. Playback stops within one piece, about a minute.
Protected, Apache or nginx deliveryLinks are never issued past the rental's end. Playback stops when the link expires.
Protected, PHP deliveryNo new links are issued. A stream that is already playing can continue until the viewer stops or reloads.
Not protectedThe play button goes away, but anyone who kept the file's address can still open it.

Rentals can't be downloaded, because a saved file would outlast the rental.

Downloads and offline playback

Visitors whose role allows downloads can save items for offline playback inside your site. This works with protected files in every delivery mode, and with HLS encrypted files. The saved copy lives in the browser's storage and plays in your site's player. It isn't a file on the visitor's computer.

  • Your site needs HTTPS. Browsers only allow offline storage on secure sites.
  • The visitor must have played something online in that browser at least once, so the player itself is stored for offline use.
  • Saved items keep playing when the connection drops. Once the visitor has opened your site online in that browser, it also opens with no connection at all: it starts in offline mode, where saved items can be played.
  • Download links for protected files last 24 hours.
  • A download of a protected file that stays paused for longer than that can't be resumed. Cancel it and download the item again.
  • Protected normal files can't be embedded on other sites; HLS items can.

Cloudflare and other proxies

Download links in PHP mode and HLS keys are tied to the visitor's IP address. Behind Cloudflare's proxy, every visitor seems to come from a Cloudflare address, and those addresses change between requests. If Cloudflare's proxy is on, turn on Cloudflare support (cf_cache in config_user.php, see Cloudflare). Without it, HLS playback and downloads can fail.

  • Don't add Cloudflare rules that cache /files/protected/, /files/hls_keys/, protector.php or hls_key.php. A cached copy would be served to everyone.
  • Behind any other proxy or load balancer, all visitors may share one address. Things keep working, but IP locking protects less.

What protection can't do

  • Recording: anyone who can play can record the screen or the sound. Nothing on the web stops that.
  • People who paid: someone with access can save what they play while their access lasts (a download, or a key together with the pieces) and share it. Short link lifetimes and IP binding make this harder, not impossible.
  • Kept download copies: a real file a visitor downloaded is theirs to keep. If that's a concern, don't allow downloads for that role or item.
  • DRM: RamTV doesn't include Widevine, FairPlay or PlayReady. Those need a licence from each vendor, a paid key service and a different player setup. They also prevent copying the stream, but can't prevent recording either.

For most sites, Protect files with Apache or nginx delivery, or HLS encryption, is the right balance of protection, speed and cost.

Moving servers, backups and updates

  • Back up and copy files/protected and files/hls_keys along with the rest of your files. Losing hls_keys makes HLS files unplayable.
  • Make sure files/protected/.htaccess and files/hls_keys/.htaccess come along. Turn on hidden files in your FTP client.
  • PHP must be able to write in files/protected, because Apache mode keeps its tokens in a .sl folder inside it.
  • Keep your cronjob running every minute if you use Apache signed links.
  • If you change your domain or move to HTTPS, read the HLS notes above first.
  • After a move, press Test in the Protected files group, and play one protected item and one HLS item.

Troubleshooting

What you seeLikely causeWhat to do
A protected file opens directly by its addressThe .htaccess in files/protected is missing, or you're on nginx without the rulesRestore the file or add the nginx rules, then run Test
Test says protector.php served the linkApache too old or missing mod_version, or nginx rules not loadedUpgrade or enable the module, or fix and reload nginx. Or stay on PHP
Apache links keep working long after they should expireThe cronjob isn't runningFix the cronjob and check Run logs
Site slows down when many people watchPHP delivery holding a worker per streamSwitch to Apache or nginx delivery, or use HLS
HLS items don't play, or stop partwayThe key was refused: IP changed, Cloudflare support off, or the nginx key rule is missing or outdatedTurn on cf_cache behind Cloudflare, add or replace the nginx hls_keys block shown above, then press play again
Old HLS items stopped after a domain or HTTPS changePlaylists load keys from the old addressKeep the old domain pointing at the same site
HLS won't load from storageCORS isn't allowed on the bucketAllow your domain for .m3u8 and .ts on the storage
Nothing plays after moving serversfiles/hls_keys or the .htaccess files weren't copiedCopy them from your backup
A priced item's file isn't protectedThe price was added after the file was processedUpload the file again, or accept it as unprotected
Saved item won't play offlineNot on HTTPS, or the player was never loaded online in that browserUse HTTPS; play something online once

Checklist

Decided which content needs protection: none, priced, or everything
Set Protect files for Audio and Video in Upload Setting
Checked item-type rules further down the same page
Chose a delivery mode and the Test button passed
On nginx: added both location blocks and reloaded
Cronjob runs every minute (needed for Apache signed links)
Using third-party storage for paid content? HLS Encryption is on, FFmpeg works, and CORS is set
Site is on its final domain with HTTPS before encoding HLS
Behind Cloudflare: Cloudflare support is on
files/protected, files/hls_keys and both .htaccess files are in your backups

Related pages: Files, Storage, FFmpeg, Nginx, Cloudflare.