Using Azure Functions v2 proxies to avoid CORS issues
Azure Functions proxies and the v2 runtime
One of the many great features of Azure Functions is the ability to define proxies. I've blogged before about how to use proxies for static website hosting, and I also used proxies in my Building Serverless Applications in Azure Pluralsight course, but until recently, proxies were not available in the newer v2 Azure Functions runtime.
However, proxies support for Azure Functions v2 was recently announced. This was perfect timing for me as I'm currently building a sample Durable Functions project for my upcoming talk at ProgNET London, and I wanted to create a simple webpage that called through to some HTTP Azure Functions to trigger durable workflows, check on their status, and send events to them.
Configuring CORS
In my case the webpage is just a static HTML page, so it can be hosted in a blob storage container. But if you've ever tried calling a an Azure Function from JavaScript on a webpage, then you'll know that your request is going to get blocked because of CORS.
There's two ways to get round CORS issues.
First, is simply to configure your Function App to allow CORS requests from the domain hosting the webpage. For cloud deployed Azure Function apps, you can do this in the Platform Features section of the Function App settings in the Azure Portal, and update the CORS settings to whitelist your domain. If you're testing your Azure Functions app locally, then you can make use of a command line parameter to configure CORS for the local tooling.
Using proxies
However, the second way around CORS issues is to make your webpages appear to be on the same domain as the Azure Functions they are calling by using proxies. It's very simple to set up, and a handy trick to have up your sleeve as proxies are useful in all kinds of situations, not just for avoiding CORS issues.
To get started with proxies, all you need is to create a proxies.json
file in the root of your Function App, alongside the host.json
file. If you're working in Visual Studio, don't forget to set the Copy to Output Directory
setting for proxies.json
to Copy if Newer
.
My website simply has an index.html
page, and a few images in an images/
folder. I'll likely add a css
and scripts
folder soon as well. I usually set up one proxy for the root path /
pointing to my index.html
, and one for each folder containing web assets using a wildcard to proxy calls to any route inside that folder. You might be wondering whether we could do everything with a single proxy definition. The trouble with doing that is that we don't want to proxy calls to the /api
route as they are heading to our functions.
Obviously, there are a few different ways you could tackle setting up the proxies, but here's how I set up my proxies.json
file.
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxyHomePage": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/"
},
"backendUri": "%WEB_HOST%/index.html"
},
"proxyImages": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/images/{*restOfPath}"
},
"backendUri": "%WEB_HOST%/images/{restOfPath}"
}
}
}
There's a few things worth highlighting here.
First, for the backendUri
setting, I'm using an application setting called WEB_HOST
. This is set to http://localhost:54045
in my local.settings.json
file. This allows me to proxy through to a simple ASP.NET Core webapp running my webpage while I'm testing locally. Then, when I deploy my application to Azure, I can set up an App Setting containing the URL of where the static web assets are stored in blob storage. For example: https://mystatichosting.blob.core.windows.net/container
.
Secondly, you'll notice the special {*restOfPath}
wildcard syntax on the route
which gives me a {restOfPath}
variable that can be used in the backendUri
setting (notice you don't need the *
character here).
Finally, a gotcha that had me stumped for a while is that to be able to use proxies that point to another port on localhost when you're testing locally, you need to create an app setting called AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL
with the value of true
. This should only be set in your local.settings.json
file though. I guess this is needed because localhost
has a special meaning in Azure Functions proxies, allowing you reference a function within the same function app without a roundtrip proxy request.
There's plenty more that can be achieved with Azure Functions proxies. They provide "override" settings to manipulate the request before it is proxied on, and the response before it is returned, although it's not a feature I've needed to make use of yet. But it's great that proxies are now available in Azure Functions v2.
Comments
Thanks for this post. I was wrestling with proxies for half a day to make it work locally.
MiladGBig thanks for mentioning AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL issue. This was the only search result I found about this issue.
Jānis Locāns