DEV Community

Cover image for How To Properly Deploy a Static HTML Site (built by Next.JS) on IIS to Avoid 404's
Zafer Gurel
Zafer Gurel

Posted on • Updated on

How To Properly Deploy a Static HTML Site (built by Next.JS) on IIS to Avoid 404's

You can build your next.js project with the following command:

npm run build

Which in fact runs next build && next export (assuming build script is defined in package.json).

Now, you have the published files in the out folder. The build process creates an html file for each jsx or tsx file under pages folder. Also, it compiles all the scripts together as chunks, scss/css files, and also images under _next folder. You can find more information here.

You copy all those files, put it into a folder on your web server, and create a website on IIS pointing that folder. It's done. Or I thought so.

The website worked well. I could navigate through the website by clicking the links.

But the problem was that when I copied a URL like www.xyz.com/products/abc (which points to products/abc.html) and paste it into the address bar of a newly opened browser tab and hit enter, I got a 404 page. IIS could not find it. As you imagine, this is a huge problem.

So, after digging a bit in the web, I came across a S.O. post which is about deployment on IIS but as a more general question. One of the answers includes the idea of using a web.config file including routing rules. Inspired with this answer and modifying the code a little bit for my needs, I created a web.config file as follows and put it into the website folder:


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" defaultResponseMode="File">
        <remove statusCode="404" />
        <error statusCode="404" path="/404.html" responseMode="ExecuteURL" />
   </httpErrors>
    <rewrite>
      <rules>
        <rule name="static assets" stopProcessing="true">
          <match url="([\S]+[.]((html?)|(svg)|(js)|(css)|(png)|(gif)|(jpe?g)|(json)))" />
        </rule>
        <rule name="reactRouter 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="/{R:1}.html"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Enter fullscreen mode Exit fullscreen mode

So, basically what this web.config file does is to rewrite the URLs of requests except for the ones for html files and static files. For example /products/abc gets rewritten as /producs/abc.html. By doing so, the problem was solved.

You will also notice the httpErrors section on top of the configuration code. This is to show a custom 404 page to the user if s/he requests a page that does not exist. [1]

Hopefully, this helps someone and earns him/her some precious time.

[1]: Edit (2022-06-21): httpErrors section was added to the sample configuration code to show a custom 404 page.

Top comments (2)

Collapse
 
lekansaheeddev profile image
Lekan Saheed • Edited

Hello, i tried this method, but only the next nested parent route after the '/' gets displayed. e.g localhost:50/dashboard, localhost:50/customers. If I route to localhost:50/customers/all and reload, i will get a 403. forbidden error. Is there a possible fix for this?

Collapse
 
zaff profile image
Zafer Gurel

Actually, it's been a while since I implemented this. But the trick is the match url part. It matches the whole part after the slash (/) character. So products become products.html. What you need to do is to change the regular expression. I'd like to give the exact solution but I don't have the setup anymore (switched to Linux :)).