Node.js and Redirects or Rewrite scenarios on App Service Linux
Sometimes due to specific needs, you may want to implement a URL redirect or rewrite on the server side. For instance, redirecting from www to non-www, or the same between HTTP and HTTPS.
It is important to note, in general, that with Node App Service on Linux that the environment is a Docker Container which includes Node itself (along with your code) and also PM2, a production process manager. Also called our “Blessed Images”.
However, these Node ‘Blessed Images’ do not include resources that can utilize other typical redirect/rewrite methods such as .htaccess
or web.config
, since .htaccess
is for Apache, which is not included in these Images and web.config
is for IIS, which runs on Windows.
Knowing that we can still utilize some other methods for what we need, mentioned below:
Code-based approach
NOTE: This approach assumes you’re running a typical Node server or framework, like the native http/s module, Express, Hapi, or related.
If using something like Express.js and have your controllers set up for routing, the below approach can be used, which is using an exported function that we can pass back into our routing middleware. req, res, next
will be available since this function can be passed as an argument directly to Express middleware. This example is redirecting from non-www to www:
const redirectController = (req, res, next) => {
try {
const host = req.headers.host;
if (host.match(/^www\..*/i)) {
next();
} else {
res.redirect(301, `${req.protocol}://www.` + host + req.url);
}
} catch (error) {
console.log("An error has occurred: ", error);
next(error);
}
};
module.exports = redirectController;
What the code is doing
- We assigned the variable
host
to the value ofhost
in our request headers (optional) - We use regular expressions to see if
host
containswww.
- If it does, we call
next()
to process the rest of our middleware chain and skip any redirect logic - If it does not contain
www.
, we redirect with a HTTP 301 to our intended URL
And then in our entrypoint .js
file (which can be any file, i.e index.js
, server.js
, app,js
, etc.), we have it structured as the following:
... other code
const express = require("express");
const app = express();
... controller imports
... other code
// Controller to capture our redirect logic
app.use(redirectController);
// Home '/' controller
app.use(homeController);
// API controllers
app.use("/api/todo/all", getTodoController);
app.use("/api/todo/find/", getTodoByIdController);
app.use("/api/todo/delete/", deleteTodoController);
app.use("/api/todo/add", addTodoController);
app.use("/api/todo/update", updateTodoController);
// Catches all non matching routes and redirects it back to the root - must be placed last in the chain of middleware
app.use(catchAllController);
... other code
NOTE: Order matters here. It’s important to understand your placement of these middleware components can affect your application logic. In the above example, if you’re using a ‘catch all’ (optional) route which is seen in the last of the chain, it needs to remain on the bottom. Our non-www to www logic is placed in the beginning so it can be processed first and then the rest of the flow is executed.
Using the above approach you should now able to have non-www requests redirect to www while preserving the existing path, if any. You can change this as desired in your code as needed, simply be changing the regular expression pattern.
For more context you can refer to these StackOverflow posts which cover different approaches as well:
This same logic can be used with either HTTP <-> HTTPS redirection.
Key Takeaways
- You can use regular expressions plus Node’s default routing logic or other middleware such as Express’s to redirect as need based on the logic you write
- Be mindful of how you write your code for this, since all logic for this routing is within the application layer
- Try to avoid hardcoding values in the redirect function logic, Node offers the
req
object and from that we can extract essentially all parts of the URL as needed - If you have a high trafficked application, this may incur a performance hit as all requests are routed within the middleware or logic flow and not before it hits the application code, such as with an Application Gateway
Single Page Application (SPA) approach using a ‘Blessed Image’
The ‘code based’ approach will handle most scenarios with Node as long as there is a live Node server running. But what happens if you have a Single Page Application and have those same requirements? This makes it more tricky. But it is still possible without the use of another product placed in front of the App Service.
This approach should work for most Single Page Applications, since most generate a compiled folder of static files (i.e /build
, /dist
, etc.) which is our main focus, and should be, as these are the assets recommended to serve in production.
Using a PHP 7.x ‘Blessed Image’ to make use of .htaccess
with our SPA
NOTE: Since we’re mixing a PHP ‘Blessed Image’ with Javascript(Node), there will be various tweaks/changes that will need to be done for this to work. Therefor a typical deployment (i.e Local Git) will not work out of the box.
Sample code:
- This example is intended for a PHP 7.x ‘Blessed Image’ and in this case, using React. No
php
files should be deployed. - For a walkthrough and a code sample - you can review this Github Repo. This contains a description on how to deploy and run this on Azure App Services.
Key Takeaways
- The sample in the link above is using React, but other SPAs (Angular, Vue, etc.) should be valid to use as well, as long as
DocumentRoot
inapache2.conf
is updated to match your production folder name. - Typical deployments, eg. Local Git will not be able to be done as Oryx with Node looks for particular files and additionally since this is PHP - this logic will not apply.
- This is not intended to cover ‘live’ Node servers, rather static applications (SPAs).
- You can get a copy of
apache2.conf
by SSH’ing into your application container, this will be under/etc/apache2/apache2.conf
. For PHP 8.x ‘Blessed Images’, you can get a copy ofnginx.conf
by navigating to/etc/nginx/nginx.conf
.
NOTE: Our PHP 8.x ‘Blessed Images’ use NGINX. The above can also be done but specifically with NGINX rather than Apache.
Custom Docker Image
A custom Docker Image that you can bring with Azure Web App for Containers can be used. Using your own custom Docker Image, you can build your image using either something like NGINX or Apache to implement your redirects or rewrites as needed instead of relying upon ‘Blessed Images’.
With this approach, you can make this work with either SPA based apps or server running apps. Below are two examples, but of course there are many other ways of doing this:
Key Takeaways
- Custom Images give more flexibility since you can pick and choose the software desired
- This however also requires time spent to develop these Docker Images with your intended software stack
Azure Application Gateway
Azure Application Gateway can also be used for URL based routing, rewrites and redirection, amongst others. The benefit of using this is that Application Gateway is placed infront of the application itself. So any rewrites, redirects, or others - or all done prior to hitting the application. Which can help as opposed to doing these same things through code, which could have a possible performance impact, or having to take a roundabout way for this approach
Restrict Access to .azurewebsites.net
One other good benefit of using this is that since we don’t have the benefits of a web.config
on Linux to redirect away from the bare *.azurewebsites.net domain and needing to use Apache or NGINX either requires a lot of changes or using a custom Image, we can utilize Application Gateway to handle the incoming traffic through the gateway - while restricting access to the App Service so the Application Gateway VIP is the only one with access.
This is called out here, which also utilizes built-in Azure App Service access restrictions. Additional documentation for App Service and Application Gateway can be located here.
Key Takeaways
- Azure Application Gateway to handle redirects, rewrites and URL based routing.
- This can be placed in front of the application to which changes to code or using a non-typical approach for the above can be avoid.
- It additionally can be a method of sending traffic only through the Application Gateway while restricting access to the default *.azurewebsites.net domain name.
In summary
- You can use a code centric approach for dynamic redirects if using a live Node.js based server.
- If using a Single Page Application with static, client-side content - you can utilize a PHP 7.x ‘Blessed Image’ to make use of Apache and PHP 8.x ‘Blessed Images’ to make use of NGINX - with additional configuration, you can override the
apache2.conf
andnginx.conf
files respectively to get what you need. - You can bring your own custom Docker Image to build it to include Apache, NGINX, or others to help with redirects and/or rewrites
- If none of the above suite you - Azure Application Gateway can do all of the above and additionally, if configured, force traffic through the gateway to block traffic to *.azurewebsites.net for your App Service.
- There is also the option of using Node on Windows to make use of a
web.config
, in the case that Linux App Service and Node.js can’t do what you need regarding the above.