ffmpegauthorizationbearer-tokenapistreaming

FFmpeg Auth Headers: How to Use Authorization Bearer

··7 min read
FFmpeg Auth Headers: How to Use Authorization Bearer

You need to download or process a video that's behind authentication. Maybe it's an HLS stream from a CDN, a DASH manifest on a protected endpoint, or just a direct MP4 URL that requires a bearer token. FFmpeg can handle all of these, but the -headers syntax trips people up.

This post shows you the exact commands that work.

The Basic Syntax

FFmpeg's -headers flag lets you pass custom HTTP headers when fetching remote URLs. For bearer token auth, it looks like this:

ffmpeg -headers "Authorization: Bearer YOUR_TOKEN_HERE" -i https://example.com/protected/video.mp4 -c copy output.mp4

Two things to notice. First, there is no \r\n at the end. Plenty of answers tell you to type one, and in bash or zsh that breaks the request. Inside ordinary quotes the shell passes \r\n through as four plain characters, they land on the end of your token, and the server answers 401. FFmpeg adds the line ending itself. When it does, it prints No trailing CRLF found in HTTP header. Adding it. and that warning is harmless.

Second, the -headers flag must come before the -i input flag. FFmpeg applies an option to the input that follows it, so headers declared after the input URL are never sent.

Downloading a Protected HLS Stream

HLS streams are the most common use case. Your video player can handle the auth token in JavaScript, but you need FFmpeg to do the same thing server-side.

ffmpeg -headers "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -i https://cdn.example.com/stream/master.m3u8 \
  -c copy \
  -bsf:a aac_adtstoasc \
  output.mp4

FFmpeg applies the -headers to every HTTP request it makes for that input. So when it fetches the playlist and then each .ts segment, the bearer token goes with every request. You don't need to handle segments individually.

The -bsf:a aac_adtstoasc bitstream filter is there because HLS segments often use ADTS-wrapped AAC audio, and MP4 containers need the raw AAC stream. Without it, you might get audio playback issues in some players.

Multiple Headers

Some APIs need more than just authorization. All headers go in a single -headers value, with a real line break between them. In bash and zsh, $'...' quoting turns \r\n into that line break:

ffmpeg -headers $'Authorization: Bearer YOUR_TOKEN\r\nX-Custom-Header: some-value\r\n' \
  -i https://api.example.com/video/12345 \
  -c copy output.mp4

Don't use multiple -headers flags. FFmpeg only reads the last one, so your earlier headers would be silently ignored.

A variable does not expand inside $'...'. When the token is in a variable, join the pieces:

ffmpeg -headers "Authorization: Bearer $TOKEN"$'\r\n'"X-Custom-Header: some-value" \
  -i https://api.example.com/video/12345 \
  -c copy output.mp4

Basic Auth

Some older services use HTTP Basic in place of a bearer token. The shortest form puts the credentials in the URL:

ffmpeg -i "https://username:password@example.com/video.mp4" -c copy output.mp4

FFmpeg sends them once the server asks, which is the normal Basic handshake. To send the header up front:

ffmpeg -headers "Authorization: Basic $(printf 'username:password' | base64)" \
  -i https://example.com/video.mp4 \
  -c copy output.mp4

DASH Streams

The same flag applies to a DASH manifest:

ffmpeg -headers "Authorization: Bearer YOUR_TOKEN" \
  -i https://cdn.example.com/dash/manifest.mpd \
  -c copy \
  -movflags +faststart \
  output.mp4

DASH input needs an FFmpeg build that includes the DASH demuxer. Check yours with ffmpeg -demuxers | grep dash. The -movflags +faststart moves the MP4 metadata to the beginning of the file, which is useful if you plan to serve the output over HTTP. Players can start playback before the full download finishes.

Common Gotchas

Quoting in different shells. The form with no line ending is the one that works everywhere, because FFmpeg adds the ending itself:

# bash, zsh, Windows cmd, PowerShell
ffmpeg -headers "Authorization: Bearer MY_TOKEN" -i url -c copy out.mp4

# bash and zsh: a real line ending, for several headers in one value
ffmpeg -headers $'Authorization: Bearer MY_TOKEN\r\n' -i url -c copy out.mp4

# Broken in bash and zsh: \r\n in ordinary quotes is sent as text, single or double
ffmpeg -headers "Authorization: Bearer MY_TOKEN\r\n" -i url -c copy out.mp4

In PowerShell, a real line ending is written with backticks: ` "Authorization: Bearer MY_TOKENrn" `.

Token in a variable. When the token comes from an environment variable or script output:

TOKEN=$(curl -s https://auth.example.com/token | jq -r '.access_token')
ffmpeg -headers "Authorization: Bearer $TOKEN" \
  -i https://cdn.example.com/stream.m3u8 \
  -c copy output.mp4

The header isn't being sent. Run FFmpeg with -v debug to see the actual HTTP requests. Look for your header in the output:

ffmpeg -v debug -headers "Authorization: Bearer YOUR_TOKEN" \
  -i https://example.com/video.mp4 -c copy output.mp4 2>&1 | grep -i auth

If the header doesn't show up in the request, the most likely cause is -headers placed after -i. If it shows up with \r\n written on the end of the value, your shell passed those characters through. Take them out.

Is it FFmpeg at all? Before changing more flags, send the same header with curl:

curl -H "Authorization: Bearer YOUR_TOKEN" "https://example.com/video.mp4" -o /dev/null -w "%{http_code}\n"

If curl also gets a 401, the problem is the token or the URL.

Token expiration during long downloads. Bearer tokens expire. If you're downloading a long HLS stream with hundreds of segments, your token might expire partway through. FFmpeg won't refresh it. You'll see 401 or 403 errors in the output once the token dies.

For short clips, this isn't a problem. But for streams longer than your token's TTL, you need either a long-lived token or a wrapper script that re-authenticates and resumes.

Redirects carry your headers along. Some CDNs redirect your initial request to a different domain. FFmpeg follows the redirect and sends your custom headers to the new location too, even on another host. That keeps a CDN redirect working. It also means your token goes wherever the first server points, so if you don't control the redirect target, request the final URL directly.

Python and Node.js

From code, pass the arguments as a list. Nothing goes through a shell, so there is no quoting to get wrong.

import subprocess

token = "your-bearer-token-here"
url = "https://api.example.com/videos/stream.m3u8"

subprocess.run([
    "ffmpeg",
    "-headers", f"Authorization: Bearer {token}",
    "-i", url,
    "-c", "copy",
    "output.mp4",
])
const { execFileSync } = require('child_process');

const token = 'your-bearer-token-here';
const url = 'https://api.example.com/videos/stream.m3u8';

execFileSync('ffmpeg', ['-headers', `Authorization: Bearer ${token}`, '-i', url, '-c', 'copy', 'output.mp4']);

When CLI Headers Get Painful

Managing tokens, handling expiration, and dealing with shell quoting across different environments gets old fast. If you're building video processing into an application and not running one-off commands, it can be simpler to keep the auth out of FFmpeg altogether.

FFmpeg Micro runs FFmpeg behind a REST API. It fetches the source from a URL and does not send custom headers, so for a protected file you give it a URL it can read: a signed URL from your storage or CDN, or a file you upload first with a presigned upload. The job itself is one JSON request:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{ "url": "https://cdn.example.com/video.mp4?signature=..." }],
    "outputFormat": "mp4"
  }'

No FFmpeg install, and no header quoting on the source side.

Last verified: 2026-10-06 against FFmpeg 8.0.1 in bash and zsh on macOS, with a local server recording the headers it received. The DASH command, the Windows forms and the API request were not run.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free