So you have a website filled with images, CSS and JavaScript files, and you realize that you haven’t bothered optimizing the images or minified the CSS and JavaScript files. Or maybe you have, but your users can upload their own and they don’t get optimized/minified.

What’s the easiest way to go about that? Well, you could use tools like Web Essentials and PngGauntlet to help out, but that doesn’t solve the issue with user-uploaded files. You probably have to modify your website to include *.min.js files, commit them to source control, modify your website project and so on.

It would be much nicer if we didn’t have to worry about any of this and didn’t have to make any modifications to our website. It would be much nicer if it just happened automatically.

With Azure Websites that is now possible. Any web application hosted on Azure Websites no longer have to bother with these types of optimizations anymore.

It doesn’t matter if your website is running ASP.NET, PHP, Node.js or plain static HTML, it works for them all.

All there is needed is to install a NuGet package and publish the website to Azure. Here’s a video demonstrating how to add automatic image optimization.

The video shows how simple it really is to optimize images. To optimize CSS and JavaScript files, we can do the exact same thing, but with a different NuGet package.

Here’s what we need:

  1. Install NuGet package: Azure Image Optimizer
  2. Install NuGet package: Azure Minifier
  3. On a web application hosted on Azure Websites

How it works

Both the Image Optimizer and the CSS/JavaScript Minifier works the same way.

When they are installed and you publish to Azure Websites, an MSBuild trick makes sure to publish the Webjobs with your web application. As soon as that is done, Azure recognizes the Webjobs and starts them up.

The first time they start up, it can take a little while for them to finish the first pass of optimizations if you have a lot of files to optimize. You might even see the Webjobs restarting in the Azure portal. That’s ok, no problem. They start up immediately again and continues on where they left off.

The Image Optimizer supports .png,.gifand .jpg files. And the Minifier supports .js and .css files.

Server Explorer in Visual Studio shows us the Webjobs along with a log file and a cache file.


The log file is being written to every time a file has been optimized. You can open it by double clicking directly on the .csv file in Server Explorer. The cool thing about using a .csv file is that it an be opened in Excel, so you can easily do more calculations on the data.

The cache file (.xml) contains a list of files and their MD5 hash values. That ensures that the same files aren’t being optimized over and over again each time you publish or restart the WebJob.

If you have enabled Streaming Logs, then you can see the optimizations happen in real time directly within Visual Studio’s Output Window as well.

Open Source

As always, we keep our source code on GitHub and of course accept pull requests.

These features have been some that both Sayed and I have been wanting to add for a long time, but it was never possible before Microsoft introduced Azure Webjobs, because they required continuously running background tasks to work most reliably and in a way that scales.

The demo website used in the video is also open source and is great for playing around with these two optimizers yourself.

Happy optimizing!


Comment by Manny

Doesn't this example go against the 'correct' way you should structure your azure website? As far as I know, in an azure website you wouldn't normally let users upload files directly to disk but instead store it in blob storage. Because if you just store it on disk what happens when you want to scale out your website to multiple instances?

I can see the utility for fixed images that are deployed to all instances of the site. Does the webjob then run on each instance of an azure website?


Comment by Paul

Disappointed this is all Azure related.. "Azure" wasn't even mentioned until the 4th paragraph. Was hoping it was about better automation of optimization without needing Web Essentials for anyone, not just Azure customers...oh well.


Comment by Alex Dresko

I got an error from Windows Defender when installing AzureImageOptimizer.

PM> Install-Package AzureImageOptimizer -Pre
Attempting to resolve dependency 'AzureJobsShared (≥ 0.0.3-beta)'.
Installing 'AzureJobsShared 0.0.3-beta'.
Successfully installed 'AzureJobsShared 0.0.3-beta'.
Installing 'AzureImageOptimizer 0.0.3-beta'.
Successfully installed 'AzureImageOptimizer 0.0.3-beta'.
Adding 'AzureJobsShared 0.0.3-beta' to RecoveryStandard.
Successfully added 'AzureJobsShared 0.0.3-beta' to RecoveryStandard.
Adding 'AzureImageOptimizer 0.0.3-beta' to RecoveryStandard.
Successfully added 'AzureImageOptimizer 0.0.3-beta' to RecoveryStandard.

Additionally, the TFS output window showed this:

Operation did not complete successfully because the file contains a virus or potentially unwanted software.

azurejobs.props, packages.config have been automatically checked out for editing.
azurejobs.props, packages.config, packages.config have been automatically checked out for editing.
Web.config has been automatically checked out for editing.

Comment by Mohammad Mahdi Ramezanpour

Webjobs are very handy. By the time, I've written a Windows Service on my client's server which does the same thing. It compresses images, downloading some information from another server and so on; but, now I can use Webjobs.
Thanks for the nice post.

Comment by Mads Kristensen

@manny, there's nothing wrong with using the file system on Azure. These particular Webjobs are singletons. They don't scale on multiple instances on purpose, because even when you add multiple instances, they all use the same file system. Scaling the Webjobs would therefore do nothing but re-optimizing the same files over and over again.

@Alex, same error happens for Web Essentials who also ship those .exe files. They aren't harmful, so see if you can add an exception for them.

Comment by Michał Dudak

Can you recommend any .NET libraries that can do this without Azure? I need to optimize user-uploaded images on my site, but it's not hosted on Azure (yet ;))

Comment by Mads Kristensen

@Paul, if you can set up scheduled tasks on your web server, then you can run these Webjobs too. They are just console apps that Azure knows how to start up, but you can run them yourself easily.

@Michal, grab the source code and run the console apps yourself. Otherwise, look at how Miniblog does it. It's not as good as these Webjobs, but they optimize pretty ok. https://github.com/madskristensen/miniblog/

Comment by Manny

Ah ok, I mistakenly thought that adding instances to an azure website made duplicates of the deployed files (like cloud services). But apparently the scale out instances all share the same file system. Live and learn ...


Comment by Mads Kristensen

@Manny, by default an Azure Webjob will scale out and make duplicate running instances when you scale a website. There is a setting file called settings.job where you can specify if your Webjob should be a singleton. Check the source code on GitHub for reference.

Comment by Sayed Ibrahim Hashimi

@Manny Azure Web Sites uses a custom file system that shares files across all instances. So if your app writes to a file and you need to scale the app there is no problem :)

Comment by Peter

Hi. Its look pretty amazing but what about optimizing images in azure storage ? Is it works on in too ? I have site with a lot of images in storage and only few directly at site space. Thanks

Comment by Ibrahim Islam

Thanks for letting us know this. Should be helpful once our app is deployed on Azure.

Ibrahim Islam

Comment by Mads Kristensen

@Peter, it only optimizes images on disk in the regular file system. It doesn't take dependencies on any Azure resources such as Blog Storage etc.

Comment by adam

Thx Mads. Btw any recommendations towards an image optimization tool, which can do similar functionality like web essentials (optimizing png with no quality loss) that can be run via command-line or api call?