MapsMessaging PROXY Protocol Support (v1/v2)
This document describes what the PROXY protocol solves, where it fits in MapsMessaging (across MQTT/AMQP/STOMP/NATS/etc.), and how to configure common fronting proxies (NGINX / HAProxy).
What problem PROXY protocol solves
When MapsMessaging is deployed behind a TCP/UDP proxy or load balancer, the connection seen by Maps often appears to originate from the proxy’s IP address, not the real client.
That breaks (or weakens):
- Access control / allow-lists by source IP
- Audit trails (“who connected from where?”)
- Rate limiting / abuse detection per client address
- Tenant/account enforcement tied to network origin
- Geo/IP-based policy, if you use that kind of thing
- Debugging (every client looks like
nginxorhaproxy)
The PROXY protocol fixes this by letting the proxy prepend a small header to the connection that contains:
- client (source) address + port
- proxy/listener (destination) address + port
- address family / transport information (v2 can represent TCP or UDP)
MapsMessaging reads the PROXY header once at connection start, then treats the connection as if it came directly from the client.
Where it fits in MapsMessaging
MapsMessaging supports multiple protocol listeners (examples: MQTT 3/5, AMQP, STOMP, NATS, NMEA-ish transports, etc.).
The PROXY protocol is not a separate application protocol. It is a small prefix header that appears before the actual protocol handshake.
So the flow is:
- Client connects to proxy (NGINX/HAProxy/…)
- Proxy connects to MapsMessaging
- Proxy sends a PROXY header (v1 or v2) first
- Proxy then forwards the client’s original bytes
- MapsMessaging parses the PROXY header (optional/required depending on config)
- MapsMessaging hands the remaining bytes to the actual protocol handler (MQTT/AMQP/STOMP/NATS/…)
This works with your existing “protocol sniffers” approach:
- PROXY detection behaves like other protocol detectors: it does not consume bytes unless it fully identifies and parses the header.
Supported PROXY versions
PROXY v1 (text)
- ASCII header starting with:
PROXY - Primarily used for TCP transports
- Common with older or simpler setups
PROXY v2 (binary)
- Binary signature and structured fields
- Supports TCP and UDP (transport indicated in header)
- Preferred for strictness and efficiency
MapsMessaging supports both v1 and v2.
Proxy protocol modes in MapsMessaging
MapsMessaging supports three operational modes:
DISABLED
- MapsMessaging does not attempt PROXY parsing.
- Any PROXY header sent will be interpreted as part of the application protocol and will usually cause protocol negotiation to fail.
Use this when:
- Maps is directly exposed (no L4 proxy in front), or
- Your proxy is not configured to send PROXY headers.
REQUIRED
- MapsMessaging expects a PROXY header for the connection.
- If the header arrives fragmented, MapsMessaging will wait (read-more) until it can fully parse it.
- If the connection does not provide a valid PROXY header, the connection should fail fast (policy decision).
Use this when:
- You always deploy Maps behind a proxy that is configured to send PROXY headers, and
- You want strict enforcement and consistent real-client addressing.
ENABLED
- MapsMessaging accepts PROXY headers if present, but allows direct connections without them.
- If the header arrives fragmented, ENABLED mode may “give up” early and proceed with protocol detection (policy choice).
Use this when:
- Mixed deployments are expected (some clients via proxy, some direct), or
- You are transitioning a deployment to use PROXY protocol.
Note: In ENABLED mode, if the proxy header arrives fragmented across multiple reads, MapsMessaging may choose to proceed without it (trade: fewer stalls, but you may not get client IP for that connection). In typical LAN deployments this is uncommon.
Operational guidance
- If you enable PROXY on the proxy, set MapsMessaging to REQUIRED unless you truly need mixed direct/proxy connections.
- If you must allow both proxy and direct, use ENABLED and prefer a proxy that sends PROXY headers in a single write (usual behaviour).
- Ensure the proxy is L4 pass-through for the target protocol, unless you are intentionally terminating TLS and re-originating.
NGINX configuration (TCP) using stream
NGINX handles TCP/UDP proxying via the stream module.
Example: MQTT (TCP/1883) with PROXY protocol
This example assumes:
- clients connect to NGINX on
1883 - NGINX forwards to MapsMessaging at
maps:1883 - NGINX sends PROXY headers to MapsMessaging
stream {
upstream maps_mqtt_1883 {
server maps:1883;
}
server {
# Accept client TCP connections
listen 1883;
# Forward to Maps
proxy_pass maps_mqtt_1883;
# Send PROXY protocol to Maps (v1/v2 depends on NGINX build/features; commonly v1)
proxy_protocol on;
# Optional: timeouts
proxy_connect_timeout 5s;
proxy_timeout 1h;
}
}
Accepting PROXY from an upstream LB into NGINX
If NGINX itself is behind something that already sends PROXY headers, NGINX can be configured to accept them on the listener:
stream {
server {
listen 1883 proxy_protocol;
proxy_pass maps:1883;
# If NGINX is also sending PROXY to Maps:
proxy_protocol on;
}
}
Notes
- If you only want NGINX to accept PROXY and not send it onward, omit
proxy_protocol on;. - If you terminate TLS at NGINX and forward cleartext, PROXY still works. If you pass-through TLS, PROXY still works, because it is prepended before the TLS handshake.
HAProxy configuration
HAProxy is commonly used for L4 load balancing and has excellent PROXY protocol support.
Example: Front Maps MQTT listener and send PROXY v2
frontend fe_mqtt
bind :1883
mode tcp
default_backend be_mqtt
backend be_mqtt
mode tcp
server maps1 maps:1883 send-proxy-v2
If HAProxy must receive PROXY from an upstream and forward it
frontend fe_mqtt
bind :1883 accept-proxy
mode tcp
default_backend be_mqtt
backend be_mqtt
mode tcp
server maps1 maps:1883 send-proxy-v2
Notes
accept-proxymeans HAProxy expects PROXY on inbound connections.send-proxy/send-proxy-v2controls what HAProxy sends to the backend (Maps).
How this applies across MapsMessaging protocols
Because PROXY is parsed before protocol negotiation, the same approach applies to any protocol listener that runs over a proxied transport:
- MQTT 3 / 5 (TCP)
- AMQP (TCP)
- STOMP (TCP)
- NATS (TCP)
- Other TCP-based protocol listeners
For each MapsMessaging listener:
- set the listener’s proxyProtocolMode to one of:
DISABLED,ENABLED,REQUIRED - ensure the proxy is configured to send PROXY headers when required
Troubleshooting
Symptom: All clients appear to originate from the proxy IP
- Proxy is not configured to send PROXY headers, or
- MapsMessaging proxy mode is DISABLED, or
- Proxy is terminating/re-originating without PROXY headers
Symptom: Clients cannot connect when proxy mode is REQUIRED
- Proxy not sending PROXY headers
- Proxy sending PROXY v1 but Maps expects only v2 (Maps supports both, so this would usually be a proxy-side issue)
- PROXY header is blocked/modified by a middlebox
- Listener mismatch (proxy is sending PROXY to a port that isn’t configured for it)
Symptom: ENABLED mode “sometimes” misses real client IP
- Proxy header is arriving fragmented; Maps chooses to proceed without PROXY in ENABLED mode (policy)
- Increase buffering/handshake strategy if you later decide this is important
Summary
- PROXY protocol preserves the real client address when MapsMessaging is behind a proxy/LB.
- MapsMessaging supports PROXY v1 and PROXY v2.
- MapsMessaging supports modes: DISABLED, ENABLED, REQUIRED.
- NGINX (
stream) and HAProxy can be configured to send (and/or accept) PROXY headers ahead of MQTT/AMQP/STOMP/NATS/etc.