Using Azure Application Gateway as a Gateway for Azure Service Bus
In many application landscapes, you see a combination of synchronous and asynchronous communication. Synchronous communication means: system A calls system B and waits for a response before proceeding. That’s simple, but quickly leads to scalability limitations.
That’s why more and more organizations are switching to asynchronous communication. In this case, a system sends a message to a queue and continues without waiting for an immediate response. The receiving system reads the messages at its own pace. A popular solution for this is Azure Service Bus. A robust messaging solution that offers queues and topics for decoupling systems.
And then it goes wrong...
In practice, the Service Bus is often set up as an internal integration layer within a single application or domain. Think of communication between microservices. That works fine as long as only internal components are reading or writing messages.
The problem arises as soon as external systems start using this same Service Bus. This happens more often than you might think. Other teams, other applications – sometimes even external vendors – connect directly to the bus. You then see something like this:
The Service Bus, originally intended as an internal mechanism, now becomes a shared integration layer. Consequence: it becomes unmodifiable. If you change something in the setup of your system, it impacts external systems. And if an external party is involved, you’ll face processes beyond your control: coordination, waiting times... In short: more bureaucracy.
The solution: centralize access
The solution is simple: place an Azure Application Gateway in front of your Service Bus. Don’t let external systems connect directly to your Service Bus, but go through this gateway. This way, you maintain control over the endpoint while being free to change or refactor things behind the scenes.
Benefits: – You can introduce an alternative (drop-in replacement) messaging solution without external parties noticing. – You prevent internal components from needing to be publicly accessible.
What you need
Azure Service Bus supports AMQP over WebSockets. That’s important because Azure Application Gateway does not support native AMQP traffic, but it does support HTTPS and WebSockets.
Additionally, your Service Bus must be in a VNet. Note: only the Premium SKU supports this. So make sure you’re using this version.
Configuring Application Gateway
Create a new Application Gateway
Use at least the Standard_v2 SKU, as it supports communication via your VNet. Choose or create a public IP address.
Configure a backend pool and routing rule
Use your own (sub)domain, such as servicebus.akkerweg6.nl
. Choose HTTPS. Certificate management via Key Vault is out of scope for this blog, but assume this is already arranged.
Add a health probe
Azure Service Bus doesn’t have a standard health endpoint, but /$namespaceinfo
works fine. If you get a 401 or 403 response, you know the Service Bus is alive (the gateway can’t authenticate, so this status is expected).
Testing with a Kotlin application
With the Kotlin code below, I demonstrate how to read messages from a queue via the Application Gateway. The Service Bus is private; only traffic through the gateway is allowed.
import com.azure.core.amqp.AmqpTransportType
import com.azure.messaging.servicebus.ServiceBusClientBuilder
import com.azure.messaging.servicebus.models.ServiceBusReceiveMode
fun main() {
println("Starting Azure Service Bus client with custom gateway configuration")
val connectionString = "Endpoint=sb://<REPLACE WITH YOUR OWN CONNECTION STRING>"
val queueName = "blog"
val receiverClient = ServiceBusClientBuilder()
.connectionString(connectionString)
.customEndpointAddress("https://servicebus.akkerweg6.nl")
.transportType(AmqpTransportType.AMQP_WEB_SOCKETS)
.receiver()
.receiveMode(ServiceBusReceiveMode.PEEK_LOCK)
.queueName(queueName)
.buildClient()
receiverClient.use {
println("Connected to Service Bus queue: $queueName")
receiverClient.peekMessages(10).forEachIndexed { i, message ->
println("Message #$i:")
println(" Message ID: ${message.messageId}")
println(" Sequence #: ${message.sequenceNumber}")
println(" Content: ${message.body}")
println(" Properties: ${message.applicationProperties}")
println(" Enqueued Time: ${message.enqueuedTime}")
println()
}
}
}
Note:
– customEndpointAddress
points to your Application Gateway.
– AMQP_WEB_SOCKETS
is essential. Without this setting, HTTPS traffic won’t work.
– In production, it’s recommended to use Managed Identity + RBAC instead of connection strings.
Example output
Starting Azure Service Bus client with custom gateway configuration
Connected to Service Bus queue: blog
Message #0:
Message ID: 34d9c105556c4e829400c0d10fb6eb56
Sequence #: 2
Content: Welcome!
Properties: {}
Enqueued Time: 2025-06-28T10:12:45.778Z
Message #1:
Message ID: 50d8340b93c54f078473059f6332a0ee
Sequence #: 3
Content: You've received this message through the app gateway, awesome!
Properties: {}
Enqueued Time: 2025-06-28T10:13:05.778Z
Summary
By placing an Application Gateway in front of your Service Bus, you create a secure, flexible, and manageable integration layer. External systems communicate via a stable endpoint while you retain the freedom to innovate or refactor internally. This way, you avoid tight coupling and maintain control over your architecture.