RTSP 400 Bad Request Fix: DESCRIBE Failed, Malformed Camera URL, and Header Errors
Fix RTSP 400 Bad Request on DESCRIBE. Covers malformed camera URLs for Axis/Dahua/Hikvision, unsupported headers, reverse proxy issues, auth, and vendor-specific stream paths with real diagnostic commands.
RTSP/1.0 400 Bad Request means the camera rejected your RTSP request as malformed. It's not a network problem. It's not a codec problem. It's a request-format problem — and it's fixable once you see the exact request the camera received.
Fast answer: 30-second triage
- Test with VLC first. If VLC works, capture the exact RTSP request it sends. Compare to your failing client.
- Check the URL path. Different cameras use completely different paths for the same RTSP stream. Copying a URL from one camera brand to another is the #1 cause of 400 errors.
- Check URL encoding. Special characters in passwords break RTSP URL parsing. Encode
@as%40,:as%3A,/as%2F. - Check for proxy interference. Direct RTSP to the camera works but through a proxy returns 400? The proxy is altering RTSP headers.
If none of these fix it, work through the detailed sections below.
First: verify the camera is reachable
Before debugging RTSP, confirm basic connectivity:
# TCP reachability
nc -zv 192.168.1.100 554
# Or with telnet
telnet 192.168.1.100 554
# RTSP OPTIONS — the simplest RTSP request
# If this fails, the camera isn't speaking RTSP
If the port is closed, the problem is network/firewall, not RTSP.
Camera-specific URL formats
The most common cause of 400 errors: using the wrong URL path format for your camera brand. Each manufacturer uses different conventions:
Axis
rtsp://<ip>/axis-media/media.amp
rtsp://<ip>/mpeg4/media.amp
rtsp://<ip>:554/axis-media/media.amp?videocodec=h264
Dahua
rtsp://<user>:<pass>@<ip>:554/cam/realmonitor?channel=1&subtype=0
rtsp://<user>:<pass>@<ip>:554/cam/realmonitor?channel=1&subtype=1
- subtype=0: main stream
- subtype=1: sub stream
Hikvision
rtsp://<user>:<pass>@<ip>:554/Streaming/Channels/101
rtsp://<user>:<pass>@<ip>:554/Streaming/Channels/102
- 101: channel 1, main stream
- 102: channel 1, sub stream
- 201: channel 2, main stream
Generic / ONVIF
rtsp://<ip>:554/stream1
rtsp://<ip>:554/live
rtsp://<ip>:554/h264
rtsp://<ip>:554/h265
rtsp://<ip>:554/profile1/media.smp
rtsp://<ip>/onvif1
rtsp://<ip>/onvif2
Testing with ffmpeg
# Test DESCRIBE only (don't decode)
ffmpeg -rtsp_transport tcp -i "rtsp://user:pass@192.168.1.100:554/stream1" -t 1 -f null -
# Verbose output shows the exact RTSP exchange
ffmpeg -loglevel debug -rtsp_transport tcp -i "rtsp://..." -t 1 -f null - 2>&1 | grep -E "DESCRIBE|SETUP|response"
URL encoding: the silent 400 trigger
When a password contains special characters, the RTSP URL becomes ambiguous. The @ separates credentials from the host, so a password containing @ breaks the parsing.
WRONG: rtsp://admin:pa@ss@192.168.1.100/stream1
parser sees: user=admin, pass=pa, host=ss@192.168.1.100
RIGHT: rtsp://admin:pa%40ss@192.168.1.100/stream1
parser sees: user=admin, pass=pa@ss, host=192.168.1.100
Characters that must be encoded in RTSP URLs:
| Character | Encoding | Example |
|---|---|---|
@ |
%40 |
user@domain → user%40domain |
: |
%3A |
pass:word → pass%3Aword |
/ |
%2F |
pass/word → pass%2Fword |
? |
%3F |
in query values |
# |
%23 |
in query values |
% |
%25 |
literal percent |
& |
%26 |
in query values (if not separating parameters) |
| space | %20 |
in query values |
Config files often add another layer of escaping. A URL in a YAML file may need both YAML escaping AND URL encoding.
Full diagnostic decision tree
RTSP returns 400 Bad Request
│
├─ Is port 554 reachable?
│ ├─ NO → Fix network/firewall. Not an RTSP issue.
│ └─ YES → Continue
│
├─ Does VLC play the same URL?
│ ├─ YES, VLC works → Capture VLC's exact request. Compare with your client.
│ │ Differences in headers, URI format, or auth will point to the fix.
│ └─ NO, VLC also fails → Problem is in the URL or camera config
│
├─ Check the URL path
│ ├─ Wrong manufacturer format → Use correct format for your camera brand
│ ├─ Query parameters missing → Add required parameters (channel, subtype)
│ └─ Path contains unencoded special chars → URL-encode credentials
│
├─ Check DESCRIBE headers
│ ├─ Missing Accept: application/sdp → Add it
│ ├─ Extra HTTP headers (via proxy) → Remove proxy from RTSP path
│ └─ Malformed Authorization → Fix Digest auth parameters
│
├─ Check CSeq and RTSP syntax
│ ├─ Missing CSeq → Add sequential CSeq header
│ ├─ Wrong RTSP version → Use RTSP/1.0
│ └─ CRLF formatting wrong → Ensure \r\n line endings
│
└─ Still failing?
├─ Try ONVIF discovery to get the correct RTSP URL
├─ Check camera firmware version (older firmware may have stricter parsing)
└─ Test with a known-working RTSP client as baseline
Reverse proxy: the hidden 400 cause
RTSP through an HTTP reverse proxy is fragile. Proxies designed for HTTP may:
- Inject HTTP-specific headers (Host, X-Forwarded-For, User-Agent)
- Rewrite the request URI
- Buffer and alter the TCP connection behavior
- Interfere with RTSP's persistent connection model
Symptoms of proxy interference:
- Direct RTSP to camera: works
- RTSP through proxy: 400 Bad Request
- Camera logs show "malformed request" with extra headers not present in the direct flow
Fix: Either bypass the proxy for RTSP traffic, use an RTSP-aware proxy, or configure the proxy to pass RTSP traffic unmodified.
Authentication edge cases
Most auth problems return 401, but a malformed Authorization header can return 400.
The "works on first try, fails on retry" pattern:
- Client sends unauthenticated DESCRIBE
- Camera returns 401 with Digest challenge (realm, nonce)
- Client computes Digest response
- Client sends authenticated DESCRIBE with Authorization header
- Camera returns 400
This happens when the Digest response computation is wrong — the URI used in the Digest calculation doesn't match the actual request URI, or the nonce was stale, or special characters in the password weren't encoded correctly before hashing.
Check: Compare the request URI in the Authorization header with the actual request URI on the wire. In Digest auth, the URI is part of the hash — if they don't match character-for-character (including encoding), the response is invalid.
When the camera is just broken
Some cameras have buggy RTSP implementations. If you've checked everything above and still get 400:
- Check for firmware updates
- Test with the manufacturer's own client software
- Try ONVIF as an alternative discovery and streaming path
- If the camera works with its own app but not with standards-compliant RTSP clients, the camera's RTSP stack is non-compliant
File a bug with the camera vendor. Include the exact RTSP DESCRIBE request and response. A proper RTSP implementation should not return 400 for a valid, well-formed request regardless of the URL path — it should return 404.