web.config rewrite rule not working in Azure static website in blob storage
Asked Answered
W

5

11

I have a single page React app hosted in Azure blob storage but am getting an The requested content does not exist. error when deep linking into a page:

enter image description here

I've enabled static website option in the storage account:

enter image description here

The files are all in place in the $web container:

enter image description here

This includes the following web.config with a rewrite rule that is supposed to let index.html handle all the routing:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="React Routes" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" appendQueryString="true" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Can anyone see where I've gone wrong?

Wu answered 9/4, 2019 at 17:52 Comment(0)
V
3

The static website in Azure Storage GPv2 is different from Azure App Service. It only hosts these static web files which include HTML/CSS/JavaScript files, other files can be handled in browser like images, robots.txt, etc. It has inability to process server-side scripts, due to there is not IIS. So your web.config file is no sense for it to change the access routing and be belong to server-side script for IIS.

Actually, you can see the words in Azure portal.

Configuring the blob service for static website hosting enables you to host static content in your storage account. Webpages may include static content and client-side scripts. Server-side scripting is not supported in Azure Storage. Learn more

And refer to the Learn more link of Static website hosting in Azure Storage

In contrast to static website hosting, dynamic sites that depend on server-side code are best hosted using Azure App Service.

I recommended using Azure App Service for your app if requires the URL-rewrite feature.

Victor answered 10/4, 2019 at 6:47 Comment(0)
A
16

You can do the deep linking if you point the 404 page back at your index.html. This is not a perfect solution - has side effects - but it will work for the majority of cases.

enter image description here

Afc answered 13/6, 2019 at 15:44 Comment(5)
This helped me out as well, care to explain the side effects (except not having a proper 404?)Rimskykorsakov
Nope... I already had "Error document path" pointing to my "index.html" file, but still get the 404 error, when try to navigate directly to webpages (routes) inside my Angular app.Livia
@PeterBoomsma The side effect is that when accessing a deep url directly (e.g. from an external link, bookmark etc) the site will most likely work because index.html is returned but it is returned with http status 404. That is probably not a big problem, but it's not something I personally would be pleased with.Gerdi
The most important side effect is that SEO doesn't like 404s. This means that your site will certainly not rank very well on google search.Serosa
If you're hosting a PWA app this way, it doesn't appear to work offline once "installed" on your phone.Alfieri
V
3

The static website in Azure Storage GPv2 is different from Azure App Service. It only hosts these static web files which include HTML/CSS/JavaScript files, other files can be handled in browser like images, robots.txt, etc. It has inability to process server-side scripts, due to there is not IIS. So your web.config file is no sense for it to change the access routing and be belong to server-side script for IIS.

Actually, you can see the words in Azure portal.

Configuring the blob service for static website hosting enables you to host static content in your storage account. Webpages may include static content and client-side scripts. Server-side scripting is not supported in Azure Storage. Learn more

And refer to the Learn more link of Static website hosting in Azure Storage

In contrast to static website hosting, dynamic sites that depend on server-side code are best hosted using Azure App Service.

I recommended using Azure App Service for your app if requires the URL-rewrite feature.

Victor answered 10/4, 2019 at 6:47 Comment(0)
S
3

