Thumbnail Fixing the Nginx trailing slash issue on Linux based Azure Web Apps

Fixing the Nginx trailing slash issue on Linux based Azure Web Apps

I recently tried to deploy a statically generated site to an Azure Web App / App Service based on Linux. Because Linux Web Apps do not offer a runtime stack specifically for static files, I chose the PHP stack instead. It is the only runtime stack that can just serve files without any configuration… or so I thought. I immediately ran into trouble. My static pages would sometimes take seconds to load, redirecting to a URL with a port number in it: https://staticsite.com:8080/subfolder/ and displaying a ERR_CONNECTION_TIMED_OUT error page.

After some testing, I discovered that this only happened for urls without trailing slash. For example: https://staticsite.com/subfolder/ would navigate to the intended page without problems. It was https://staticsite.com/subfolder the problems occurred with. So what’s going on here? And how can it be fixed?

The PHP code stack can serve static files without configuration


The PHP runtime stack can serve static files without configuration

The problem occurs because of a (mis)configuration of Nginx. Nginx is the webserver that’s being used with the PHP 8 runtime stack. And it’s running / listening to traffic, using the 8080 port. You can check this out by opening the Web SSH Console from Azure and running nginx -T. This will show the entire Nginx server configuration:

Using the web ssh console


Using the Web SSH Console you can view the Nginx server configuration

The server configuration looks more or less as follows:

server {
    listen 8080;
    listen [::]:8080;
    
    root /home/site/wwwroot;
    index  index.php index.html index.htm;

    #...omitted for brevity
}
Did you know: Linux web apps are running on docker containers. (Whether or not you chose to publish your app as a docker container) Every stack has it's own preconfigured docker image. The image is built and the container started when you start the site. In the case of PHP 8 / Nginx, port 8080 is mapped to the 8080 port of the container.

A great explanation of what is happening can be found here. It turns out that Nginx by default uses absolute urls for redirecting users. This is what happens when a user accesses a url without a trailing slash: Nginx tries to redirect the user, but uses an absolute url, even including the port number. This is apparently problematic on app services, possibly because they are running behind a load balancing mechanism by default and/or don’t open up port 8080 to the outside world.

However the precise mechanics of App Service Architecture, the absolute redirect fails.

Fortunately, this problem can be solved.

Of course this is more of a workaround, but you can configure the Linux app service to use the PHP 7 runtime stack. This runtime stack uses an Apache webserver instead of the Nginx one, and does not have this specific issue.

Update 06-12-2021: This stack is on the way out! PHP 7.4 extended support on Windows will end on 28 November 2022, any applications hosted on Azure App Service that are still using it will be unsupported after 28 November 2022.

The more interesting solution would be to change the Nginx configuration itself. This is harder than it would seem. You cannot just add a config file to your site root, like you would add a web.config or .htaccess file.

You could get to the configuration file using SSH, but it’s located in a subfolder of the linux rootsystem /etc/nginx, where changes to the files do not get persisted. Every time the app service restarts, the configuration files would be reset. (Changes are only persisted in the /home/ part, where the actual website files are stored.)

So how can we change the config? We can do so by letting the app service execute a script for us, on app service startup. This blog tought me how to do it..

So what I did was the following:

I created a bash script in VS code that uses the sed string replace tool to insert the absolute_redirect off; statement into the Nginx configuration file. Next, it instructs Nginx to reload the configuration. The code looks as follows:

#!/bin/bash
echo "[CUSTOM STARTUP SCRIPT] Fixing trailing slash issue by adapting Nginx configuration file"

replace="server {"
replaceWith="server { absolute_redirect off;"

sed -i "s/${replace}/${replaceWith}/g" /etc/nginx/sites-available/default

echo "[CUSTOM STARTUP SCRIPT] Reloading nginx to apply new configuration"

service nginx reload

default is the configuration file and sed -i does an in place string replace. So it updates the file where it resides. As you can see, I’m updating the file in the sites-available folder. When running nginx -T in the SSH console, you’ll see the Nginx config file originating from the sites-enabled folder. This folder is symlinked to the sites-available folder, so the sites-enabled/default file will show the updated config as well. Running nginx -T after the startup confirms this.

Note: If you copy this code do not forget to set VS Code to save it using `LF` line endings. Otherwise, the script will fail.

I then uploaded the script to the root folder of my site, using my existing Azure Pipeline.

The last step was to reference the script as startup command:

Using App Service Startup commands

My startup command:

/bin/bash /home/site/wwwroot/startup_script.sh

Using the Log stream in the Azure portal I could see that my script executed just fine, also when restarting the app service.

It’s a strange issue, and I hope the Azure team will fix it soon. But using the startup command is actually quite flexible, and gives you a lot of options to edit the configuration files a bit.

I can now safely use urls without trailing slashes! Plus I learned a lot diving into this.

Happy coding!


webapp linux nginx
Support me by sharing this

More

More blogs

Creating a beautiful documentation site with MkDocs
Creating a beautiful documentation site with MkDocs

MkDocs is a great tool to create straightforward documentation sites. It's 1 of 2 static site generators I've recently worked with.

Read more
Fixing an Azure web app zip deployment issue
Fixing an Azure web app zip deployment issue

My Azure DevOps pipeline recently stopped zipdeploying to Azure App Service. This is how I fixed it.

Read more
Quick tip: how to deploy to web app subfolders
Quick tip: how to deploy to web app subfolders

A quick tip on the possibilities of deploying to web app subfolders in Azure DevOps pipelines.

Read more

Thanks

Thanks for reading

Thanks for reading my blog, I hope you got what you came for. Blogs of others have been super important during my work. This site is me returning the favor. If you read anything you do not understand because I failed to clarify it enough, please drop me a post using my socials or the contact form.


Warm regards,
Martin

Microsoft MVP | Microsoft 365 Architect

Microsoft MVP horizontal