XMPP rarely gets taught; people run into it. Usually because they installed Jitsi Meet, saw Prosody in the process list, and then hit a config value like xmpp-domain or a log line full of XML. This is the practical explanation of what that protocol is doing.

The XMPP protocol in one paragraph

XMPP, the Extensible Messaging and Presence Protocol, is an open standard for exchanging small pieces of structured XML in near real time. Clients keep a long-lived connection to a server, servers talk to other servers, and anyone can run one, the same federated model as email. The core is defined in RFC 6120 (streams and stanzas) and RFC 6121 (instant messaging and presence), and nearly everything else is added through extensions. It is built for many small messages, not large binary transfers.

Addresses: JIDs

Every entity has a Jabber ID, a JID, in this shape:

localpart@domainpart/resourcepart
alice@example.com/laptop
  • Bare JID alice@example.com identifies the account.
  • Full JID alice@example.com/laptop identifies one connected session. Alice on her phone and on her laptop has the same bare JID and two full JIDs.
  • Domain-only JIDs such as conference.example.com identify servers and services.

That last form is why Jitsi's config is full of domains. auth.meet.example.com and conference.meet.example.com are not websites, they are XMPP service addresses inside Prosody.

XMPP architecture: streams and connections

A client connects to its server, traditionally on TCP port 5222, and opens an XML stream. The server opens one back. Everything after that, authentication and all messages, travels as XML elements inside those two open-ended documents until someone closes the stream.

Browsers can't open raw TCP connections, so web clients carry the same stream over WebSocket (RFC 7395) or the older BOSH long-polling method. That is what the /xmpp-websocket and /http-bind paths on a Jitsi server are: nginx forwards them to Prosody. In JavaScript you rarely handle the stream by hand. Strophe.js is the classic library (Jitsi's client is built on it), and StanzaJS, formerly stanza.io, is the newer TypeScript option.

The three stanzas, with XMPP examples

A stanza is one complete unit of communication inside the stream. There are exactly three types, and every XMPP feature, from chat to video conference signaling, is built from them. All three share the same core attributes: to, from, id, type and xml:lang.

message: fire and forget

Push content to someone, with no reply required:

<message from="alice@example.com/laptop"
         to="bob@example.com"
         type="chat"
         id="msg-4711">
  <body>Are we still on for the call?</body>
</message>

The type is chat for one-to-one, groupchat for a multi-user room, headline for alerts, normal, or error.

XMPP presence: availability

Broadcast whether you are online, and optionally how available:

<presence from="alice@example.com/laptop">
  <show>away</show>
  <status>In a meeting until 3</status>
</presence>

A detail our own older article got wrong: <show> only accepts away, chat, dnd and xa (extended away). Being online is simply presence with no show element, and going offline is type="unavailable". Presence also carries subscription requests, with types like subscribe and subscribed. And element names are lowercase; <Presence> is not a valid stanza.

XMPP iq: request and response

Info/query stanzas work like a tiny RPC. A get or set must receive exactly one result or error with the same id. The child element's namespace says what is being asked. Fetching the contact list, called the roster:

<iq from="alice@example.com/laptop" type="get" id="roster-1">
  <query xmlns="jabber:iq:roster"/>
</iq>

<iq to="alice@example.com/laptop" type="result" id="roster-1">
  <query xmlns="jabber:iq:roster">
    <item jid="bob@example.com" name="Bob" subscription="both"/>
  </query>
</iq>
Stanza Pattern Typical use
message One way Chat, notifications, room messages
presence Broadcast Online status, joining and leaving rooms, subscriptions
iq Request and exactly one reply Rosters, profiles, service discovery, configuration

Extensions (XEPs)

The "extensible" part is literal. XMPP Extension Protocols, XEPs, published by the XMPP Standards Foundation, add features by defining new child elements in their own namespaces. The ones you meet most:

  • XEP-0045 Multi-User Chat: rooms. Jitsi conferences are MUC rooms.
  • XEP-0030 Service Discovery: ask a server or client what it supports.
  • XEP-0198 Stream Management: resume a session after a dropped connection without losing stanzas, essential on mobile.
  • XEP-0313 Message Archive Management: server-side chat history.
  • XEP-0363 HTTP File Upload: share files by uploading over HTTP instead of pushing bytes through XMPP.
  • XEP-0166 Jingle: negotiating media sessions, which is how XMPP carries WebRTC call setup (the same job a WebRTC signaling server does in apps without XMPP).

XMPP server options and where XMPP runs today

Server Written in Good fit
Prosody Lua Small to medium deployments, easy modules, Jitsi Meet
ejabberd Erlang Large clustered messaging platforms
Openfire Java Web admin console, enterprise directory integration

The biggest XMPP deployment most developers touch today is inside video conferencing. In Jitsi Meet, every participant's browser connects to Prosody over WebSocket, the conference is a MUC room, and Jicofo, the conference focus, uses XMPP to allocate a videobridge and hand each participant a Jingle session description. Jitsi's architecture shows where each piece sits, and when XMPP-level config goes wrong, fixing Jicofo configuration errors decodes the domain settings. If you want to run Openfire yourself, installing Openfire on Ubuntu covers it, and running Jitsi Meet on Openfire or ejabberd explains why swapping Prosody out is harder than it looks.

Watch the stanzas yourself

The fastest way to understand XMPP is to read real traffic. In a Jitsi meeting, open the browser developer tools, find the xmpp-websocket connection in the Network tab, and read the frames: you will see the presence stanzas as people join the MUC room and the iq exchanges Jicofo uses to set up media.

Frequently Asked Questions

What is an XMPP stanza?

A stanza is one unit of XMPP communication, a small XML element sent over an open stream. There are three kinds: message for one-way content such as chat, presence for availability, and iq (info/query) for requests that always get exactly one reply.

What is an XMPP server?

The software clients connect to. It authenticates users, routes stanzas between them, stores rosters and offline messages, and talks to other XMPP servers for federation. Prosody, ejabberd and Openfire are the common open source choices.

Is XMPP still used in 2026?

Yes, mostly out of sight. Jitsi Meet runs its signaling on XMPP through Prosody, plenty of in-app chat systems use it, and there is an active federated community around Prosody, ejabberd and clients such as Conversations.

Which JavaScript library should I use for XMPP?

Strophe.js is the long-standing choice and is what Jitsi Meet's client library builds on. StanzaJS, formerly stanza.io, is a more modern TypeScript option that parses stanzas into JSON. Both connect over WebSocket or BOSH.

What is the difference between XMPP and Jabber?

Jabber was the original name of the open protocol and its early community. When it was standardized at the IETF, the protocol became XMPP. Addresses are still called Jabber IDs, or JIDs, which is where the name survives.

Which XMPP server should I use?

Prosody for small to medium deployments and anything Jitsi-related, since Jitsi depends on its Lua modules. ejabberd, written in Erlang, for very large clustered deployments. Openfire, written in Java, if you want a web admin console and easy integration with Java systems.

What does the xmlns attribute do in an iq stanza?

The namespace on the child element says which protocol extension the request belongs to. A query in the jabber:iq:roster namespace asks for the contact list, while vcard-temp asks for a profile. The iq type (get, set, result, error) marks request or reply.