Although the accepted answer is right when it says that static websites don't process nicely the web.config, it is false that you cannot do what you want using a static website. Here's what you can do:

  1. Click on the 'Azure CDN' tag (below Static Website)
  2. Create a new CDN Profile (be careful to select the WEB (static website) link and not BLOB.
  3. Enter the newly CDN profile on 'All Resources'.
  4. Click on the single endpoint you see in 'Overview'.
  5. Click on 'Rules Engine'
  6. Add the following rule:

enter image description here

Credits: My answer is based on Andreas Wendl answer which you can find here (https://mcmap.net/q/281058/-hosting-spa-with-static-website-option-on-azure-blob-storage-clean-urls-and-deep-links). I added some detail because after checking his answer it took me a couple of hours to understand exactly what I had to do.

There you go, you can now see perfectly the screen Andreas is speaking of

Serosa answered 31/7, 2020 at 18:50 Comment(4)
Note there is a limit of 25 rules and a charge of $1 per month per rule.Juvenescence
I didn't know that @John, thanks! It doesn't affect me as I have $150 to spend per month.Serosa
@john - Azure CDN lets you have 5 rules for free and then it's $1 per rule + .60 per million requests learn.microsoft.com/en-us/azure/cdn/…Cymose
Excellent solution! This should be the flagged solution.Odelle
W
2

I have multi-lingual Angular application with deep linking (which should be accessed directly) and I did the following:

Folder structure in the $web contrainer:

. ru-UA
. uk
. en-US
. index.html
  1. Enable 404 to the index.html in the static website setup in the Azure Portal
  2. On your root index.html add the next code:
<html>
<head>
<script>
 if (window.location.pathname != null) {
     const segments = window.location.pathname.split('/').filter(x => x.length > 0);
     const languages = ['ru-UA', 'uk', 'en-US'];

     if (languages.indexOf(segments[0]) >= 0)
     {
         sessionStorage.setItem('path', window.location.pathname);
         window.location = '/' + segments[0] + '/';
     } else {
         window.location = '/uk/'; // It might be your default language
     }
 }
</script>
</head>
</html>
  1. Then, in your Angular add the next:
     const spaRedirect = sessionStorage.getItem('path');
     if (spaRedirect) {
         const path = spaRedirect.split('/').filter(x => x.length > 0).slice(1).join('/'); // slice(1) to remove language segment
         await this.router.navigate(['/' + path]);
         sessionStorage.removeItem('path');
     }

Now you can have deep linking.

How it works:

  1. User enters in the browser: www.domain.com/uk/very/deep/link Azure
  2. Static Website engine can't find such blob and redirects to 400, which is root index.html -> www.domain.com/index.html
  3. Root index.html got full link via pathname property and split it to the segments.

    Actually, you can avoid splitting by segment, but for me it's just extra validation step.

  4. Then it redirect to Angular localized-specific index.html in appropriate folder and Angular Engine start to serve website -> www.domain.com/uk/index.html

    Please note, that you can have a delay here, cause you have to launch Angular with all dependencies and only after that redirect

  5. Your very basic component finds something in the sessionStorage.getItem('path') and make Angular-based redirect to the deep link. -> www.domain.com/uk/very/deep/link
Witting answered 4/4, 2020 at 20:3 Comment(0)
L
-3

None of these solutions worked for me, and I miserably started to accept that I wouldn't be able to host my Angular application (with Routing) just in Azure Storage.

But, actually, I found a solution.

You need to manually add a web.config file to your src folder (yes, even though we're not hosting this on an IIS server)

 <configuration>
    <system.webServer>
        <rewrite>
          <rules>
            <rule name="Main Rule" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
  </configuration>

...then add this file to the assets section in your angular.json file:

  "architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
        . . . 
        "assets": [
          "src/web.config"
        ],

Shockingly, this works now.

In my Angular app, I could click around to various sub-pages, eg

http://www.mikesapp.web.core.windows.net/Orders/List

and, crucially, I can also open a browser and go directly to this webpage.

Livia answered 5/11, 2019 at 20:23 Comment(5)
Can you elaborate please? Where does this web.config file end up after you build? Is it copied to the root of your $web container?Kristie
Yes, it ends up deployed to the root of the $web container.Livia
Does this mean that, while undocumented, Azure Static Site hosting uses IIS and your web.config works because of folder config inheritance?Jacquie
I've tested this and not found it to work. If the Error document path option in the Storage Account resource config is set to index.html or similar, though, 404 responses will use that file for their body. For an Angular, Next.js, etc. website, this would mean the page would load and the client-side JavaScript would handle the routing. It would work, but it wouldn't be ideal. For what it's worth, Azure static sites have an HTTP Server header value of "Windows-Azure-Web/1.0 Microsoft-HTTPAPI/2.0", which probably isn't IIS. I wouldn't be surprised if blob storage wasn't even Windows.Muzzy
I also tested this option and it doesn't workIdo

© 2022 - 2024 — McMap. All rights reserved.