FFmpeg Auth Headers: How to Use Authorization Bearer with FFmpeg

You need to pull a video from behind auth. You’ve got the URL, you’ve got the token, and FFmpeg keeps throwing a 401. You Google it, find three Stack Overflow answers that all look slightly different, try each one, and none of them work.
I’ve wasted more time on this than I’d like to admit. The problem is almost always the same thing: FFmpeg’s -headers flag has quirks that nobody explains clearly. So here’s everything I know about passing auth headers in FFmpeg, distilled into examples you can actually copy-paste.
The Syntax (And the One Detail Everyone Misses)
FFmpeg sends custom HTTP headers with the -headers flag. Straightforward enough. But here’s the thing that trips up almost everyone: **each header must end with \r\n** (carriage return + newline). Miss that, and FFmpeg silently drops your header. No error, no warning. Just a 401 and confusion.
ffmpeg -headers "Authorization: Bearer YOUR_TOKEN\r\n" -i "https://example.com/protected-video.m3u8" -c copy output.mp4
That’s the whole thing. But there are a handful of gotchas that’ll bite you depending on your use case.
HLS Streams (The Most Common Scenario)
If you’re pulling a protected .m3u8 stream, good news: FFmpeg sends your header on every segment request automatically. You set it once and it propagates to every .ts chunk.
ffmpeg \
-headers "Authorization: Bearer eyJhbGciOiJIUzI1NiIs...\r\n" \
-i "https://cdn.example.com/stream/playlist.m3u8" \
-c copy \
-bsf:a aac_adtstoasc \
output.mp4
I’ve seen people try to intercept segment requests and inject headers individually. You don’t need to do that. FFmpeg handles it.
Downloading from an API Endpoint
Sometimes the video isn’t a stream—it’s a file behind a REST API. Same approach, you can stack multiple headers:
ffmpeg \
-headers "Authorization: Bearer sk_live_abc123\r\n" \
-headers "Accept: video/mp4\r\n" \
-i "https://api.example.com/v1/videos/download/42" \
-c copy \
output.mp4
You can also combine headers into one flag separated by \r\n, but multiple flags is easier to read and less error-prone.
Multiple Headers
Need to pass several headers? Two options:
ffmpeg \
-headers "Authorization: Bearer YOUR_TOKEN\r\n" \
-headers "X-API-Key: your-api-key\r\n" \
-headers "User-Agent: MyApp/1.0\r\n" \
-i "https://api.example.com/video.mp4" \
-c copy \
output.mp4
Or combine them:
ffmpeg \
-headers "Authorization: Bearer YOUR_TOKEN\r\nX-API-Key: your-api-key\r\n" \
-i "https://api.example.com/video.mp4" \
-c copy \
output.mp4
Both work. I prefer separate flags because debugging a 200-character header string at 11pm is not my idea of a good time.
Basic Auth
Some older services use HTTP Basic instead of Bearer tokens:
ffmpeg -i "https://username:password@example.com/video.mp4" -c copy output.mp4
That’s the easiest approach. If you need the header explicitly:
ffmpeg \
-headers "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=\r\n" \
-i "https://example.com/video.mp4" \
-c copy \
output.mp4
The Base64 value is just username:password encoded:
echo -n "username:password" | base64
Why It’s Not Working
I’ve debugged this enough times to know the usual suspects. Here they are, in order of how often they bite people.
1. Missing \r\n
This is the #1 cause. FFmpeg silently ignores headers without the trailing \r\n. No error message, no hint. Your header just vanishes.
# Broken — header silently dropped
ffmpeg -headers "Authorization: Bearer TOKEN" -i "https://..." -c copy out.mp4
# Fixed
ffmpeg -headers "Authorization: Bearer TOKEN\r\n" -i "https://..." -c copy out.mp4
2. -headers after -i
Order matters in FFmpeg. The -headers flag applies to the next input, so it has to come before -i. Put it after and FFmpeg never sends it.
# Broken — header never sent
ffmpeg -i "https://..." -headers "Authorization: Bearer TOKEN\r\n" -c copy out.mp4
# Fixed
ffmpeg -headers "Authorization: Bearer TOKEN\r\n" -i "https://..." -c copy out.mp4
3. Shell quoting weirdness
Different shells interpret \r\n differently. If you’re on bash or zsh and the header still isn’t working, try:
ffmpeg -headers $'Authorization: Bearer TOKEN\r\n' -i "https://..." -c copy out.mp4
The $'...' syntax forces your shell to interpret the escape sequences literally.
4. Special characters in your token
JWTs can contain !, $, and backticks—all characters your shell loves to interpret. Use single quotes around the header value:
ffmpeg \
-headers 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOi...\r\n' \
-i "https://..." \
-c copy output.mp4
5. It’s not actually an FFmpeg problem
Before you spend another hour tweaking FFmpeg flags, test the URL with curl:
curl -H "Authorization: Bearer TOKEN" "https://example.com/video.mp4" -o /dev/null -w "%{http_code}"
If curl also gets a 401, the problem is your token or the URL—not FFmpeg.
Using Auth Headers in Code
If you’re calling FFmpeg from code rather than the terminal, here’s what that looks like.
Bash script:
#!/bin/bash
TOKEN="your-bearer-token-here"
VIDEO_URL="https://api.example.com/videos/stream.m3u8"
ffmpeg \
-headers "Authorization: Bearer ${TOKEN}\r\n" \
-i "${VIDEO_URL}" \
-c copy \
-movflags +faststart \
output.mp4
Python:
import subprocess
token = "your-bearer-token-here"
url = "https://api.example.com/videos/stream.m3u8"
subprocess.run([
"ffmpeg",
"-headers", f"Authorization: Bearer {token}\r\n",
"-i", url,
"-c", "copy",
"output.mp4"
])
Node.js:
const { execSync } = require('child_process');
const token = 'your-bearer-token-here';
const url = 'https://api.example.com/videos/stream.m3u8';
execSync(`ffmpeg -headers "Authorization: Bearer ${token}\\r\\n" -i "${url}" -c copy output.mp4`);
When This Gets Old
Auth headers work fine when you’re downloading one video. But if you’re building anything production-grade—processing user uploads, pulling from authenticated sources at scale, running batch jobs—the overhead adds up fast.
Token refresh logic. Retry handling for expired credentials. Secret management so tokens don’t leak into logs. Error parsing to tell "bad token" from "network timeout" from "corrupt file." You end up spending more time on auth plumbing than actual video processing.
FFmpeg Micro handles all of this through a simple REST API. Auth is one Bearer token—no \r\n hacks, no flag ordering, no shell escaping:
curl -X POST https://api.ffmpeg-micro.com/v1/jobs \
-H "Authorization: Bearer fm_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"input": "https://example.com/source-video.mp4",
"ffmpeg_args": ["-vf", "scale=-2:720", "-c:v", "libx264", "-c:a", "aac"]
}'
No FFmpeg installation. No infrastructure. Pass any source URL and FFmpeg Micro fetches it server-side, processes it, and gives you a download link or webhook when it’s done.
const response = await fetch('https://api.ffmpeg-micro.com/v1/jobs', {
method: 'POST',
headers: {
'Authorization': 'Bearer fm_sk_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
input: 'https://cdn.example.com/protected/video.m3u8',
preset: '720p',
webhook_url: 'https://yourapp.com/webhooks/video-done'
})
});
const { job_id, status } = await response.json();
Free tier includes 30 minutes of processing. No credit card required.
Try FFmpeg Micro free — auth headers handled automatically.
Quick Reference
| Scenario | Command |
|---|---|
| Bearer token | `ffmpeg -headers "Authorization: Bearer TOKEN\r\n" -i URL -c copy out.mp4` |
| Basic auth | `ffmpeg -i "https://user:pass@host/video.mp4" -c copy out.mp4` |
| Multiple headers | `ffmpeg -headers "Auth: Bearer TOKEN\r\n" -headers "X-Key: KEY\r\n" -i URL -c copy out.mp4` |
| HLS with auth | `ffmpeg -headers "Authorization: Bearer TOKEN\r\n" -i "https://host/stream.m3u8" -c copy -bsf:a aac_adtstoasc out.mp4` |
Always put -headers before -i. Always end each header with \r\n. Save yourself the debugging.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free