WebRTC Codec Fingerprinting: Media Capabilities Leak
WebRTC codec enumeration through getCapabilities() and SDP offers exposes media capabilities that differ by operating system. Learn how the codec list becomes a platform signal.
BotBrowser Team
Want the structured docs for Fingerprint?
This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.
Before a call connects, a page can ask the browser which video and audio codecs it can negotiate, and the answer arrives without a permission prompt, a peer, or a microphone. BotBrowser can present a codec list that follows the loaded profile, and it cannot promise that every peer will accept that list or that a real call behaves the same way. The relevant questions are where those lists come from, which API surfaces have to agree, and what a team should record before treating the result as reviewed.
TL;DR
RTCRtpSender.getCapabilities('video')andRTCRtpReceiver.getCapabilities('audio')are static calls that return a codec list immediately, with no connection and no permission.createOffer()writes the same negotiation into SDP, so them=videoandm=audiolines should list the codecs and codec numbers that the capability call reported for the same profile.- Codec order and the presence of
H264,VP8,VP9,AV1oropusfollow the platform's hardware and licensing, which is why the list differs between a Windows profile and a Linux host. - BotBrowser keeps the JavaScript-visible list on the profile while the browser uses host decoders internally, so a profile codec the host lacks does not crash the call.
- Approve a release on three checks: matching counts across reloads, an SDP that agrees with
getCapabilities(), and a media call that still negotiates between the two peers.
Contents
- Introduction
- BotBrowser's Solution
- Load the profile and read the codec lists
- Verification
- Keep one profile for codec and call tests
- Practical conclusion
Introduction
WebRTC enables real-time video and audio communication in the browser. Before any media flows between peers, browsers enumerate their supported codecs and expose them through capability APIs and SDP (Session Description Protocol) offers. This negotiation is essential for functional video calls, but it also creates a detailed inventory of your system's media capabilities that any website can query without special permissions. In a controlled context BotBrowser can present a profile-consistent codec list; it cannot guarantee compatibility with every peer or replace media negotiation tests. For how one CreepJS report transforms codec and other WebRTC fields into a partial, selected-field summary, see the field provenance analysis.
WebRTC codec capabilities differ across platforms and can be used as a tracking signal. Different operating systems provide different levels of hardware acceleration support, which directly determines which codecs appear in the browser's capability lists. A website can query these lists and compare them against known platform profiles to infer your real operating system, even when your user agent string claims something different. Voice lists work similarly, as covered in Speech Synthesis Fingerprinting: Voice List Tracking.
BotBrowser addresses this by constructing WebRTC codec capability lists from the fingerprint profile, so JavaScript can see values aligned with the profile's target platform rather than the host system's actual hardware.
BotBrowser's Solution
BotBrowser builds the WebRTC codec capability lists from the profile data at the point where capabilities are assembled, rather than patching JavaScript objects or rewriting SDP strings afterwards. This is intended to keep supported codec APIs consistent with the loaded profile; verify the exact coverage for the profile and browser version in use.
Profile-Driven Codec Construction
When a BotBrowser profile is loaded, the WebRTC codec capabilities are built from the profile's recorded codec data rather than from the host system's actual hardware capabilities. The profile contains the complete codec configuration captured from a real device matching the target platform:
- The full list of supported video and audio codecs
- Codec parameters including profiles, levels, and format-specific options
- The correct ordering of codecs in capability lists
- The mapping from codec to the numbers used in SDP generation
This means that a profile targeting one platform, loaded on a completely different host OS, will still report the correct WebRTC codecs for the profile's target. The host system's actual capabilities are irrelevant to what JavaScript sees.
SDP Consistency
BotBrowser aims to keep the SDP generated by createOffer() consistent with the codec capabilities reported by getCapabilities() when both use the same loaded profile.
The m=video and m=audio lines in the SDP are intended to carry the codec identifiers and numbers that correspond to the profile's codec set. Scripts comparing both APIs should see matching values within that documented profile scope; this is not a guarantee for every peer or external media path.
Functional Compatibility
One of the challenges in controlling WebRTC codecs is maintaining actual media functionality. If the profile specifies a codec that the host system cannot actually decode, the browser needs to handle this gracefully. Codec checks are one kind of capability observation; whether a browser can hand a non-web URL to an application is another, covered in Safe External URL Handling for Browser Profiles.
BotBrowser addresses this through a dual-layer design: JavaScript sees the profile's codec list, while internally the browser registers only the codecs that the host system can actually process. This separation means:
- Fingerprinting scripts see the profile-accurate codec list.
- Actual WebRTC calls negotiate using codecs that both peers and the local system can handle.
- There is no crash or error when a profile lists a codec that the host cannot decode.
This is a critical distinction. Simply injecting codec entries into the capability list would cause failures when the browser attempts to actually use those codecs. BotBrowser keeps the boundary between reported capabilities and functional media processing inside the browser.
Cross-API Alignment
BotBrowser is designed to align WebRTC APIs and the following media capability interfaces when they are covered by the loaded profile:
RTCRtpSender.getCapabilities()andRTCRtpReceiver.getCapabilities()return profile-consistent codecs.- SDP offers from
createOffer()contain matching codec identifiers and numbers. - General media APIs like
canPlayType()andMediaCapabilities.decodingInfo()return results that are consistent with the WebRTC codec profile. - The overall media fingerprint describes the platform in one coherent way.
This cross-API alignment helps keep codec-reporting paths tied to the loaded profile; host codec availability and actual peer negotiation still constrain functional media. For what WebCodecs exposes and why support differs by platform, see WebCodecs Browser Capabilities and Privacy, which covers privacy and compatibility boundaries.
Load the profile and read the codec lists
Basic CLI Usage
WebRTC codec protection is automatic when a profile is loaded. No additional flags are needed:
chrome --bot-profile="path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
The profile's WebRTC codec configuration is applied at startup. All subsequent WebRTC capability queries and SDP generation will reflect the profile's codec set.
Playwright Integration
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: 'path/to/botbrowser/chrome',
args: ['--bot-profile=path/to/profile.enc'],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('about:blank');
// Verify WebRTC codec capabilities from the loaded profile
const codecInfo = await page.evaluate(() => {
const videoCaps = RTCRtpSender.getCapabilities('video');
const audioCaps = RTCRtpSender.getCapabilities('audio');
return {
videoCodecCount: videoCaps?.codecs.length || 0,
audioCodecCount: audioCaps?.codecs.length || 0,
};
});
console.log('Video codecs:', codecInfo.videoCodecCount);
console.log('Audio codecs:', codecInfo.audioCodecCount);
await browser.close();
})();
Puppeteer Integration
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: 'path/to/botbrowser/chrome',
args: ['--bot-profile=path/to/profile.enc'],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('about:blank');
// Verify WebRTC codec capabilities from the loaded profile
const codecs = await page.evaluate(() => {
const videoCaps = RTCRtpSender.getCapabilities('video');
const audioCaps = RTCRtpSender.getCapabilities('audio');
return {
videoCount: videoCaps?.codecs.length || 0,
audioCount: audioCaps?.codecs.length || 0,
};
});
console.log(`Video codecs: ${codecs.videoCount}`);
console.log(`Audio codecs: ${codecs.audioCount}`);
await browser.close();
})();
Combined with Proxy and Platform Settings
For broader platform consistency, combine codec protection with proxy and platform configuration, then verify each signal separately:
chrome --bot-profile="path/to/profile.enc" \
--proxy-server="socks5://user:[email protected]:1080" \
--bot-timezone="America/New_York" \
--bot-locale="en-US" \
--user-data-dir="$(mktemp -d)"
This can align codec capabilities with the profile's platform, while network identity, timezone, locale, and other signals remain dependent on their own settings and proxy behavior. For how to choose between profile, real and disabled WebRTC postures, see WebRTC Network Identity and Privacy: Choosing the Right Policy, which explains why candidate and statistics addresses should agree.
Verification
After configuring BotBrowser with a profile, verify that WebRTC codec protection is working:
-
Codec count check: Call
RTCRtpSender.getCapabilities('video')and confirm the number of codecs matches what you expect for the profile's target platform. -
Stability check: Reload the page and repeat. The codec count and types should be identical across page loads. Restart the browser with the same profile and verify again.
-
Fingerprint testing sites: Visit browser fingerprint testing services and check the WebRTC section. The reported codecs should not flag any anomalies.
Treat the three surfaces as one record rather than three separate observations. Write the codec names and their order from RTCRtpSender.getCapabilities('video'), the same list from RTCRtpReceiver.getCapabilities('video'), and the m=video section of an offer produced by createOffer(). A receiver list that omits a codec the sender advertises, or an SDP section whose numbers do not follow the capability order, tells a reviewer that one path was built from different input than the others. That kind of mismatch is worth fixing before anyone asks whether the list looks like the target platform, because an internally inconsistent browser is easier to notice than an unusual codec.
Audio deserves its own line in that record. The audio side of WebRTC on a browser that lacks a platform codec package falls back to different entries than the video side, and opus is the codec most calls rely on in practice. Compare getCapabilities('audio') against the m=audio section the same way, and keep the two media kinds apart so a video difference does not hide an audio change.
Keep one profile for codec and call tests
-
Always use a complete profile. WebRTC codec capabilities must align with the profile's operating system, browser version, and GPU configuration. BotBrowser profiles are captured from real devices, ensuring that all these dimensions are internally consistent. Do not mix and match profile components.
-
Match codec expectations to your use case. If you are working with websites that actively use WebRTC for video calls or streaming, make sure your profile's codec set supports the formats those sites expect. A profile that excludes commonly used codecs may cause negotiation failures during actual WebRTC sessions.
-
Combine with WebRTC IP protection. Codec fingerprinting and IP leaking are separate WebRTC privacy concerns, but both need to be addressed. Use BotBrowser's proxy integration with
--proxy-serverand--bot-webrtc-iceto ensure that your WebRTC IP addresses are also controlled. See the WebRTC leak prevention guide for details. -
Do not modify codec settings manually. Let the profile handle codec configuration entirely. Manually adding or removing codecs through chrome flags or configuration overrides can create inconsistencies that undermine the profile's consistency.
-
Keep profiles up to date. Codec support changes with browser versions. New codecs are added, and existing codec parameters may change. Using profiles that match the claimed browser version ensures that your codec capabilities correspond to what a real browser of that version would report.
-
Verify cross-platform consistency. If you use the same profile across different host operating systems (for example, running a Windows profile on both a macOS host and a Linux host), verify that the WebRTC codec capabilities are identical on both hosts. BotBrowser's profile-driven approach should produce the same output regardless of the host, but verification gives you confidence.
-
Monitor for codec-related WebRTC errors. If you encounter WebRTC connectivity issues, check the browser console for codec negotiation errors. BotBrowser handles the gap between profile codecs and locally available codecs gracefully, but certain edge cases with unusual peer configurations may require attention.
Frequently Asked Questions
What is WebRTC codec fingerprinting?
WebRTC codec fingerprinting is a technique where websites query your browser's WebRTC media capabilities to learn which video and audio codecs your system supports. Because codec support varies by operating system and hardware, this information can be used as a tracking signal to infer details about your platform.
How is this different from general media codec fingerprinting?
General media codec fingerprinting uses APIs like canPlayType() and MediaCapabilities. WebRTC codec fingerprinting uses WebRTC-specific APIs: RTCRtpSender.getCapabilities(), RTCRtpReceiver.getCapabilities(), and SDP offers from createOffer(). The WebRTC codec set is not identical to the general media codec set, because WebRTC has its own supported codec list that reflects real-time communication requirements. BotBrowser's profile-driven approach covers both surfaces consistently. For general media codec protection, see the MIME type and codec fingerprinting article.
Can a website query WebRTC codecs without making a call?
Yes. No actual WebRTC connection is needed. RTCRtpSender.getCapabilities('video') is a static method that returns codec information immediately. Similarly, createOffer() generates an SDP document without connecting to any remote peer. Both operations are fast, silent, and require no user interaction or permission.
Does BotBrowser disable any codecs?
No. BotBrowser does not remove or disable codec functionality. Instead, it controls what codec information is reported to JavaScript. The JavaScript-visible codec list matches the loaded profile. Internally, the browser still uses whichever codecs are available on the host system for actual media processing. This means your fingerprint is controlled without sacrificing WebRTC functionality.
What happens if the profile lists a codec my system does not support?
BotBrowser handles this gracefully. The JavaScript-visible APIs report the profile's codec list, maintaining the correct fingerprint. When actual WebRTC media negotiation occurs, the browser internally uses only the codecs that the host system can process. If a profile codec is not locally available, it is excluded from internal processing without affecting the fingerprint presentation. This prevents crashes and ensures that calls still work.
Does this protect audio codecs too?
Yes. BotBrowser's profile-driven codec construction applies to both video and audio capabilities. The audio codec list from getCapabilities('audio') and the audio entries in SDP offers are also derived from the profile data.
How does BotBrowser handle codec ordering?
Codec ordering in capability lists and SDP offers can carry platform-specific information. BotBrowser preserves the exact codec ordering from the profile, which reflects the natural ordering on the target platform. This detail matters because the order in which codecs appear is itself a signal that should match the profile's target.
Does this work with headless mode?
Yes. WebRTC codec capabilities are available in headless mode, and BotBrowser's profile-driven codec construction works identically whether the browser is running headless or with a visible window.
Can I verify the protection without making a real WebRTC call?
Yes. You can verify by calling RTCRtpSender.getCapabilities('video') and createOffer() in a page's JavaScript console or through Playwright/Puppeteer automation. No peer connection or remote server is needed. The code examples in the Configuration and Usage section demonstrate this.
Summary
WebRTC codec enumeration through getCapabilities() and SDP offers exposes media capabilities that differ across operating systems, making the codec list a potential tracking signal. BotBrowser builds WebRTC codec capabilities from the loaded fingerprint profile, so every surface that exposes codec information, from getCapabilities() to createOffer() SDP generation, returns profile-consistent results. The separation between the JavaScript-visible codec list and the internally registered decoders keeps the reported list on the target platform while actual WebRTC functionality continues to work. See the related profile protections on the features page.
Combined with WebRTC IP leak prevention, MIME type and codec protection, cross-platform browser profiles, and comprehensive fingerprint verification, BotBrowser provides thorough control over every WebRTC signal that could reveal your platform identity.
BotBrowser supports controlled WebRTC codec settings for fingerprint review; it does not guarantee peer compatibility or replace media negotiation tests.
Sources
- BotBrowser WebRTC fingerprint documentation: MIME and codec fingerprinting
- MDN reference for the capability call: RTCRtpSender.getCapabilities
Practical conclusion
Decide what the review is about before you collect anything. A codec list answers a compatibility question, namely whether this profile can offer the media formats the application needs, and it does not answer an identity question about the person or the machine. Write the profile family, the target platform, the browser release and the application path into the release record, then keep the check to those four inputs.
Run the comparison in one context. Call RTCRtpSender.getCapabilities('video') and RTCRtpSender.getCapabilities('audio'), record the counts and the codec names, call createOffer() without a peer, and compare the m=video and m=audio lines with what the capability calls returned. Repeat after a reload and after a browser restart with the same profile; counts that move between loads point at the profile or the browser build, not at the network. Add --proxy-server and --bot-webrtc-ice only when the deployment also needs the candidate addresses controlled, and verify those on their own.
Then run one real call between the two peers the product actually uses. A profile whose codec set excludes a format the site relies on will fail negotiation even though every JavaScript check passed, and a codec list that matches the profile but breaks calls is a product regression rather than a privacy gain. When a browser release changes the list, keep the previous profile package and the previous browser build available until the new pair passes both the list comparison and the call, and record which of the two checks decided the outcome.
This article is part of the guide: What Is a WebRTC IP Leak? Prevention and Browser Privacy
Related Articles
WebCodecs Browser Capabilities and Privacy
FingerprintBrowserLeaks WebRTC Test Report Fields
FingerprintCreepJS WebRTC Report: Trace the Fields
FingerprintMIME and Codec Fingerprinting: Media Support Signals
FingerprintText Measurement Fingerprinting: Sub-Pixel Tracking
FingerprintWebGPU Fingerprinting: The Next Generation of GPU Tracking
Run the same check in BotBrowser
Load a profile, keep the signals you just read about consistent inside the browser, and confirm the result on your own machine before a rollout.