Simple Webserver on Windows

Let's have a look at how to start a minimal web server on Windows to make your frontend development easier. This is useful for especially vanilla JavaScript development or if you want to make sure your asset loading works as it would on the server.

Lots of tutorials either focus on one underlying programming language only or they suggest installing more complex servers like nginx or apache through XAMPP which is usually a lot more configuration than you need to do.

We'll use Chocolatey to install either node, ruby, python or php which all come with a small web server built in, that you can start on any port to serve your frontend project from the directory you're in with Powershell. You can use any other terminal you have installed, it's just a default on Windows, feel free to use your preferred one.

The Example Project 😎

Our example Project will have 3 files:

  • index.html
  • index.js
  • games.json

which will mimick a site that loads additional data from a server, which in our case is just a JSON file with some structured data in it, a list of our favourite games!

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Jonathan's Gaming Site</title>
</head>
<body>
  <h1>A list of my favourite games!</h1>
  <div id="games"></div>
    <h2>debug:</h2>
  <pre id="debug"></pre>
  <script src="index.js"></script>
</body>
</html>
  • <script src="index.js"></script> loads our index.js

games.json:

[
  {
    "name": "Warhammer 40k: Darktide",
    "developers": [
      "Fatshark"
    ],
    "publishers": [
      "Fatshark"
    ],
    "released": {
      "year": 2022
    }
  },
  {
    "name": "Valheim",
    "developers": [
      "Iron Gate Studio"
    ],
    "publishers": [
      "Coffee Stain Publishing"
    ],
    "released": {
      "year": 2021
    }
  },
  {
    "name": "Deep Rock Galactic",
    "developers": [
      "Ghost Ship Games"
    ],
    "publishers": [
      "Coffee Stain Publishing"
    ],
    "released": {
      "year": 2020
    } 
  }
]

This is our structured data source, you could also imagine that this is a network request to an API.

index.js:

console.log('index.js starts executing!')

fetch('./games.json')
  .then((response) => response.json())
  .then((data) => {
    const debugElement = document.querySelector('#games');
    const outputElement = document.querySelector('#games');

    debugElement.innerHTML = JSON.stringify(data)

    let ul = document.createElement("ul")

    data.forEach(element => {
      let li = document.createElement('li')
      li.innerText = `${element.name} (${element.released.year})`
      ul.appendChild(li);
    });

    outputElement.appendChild(ul);
  });
  • fetch lets us load data from another file or server response
  • element.appendChild() lets us put newly created HTML elements into existing ones

If we were to just double-click our index.html from a file manager, it would load the JavaScript file just fine, but as soon as our JavaScript tries to make a network request, it would show us an error in the browsers JavaScript console, since the browser will not let JavaScript make a "network" request to the local file system.

This puts our pages in a bit of a broken state, as you can see here:

The CORS error in Firefox:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///home/jonathan/projects/simple-servers/games.json. (Reason: CORS request not http).
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.
    <anonymous> file:///home/jonathan/projects/simple-servers/index.js:3

The CORS error in Chrome:

Access to fetch at 'file:///home/jonathan/projects/simple-servers/games.json' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.
GET file:///home/jonathan/projects/simple-servers/games.json net::ERR_FAILED
(anonymous) @ index.js:7
Uncaught (in promise) TypeError: Failed to fetch
    at index.js:3:1

These are not the only contexts for these errors, you can also mitigate them with server headers if you're not trying to load something through the file:// protocol.

My usual default solution, because I always have Node.js installed would be:

npx serve .
# or
npm install -g serve
serve .

but it also works with lots of other languages and runtimes, which we're checking out in the next section.

The working version of the test site is supposed to look like this:

Installing Dependencies with Chocolatey 🍫

You don't need to do this step, you can also install node.js or any of the options from the official websites, it's just more convenient to me to have a package manager like brew on MacOS or apt or pacman on Linux available on Windows as well.

If you open your Powershell as an Administrator after installing Chocolatey, you can for exmaple install ruby to run a server.

choco install ruby
# cd C:\Users\jonathan\simple-server
ruby -run -e httpd . -p 8080
# if: webrick is not found. You may need to `gem install webrick` to install webrick.
# gem install webrick

and you can visit your tiny ruby server at http://localhost:8080.

Now your Powershell output should look something like this:

This also works for python:

choco install python3
# cd C:\Users\jonathan\simple-server
python -m http.server 8080

You can even run this straight from within VScode or any other editor that has any terminal integration, you can bring it up with CTRL+J:

This also works in Sublime and a few others, but I would recommend getting in the habit of having a terminal open in the directory where you're editing code because it's a very common practise in lots of languages.

Adding Python3 to Path on Windows 🐍

I had to add the Python3 installed through Chocolatey to my Path manually, which was fairly simple if you find your "Advanced System Settings":

You'll need to add a new line to the Environment which is the path to where python.exe exists, so we need to check where chocolatey installs it.

Summary 💡

Now we have a super simple web server that we only run when we want to, so not as a continous background service, that runs on a port of our choosing like 3000 or 8080 and it lets us develop websites much closer to real world conditions than only using the file system like back in the old Frontpage days 🙌.

Further Reading:

Tagged with: #Windows #Chocolatey

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!