Skip to content

Headers Exchange — Complete Guide

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Headers Exchange. We cover key concepts, practical examples, and best practices to help you master this topic.

A headers exchange routes messages based on header attributes instead of routing keys, using x-match all/any for flexible attribute-based routing.

Headers Exchange Flow

flowchart LR
    P[Publisher] --> H[Headers Exchange]
    H -->|"x-match: all
format=json, source=partner"| Q1[Partner JSON] H -->|"x-match: any
priority=high"| Q2[Priority Queue] style H fill:#f90,color:#fff
import pika
import json

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='header_router', exchange_type='headers', durable=True)

channel.queue_declare(queue='partner_json', durable=True)
channel.queue_declare(queue='priority_msgs', durable=True)
channel.queue_declare(queue='csv_archive', durable=True)

channel.queue_bind(
    exchange='header_router', queue='partner_json',
    arguments={'x-match': 'all', 'format': 'json', 'source': 'partner'}
)
channel.queue_bind(
    exchange='header_router', queue='priority_msgs',
    arguments={'x-match': 'any', 'priority': 'high', 'source': 'partner'}
)
channel.queue_bind(
    exchange='header_router', queue='csv_archive',
    arguments={'x-match': 'all', 'format': 'csv'}
)

messages = [
    ({'format': 'json', 'source': 'partner', 'priority': 'high'}, 'Partner JSON data'),
    ({'format': 'csv', 'source': 'internal', 'priority': 'low'}, 'Internal CSV report'),
    ({'format': 'xml', 'source': 'partner', 'priority': 'high'}, 'Partner XML feed'),
]

for headers, body in messages:
    channel.basic_publish(
        exchange='header_router', routing_key='', body=body,
        properties=pika.BasicProperties(headers=headers, delivery_mode=2)
    )
    print(f"Published with headers: {headers}")

connection.close()

Expected output:

Published with headers: {'format': 'json', 'source': 'partner', 'priority': 'high'}
Published with headers: {'format': 'csv', 'source': 'internal', 'priority': 'low'}
Published with headers: {'format': 'xml', 'source': 'partner', 'priority': 'high'}

x-match: all vs any

x-match: all — All specified headers must match (AND logic) x-match: any — Any one header must match (OR logic)

Header values must match exactly. Missing headers cause x-match: all to fail.

Use Cases

  • Routing by content type, format, and source
  • Multi-attribute filtering without routing key complexity
  • Compatibility with systems that already use message headers

Common Mistakes

1. Forgetting x-match

Without x-match, the binding never matches. Always include x-match with value all or any.

2. Using Headers for Simple Routing

For single-attribute routing, use direct or topic exchange. Headers add unnecessary complexity.

3. Type Mismatches

Headers values are typed. The string 'true' is different from the boolean True. Be consistent.

4. Assuming Headers Match on Absence

Headers exchange matches on presence and value of specified headers. It does not match on headers that are absent.

5. Performance with Many Headers

Each header comparison adds processing time. Keep bindings to 3-5 headers for good performance.

Practice Questions

1. How does headers exchange differ from topic?

Headers exchange matches on arbitrary header attributes. Topic exchange matches on routing key patterns. Headers support AND/OR logic; topic does not.

2. What does x-match=all mean?

All specified header values must match the message headers for routing. If any header is missing or different, the message is not routed.

3. When would you choose headers over topic?

When the routing decision depends on multiple independent attributes. For example, route by format AND source AND priority.

4. Can headers exchange match on header existence alone?

Not directly. You must specify a value. Use a sentinel value like true or exists to match on existence.

Challenge

Design a headers exchange for a document processing pipeline. Documents have type (invoice, report, contract), sensitivity (public, internal, confidential), and language (en, es, fr). Define bindings for automated processing, archival, and manual review.

FAQ

What header value types are supported?

String, integer, decimal, boolean, array, and table (nested dict). Type is inferred from the value.

Can headers exchange match on multiple values?

Not directly. Use multiple bindings with different values to achieve match-any-of behavior.

Is headers exchange slower than direct?

Yes, 10-30% slower due to header comparison overhead. Acceptable for most use cases.

Do headers work across RabbitMQ versions?

Headers exchange has been available since RabbitMQ 1.0. The feature is stable and well-tested.

Can I use routing keys with headers exchange?

Routing keys are ignored. All routing is done by header matching.

Mini Project: Multi-Attribute Router

import pika, json, threading, time

def start_consumer(name, exchange, binding_args):
    conn = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    ch = conn.channel()
    ch.exchange_declare(exchange=exchange, exchange_type='headers', durable=True)
    q = ch.queue_declare(queue=name, durable=True)
    ch.queue_bind(exchange=exchange, queue=q.method.queue, arguments=binding_args)

    def cb(c, m, p, body):
        print(f"[{name}] {body.decode()} (headers: {p.headers})")

    ch.basic_consume(queue=q.method.queue, on_message_callback=cb, auto_ack=True)
    ch.start_consuming()

consumers = [
    ('premium_json', {'x-match': 'all', 'format': 'json', 'tier': 'premium'}),
    ('any_partner', {'x-match': 'any', 'source': 'partner', 'tier': 'premium'}),
]
for name, args in consumers:
    t = threading.Thread(target=start_consumer, args=(name, 'docs', args), daemon=True)
    t.start()

time.sleep(1)
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
ch = conn.channel()
ch.exchange_declare(exchange='docs', exchange_type='headers', durable=True)

docs = [
    ({'format': 'json', 'tier': 'premium', 'source': 'internal'}, 'Premium JSON doc'),
    ({'format': 'csv', 'tier': 'standard', 'source': 'partner'}, 'Partner CSV doc'),
    ({'format': 'json', 'tier': 'standard', 'source': 'partner'}, 'Partner JSON doc'),
]
for h, b in docs:
    ch.basic_publish(exchange='docs', routing_key='', body=b,
                     properties=pika.BasicProperties(headers=h, delivery_mode=2))

time.sleep(1)

Expected output:

[premium_json] Premium JSON doc (headers: {'format': 'json', 'tier': 'premium', ...})
[any_partner] Partner CSV doc (headers: {'format': 'csv', ...})
[any_partner] Partner JSON doc (headers: {'format': 'json', ...})

What's Next

Now that you understand headers exchange, explore dead letter exchange for handling failed messages, then learn about message TTL for time-based message expiry.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro