TURN config goes in the browser, not the PBX.
Your PBX (Asterisk, FreeSWITCH, Kamailio, OpenSIPS) speaks SIP and RTP. The browser-based softphone speaks WebRTC. The TURN server only relays the WebRTC media leg — between the browser and whatever endpoint terminates the WebRTC. The PBX itself does not connect to TURN; it sees standard RTP from a public-facing WebRTC bridge.
// npm install jssip
import JsSIP from 'jssip';
const ua = new JsSIP.UA({
uri: 'sip:alice@your-pbx.example.com',
password: 'secret',
sockets: [new JsSIP.WebSocketInterface('wss://your-pbx.example.com:7443')],
pcConfig: {
iceServers: [
{ urls: 'stun:stun.expressturn.com:3478' },
{
urls: [
'turn:relay1.expressturn.com:3478?transport=udp',
'turn:relay1.expressturn.com:3478?transport=tcp',
'turns:relay1.expressturn.com:443?transport=tcp'
],
username: 'YOUR_EXPRESSTURN_USERNAME',
credential: 'YOUR_EXPRESSTURN_PASSWORD'
}
],
iceTransportPolicy: 'all'
}
});
ua.start();
ua.call('sip:bob@your-pbx.example.com');
import { UserAgent } from 'sip.js';
const userAgent = new UserAgent({
uri: UserAgent.makeURI('sip:alice@your-pbx.example.com'),
authorizationPassword: 'secret',
transportOptions: { server: 'wss://your-pbx.example.com:7443' },
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers: [
{ urls: 'stun:stun.expressturn.com:3478' },
{
urls: ['turn:relay1.expressturn.com:3478?transport=udp',
'turns:relay1.expressturn.com:443?transport=tcp'],
username: 'YOUR_EXPRESSTURN_USERNAME',
credential: 'YOUR_EXPRESSTURN_PASSWORD'
}
]
}
}
});
In pjsip.conf set the WebRTC transport on port 8089/wss with appropriate media_address and external_media_address if Asterisk is behind NAT. Asterisk uses STUN for its own NAT traversal — you don't need to configure TURN on Asterisk because it has a public address. The browser side handles TURN for itself.
For mod_verto WebRTC clients, configure verto.conf.xml with appropriate WebSocket and external IP. As with Asterisk, FreeSWITCH itself doesn't connect through TURN; the browser does.
Both proxies handle SIP signaling. WebRTC media bridging usually goes through a separate RTP engine (rtpengine for Kamailio, mediaproxy or rtpproxy for OpenSIPS). The bridge has a public IP and doesn't need TURN; the browser does.
iceTransportPolicy: 'relay') reveals whether TURN is the problem or the bridge is.Related: TURN for VoIP & SIP · PeerJS recipe