|
| 1 | +--- |
| 2 | +title: How to use SignalR Service with Azure Front Door |
| 3 | +description: This article provides information about using Azure SignalR Service with Azure Front Door. |
| 4 | +author: kevinguo-ed |
| 5 | +ms.author: kevinguo |
| 6 | +ms.date: 04/10/2025 |
| 7 | +ms.service: azure-signalr-service |
| 8 | +ms.topic: how-to |
| 9 | +--- |
| 10 | + |
| 11 | +# How to use Azure SignalR Service with Azure Front Door |
| 12 | + |
| 13 | +Azure Front Door is a modern cloud-native application delivery network (ADN) that provides dynamic site acceleration, global load balancing, TLS termination, and application layer security. It operates at the HTTP/HTTPS layer (Layer 7) and acts as the entry point for web applications—routing and optimizing traffic based on attributes such as URL paths, latency, and health status of backends. |
| 14 | + |
| 15 | +A key benefit of Azure Front Door is its native support for WebSocket and WebSocket Secure (WSS) connections. This support enables real-time, bi-directional communication between clients and backend services without requiring any special configuration. |
| 16 | + |
| 17 | +In this guide, we demonstrate how to use Azure Front Door with Azure SignalR Service to front-end your real-time applications. By routing traffic through Front Door, you can: |
| 18 | + |
| 19 | +- Apply WebSocket support with global reach and edge acceleration, |
| 20 | +- Apply centralized security policies, such as WAF rules and rate limiting, |
| 21 | +- Reduce public exposure of your backend services. |
| 22 | + |
| 23 | +As shown in the diagram, you configure Azure Front Door to route WebSocket traffic to your SignalR-powered application backend. This setup ensures that your real-time functionality benefits from low-latency, scalable, and secure traffic handling through Azure’s global edge network. |
| 24 | + |
| 25 | +## Set up and configure Azure Front Door |
| 26 | + |
| 27 | +### Create an Azure SignalR Service resource |
| 28 | + |
| 29 | +Follow [the article](./signalr-quickstart-azure-signalr-service-arm-template.md) and create a SignalR Service resource |
| 30 | + |
| 31 | +### Create an Azure Front Door resource |
| 32 | + |
| 33 | +On the [Azure portal](https://portal.azure.com/), search for **Front Door** and **Create**. |
| 34 | + |
| 35 | + :::image type="content" source="./media/signalr-howto-work-with-azure-front-door/create-front-door.jpg" alt-text="Screenshot of creating an Azure Front Door resource."::: |
| 36 | + |
| 37 | + |
| 38 | +### Quick test |
| 39 | +Conduct quick tests to verify that SignalR endpoint is healthy and Azure Front Door resource is correctly configured. |
| 40 | + |
| 41 | +Send a request to `<your-SignalR-resource-endpoint>/client` and it should return _400_ with error message _'hub' query parameter is required._ This message means that the request arrived at SignalR Service and the service performed validation as expected. |
| 42 | + ```bash |
| 43 | + curl -v <your-SignalR-resource-endpoint>/client |
| 44 | + ``` |
| 45 | + Returns |
| 46 | + ``` |
| 47 | + < HTTP/1.1 400 Bad Request |
| 48 | + < ... |
| 49 | + < |
| 50 | + 'hub' query parameter is required. |
| 51 | + ``` |
| 52 | +Send a request to the same health endpoint of Azure SignalR through Azure Front Door `http://<the-hostname-of-your-Azure-Front-Door-resource>/client`. Go to the Overview tab of the created Azure Front Door resource, and locate the endpoint hostname. |
| 53 | + |
| 54 | + :::image type="content" source="./media/signalr-howto-work-with-azure-front-door/afd-hostname.jpg" alt-text="Screenshot of the the hostname of Azure Front Door resource"::: |
| 55 | + |
| 56 | + ```bash |
| 57 | + curl -I http://<the-hostname-of-your-Azure-Front-Door-resource>/client |
| 58 | + ``` |
| 59 | + It should also return _400_ with error message _'hub' query parameter is required._ This message confirms that the request successfully went through Azure Front Door to SignalR Service. |
| 60 | + |
| 61 | + ``` |
| 62 | + < HTTP/1.1 400 Bad Request |
| 63 | + < ... |
| 64 | + < |
| 65 | + 'hub' query parameter is required. |
| 66 | + ``` |
| 67 | + |
| 68 | +### Run a SignalR sample app through Azure Front Door |
| 69 | + |
| 70 | +Now that we can verify that the traffic can reach SignalR Service through Azure Front Door. Next, we use a barebone sample app to demonstrate Azure Front Door's ability to route WebSocket traffic without configuration. We take a step-by-step approach so that you can follow along, if needed. |
| 71 | + |
| 72 | +#### Create the project |
| 73 | +```bash |
| 74 | +mkdir afd-demo |
| 75 | +cd afd-demo |
| 76 | + |
| 77 | +touch afd-demo.csproj |
| 78 | +``` |
| 79 | + |
| 80 | +Paste in the content to the `afd-demo.csproj` file. This project uses only the "Microsoft.Azure.SignalR" package. |
| 81 | +```json |
| 82 | +<Project Sdk="Microsoft.NET.Sdk.Web"> |
| 83 | + <PropertyGroup> |
| 84 | + <TargetFramework>net7.0</TargetFramework> |
| 85 | + <Nullable>enable</Nullable> |
| 86 | + <ImplicitUsings>enable</ImplicitUsings> |
| 87 | + <RootNamespace>afd_demo</RootNamespace> |
| 88 | + </PropertyGroup> |
| 89 | + <ItemGroup> |
| 90 | + <PackageReference Include="Microsoft.Azure.SignalR" Version="1.30.2" /> |
| 91 | + </ItemGroup> |
| 92 | +</Project> |
| 93 | +``` |
| 94 | + |
| 95 | +#### Configure app settings |
| 96 | +Create an `appsettings.json` file and paste in the content. The values will be referenced in the `Program.cs` file, which we create in the next step. |
| 97 | +```bash |
| 98 | +touch appsettings.json |
| 99 | +``` |
| 100 | + |
| 101 | +[!INCLUDE [Connection string security comment](includes/signalr-connection-string-security-comment.md)] |
| 102 | + |
| 103 | +```json |
| 104 | +{ |
| 105 | + "Azure": { |
| 106 | + "SignalR": { |
| 107 | + "ConnectionString": "<the-connection-string-of-your-Azure-SignalR-resource>" |
| 108 | + }, |
| 109 | + "AFD": { |
| 110 | + "Endpoint": "<the-endpoint-of-your-Azure-Front-Door-resource>" |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +#### Create `Program.cs` file |
| 117 | +```bash |
| 118 | +touch Program.cs |
| 119 | +``` |
| 120 | + |
| 121 | +Paste in the code to the `Program.cs` file. The web app defines a SignalR hub and serves `index.html` at the web root. |
| 122 | +```csharp |
| 123 | +using Microsoft.Azure.SignalR; |
| 124 | +var builder = WebApplication.CreateBuilder(args); |
| 125 | + |
| 126 | +// Reads in the configuration from `appsettings.json` |
| 127 | +var azureSignalRConnectionString = builder.Configuration["Azure:SignalR:ConnectionString"]; |
| 128 | +var afdEndpoint = builder.Configuration["Azure:AFD:Endpoint"]; |
| 129 | + |
| 130 | +builder.Services.AddSignalR().AddAzureSignalR(o => |
| 131 | +{ |
| 132 | + o.Endpoints = new Microsoft.Azure.SignalR.ServiceEndpoint[1] |
| 133 | + { |
| 134 | + new Microsoft.Azure.SignalR.ServiceEndpoint(azureSignalRConnectionString) |
| 135 | + { |
| 136 | + // Instructs SignalR server to return a `ClientEndpoint` to SignalR clients, with which they establish a connection. In our case, it's the endpoint of the Azure Front Door resource you just created. |
| 137 | + ClientEndpoint = new Uri(afdEndpoint), |
| 138 | + } |
| 139 | + }; |
| 140 | +}); |
| 141 | + |
| 142 | +var app = builder.Build(); |
| 143 | +app.UseStaticFiles(); |
| 144 | + |
| 145 | +app.UseRouting(); |
| 146 | + |
| 147 | +app.MapHub<DemoHub>("/demohub"); |
| 148 | + |
| 149 | +app.MapGet("/", async context => |
| 150 | +{ |
| 151 | + var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "index.html"); |
| 152 | + context.Response.ContentType = "text/html"; |
| 153 | + await context.Response.SendFileAsync(path); |
| 154 | +}); |
| 155 | + |
| 156 | +app.Run(); |
| 157 | +``` |
| 158 | + |
| 159 | +#### Define a SignalR hub |
| 160 | +```bash |
| 161 | +mkdir hubs && cd hubs |
| 162 | +touch demohubs.cs |
| 163 | +``` |
| 164 | + |
| 165 | +Paste in the code to the `demohubs.cs` file. For simplicity, the hub exposes only `BroadcastMessage` method to SignalR client, which broadcasts the received message to all connected SignalR clients. |
| 166 | +```csharp |
| 167 | +using Microsoft.AspNetCore.SignalR; |
| 168 | + |
| 169 | +public class DemoHub : Hub |
| 170 | +{ |
| 171 | + public Task BroadcastMessage(string message) => |
| 172 | + Clients.All.SendAsync("broadcastMessage", message); |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +#### Define Web UI |
| 177 | +Make sure you're at the root of the project folder. |
| 178 | +```bash |
| 179 | +mkdir wwwroot && cd wwwroot |
| 180 | +touch index.html |
| 181 | +``` |
| 182 | + |
| 183 | +Paste in the code to `index.html`. The user interface consists of a `<textarea>` to receive text input from user and a `<button>` to send the user input through a SignalR connection. Since we defined the SignalR server's behavior to broadcast received messages, you see the same message logged to the browser console. |
| 184 | +```html |
| 185 | +<!DOCTYPE html> |
| 186 | +<html> |
| 187 | + |
| 188 | +<head> |
| 189 | + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> |
| 190 | + <meta name="viewport" content="width=device-width"> |
| 191 | + <meta http-equiv="Pragma" content="no-cache" /> |
| 192 | + <meta http-equiv="Expires" content="0" /> |
| 193 | + <title>Azure SignalR with Azure Front Door as the reverse proxy</title> |
| 194 | +</head> |
| 195 | + |
| 196 | +<body> |
| 197 | + <div> |
| 198 | + <textarea id="message" style="display: block; width: 100%; padding: 5px 10px; max-width: 400px; margin-bottom: 8px;" |
| 199 | + placeholder="Your message..."></textarea> |
| 200 | + <button id="btn-send" disabled>Send</button> |
| 201 | + </div> |
| 202 | + |
| 203 | + <!--Reference the SignalR library. --> |
| 204 | + <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script> |
| 205 | + |
| 206 | + <script type="module"> |
| 207 | + document.addEventListener("DOMContentLoaded", async function () { |
| 208 | + const connection = new signalR.HubConnectionBuilder() |
| 209 | + .withUrl("/demohub") |
| 210 | + .build(); |
| 211 | +
|
| 212 | + connection.on("broadcastMessage", (msg) => { |
| 213 | + console.log(msg) |
| 214 | + }) |
| 215 | + await connection.start(); |
| 216 | + document.getElementById("btn-send").removeAttribute("disabled") |
| 217 | +
|
| 218 | + document.getElementById("btn-send").addEventListener("click", () => { |
| 219 | + const message = document.getElementById("message").value |
| 220 | + if (message !== "") { |
| 221 | + connection.send("broadcastMessage", message) |
| 222 | + document.getElementById("message").value = "" |
| 223 | + } else { |
| 224 | + alert("Message body is empty. Please enter message.") |
| 225 | + } |
| 226 | + }) |
| 227 | + }) |
| 228 | + </script> |
| 229 | +</body> |
| 230 | + |
| 231 | +</html> |
| 232 | +``` |
| 233 | + |
| 234 | +#### Run the app and verify the flow of message through Azure Front Door |
| 235 | +That is all the code to the sample. Let's run the app. |
| 236 | + |
| 237 | +```bash |
| 238 | + dotnet restore |
| 239 | + dotnet run |
| 240 | +``` |
| 241 | + |
| 242 | +Open http://localhost:5129 from the browser and use `F12` keyboard shortcut to open developer tools. Head to the network panel, you can see that the WebSocket connection is indeed established with Azure Front Door. |
| 243 | + |
| 244 | + :::image type="content" source="./media/signalr-howto-work-with-azure-front-door/network-panel-afd.jpg" alt-text="Screenshot of the running app establishing a WebSocket connection with Azure Front Door."::: |
| 245 | + |
| 246 | +Try to type something in the text box and hit the send button. You see the message is logged to browser console as expected. |
| 247 | + |
| 248 | + :::image type="content" source="./media/signalr-howto-work-with-azure-front-door/console-log.jpg" alt-text="Screenshot of the received message in browser's console log."::: |
| 249 | + |
| 250 | +You can also inspect the flow of messages in the network panel. |
| 251 | + |
| 252 | + :::image type="content" source="./media/signalr-howto-work-with-azure-front-door/network-panel-flow-of-messages.jpg" alt-text="Screenshot of flow of messages in the network panel."::: |
| 253 | + |
| 254 | + |
| 255 | + |
0 commit comments