Skip to content

Host Your Website: Nginx Crash Course, Part 1

Published: at 06:50 PM

Highland cow in a pop art style

Nginx is how you can serve your web app to the world. As a web server, it receives a request for your site, locates and prepares the requested web pages, and sends these pages back to the users’ browsers for their delight.

It’s designed to be able to handle lots of visitors smoothly and quickly and is known for being lightweight and resource-efficient. Some of the biggies which use Nginx include Netflix, Hulu, Pinterest, Airbnb, WordPress.com, GitHub, and SoundCloud. It’s been around for a while, launching in 2004 as a direct competitor to its biggest rival, Apache.

If you have a static site ready to go to test with Nginx then feel free to use your own files. I’ll pop all my code examples here on GitHub for you to borrow.

I used Ubuntu for this project.

Install Nginx and set up firewall

Obviously, first of all, we need to install it!

sudo apt update
sudo apt install nginx

Adjust the firewall with ufw (Uncomplicated Firewall) by allowing HTTP traffic to Nginx:

sudo ufw allow 'Nginx HTTP'

Other commands you can use with ufw:

# Check the firewall status
sudo ufw status

# If the status says 'status: inactive'
sudo ufw enable

# If needed, restart or disable the firewall
sudo ufw reload
sudo ufw disable

# List application profiles
sudo ufw list

# Enable a specific port
sudo ufw allow [port number]

# This command will open port 8000 for both TCP and UDP traffic. If you want to specify the protocol, you can do so like this:
sudo ufw allow 8000/tcp

# See more commands
ufw help

Create configs and website files

Now we’ll make a home for our site and create a server block config.

You can have a look at the default server block file:

cat /etc/nginx/sites-available/default

We’re going to use our own config and not the default one. It’s a good idea to keep the default config code in our files in case we want to use it as a reference in future, so we’ll just unlink it so it doesn’t get loaded by Nginx:

sudo unlink /etc/nginx/sites-enabled/default

Add the folder where our beautiful website is going to be served from:

sudo mkdir /var/www/mywebsite

If you run into folder permission issues like I did, you can change ownership and permission of the folders to your user name and give yourself read, write, and execute privileges:

sudo chown your_username /var/www/mywebsite
sudo chmod -R 755 /var/www/mywebsite

Set permissions for Nginx:

sudo chown -R www-data:www-data /var/www/mywebsite

To make sure we’ve got it working, let’s start by making a server block for a localhost-only site - the most useless website in the world.

This is going to be an Nginx configuration file. A server block establishes the settings that determine how the web server will process and respond to HTTP requests targeted at a particular domain or set of domains.

Create a file with nano (use whatever text editor you want):

sudo nano /etc/nginx/sites-available/myconfig

You can of course change ‘mywebsite’ and ‘myconfig’ to whatever names you want.

It’s going to be an HTML site. Add this code and save the file:

server {
    listen 80;
    server_name localhost;

    root /var/www/mywebsite;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Listen, server_name, root, index and location are all directives, a set of instructions that tell Nginx how to handle requests and serve content.

Specifically in this server block:

Create a symbolic link named “myconfig” in the sites-enabled folder. Instead of copying the entire configuration file from sites-available, which can potentially contains many configs, the link just points to it and helps us keep our code nice and organised:

sudo ln -s /etc/nginx/sites-available/myconfig /etc/nginx/sites-enabled/

Check that there are no syntax errors in your config:

sudo nginx -t

Cross your fingers and hope for this result:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Nginx needs to be restarted whenever there’s a change to config files, so make sure you do that:

sudo service nginx restart

(IF IN DOUBT, RESTART NGINX. THAT IS YOUR NEW MOTTO.)

You can check if Nginx is running:

systemctl status nginx

If you have a website you want to try out already, copy the files over to the directory you made earlier:

sudo cp -r /path/to/your/site/* /var/www/mywebsite

Don’t have one? Don’t worry, Auntie Adie is here to help with this masterpiece. Save it as index.html to the folder /var/www/mywebsite. You can do so with nano if you hate life, with sudo nano /var/www/mywebsite/index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>cool website</title>
  </head>
  <body>
    <h1>Hello from Nginx. ^_^</h1>
  </body>
</html>

Make sure your HTML is correct because you don’t want to spend an hour trying to troubleshoot Nginx only to discover the issue with your site not appearing was because you didn’t close the meta tag properly! I would never do that, but you might.

Anyway, you can now view your website on the same computer as the host by popping localhost in your browser.

Screenshot of the desired output Truly amazing. FAANG, here I come.

Baby steps. Time to make it slightly more interesting.

Serve a frontend and backend on your local network

The next mini-project assumes again that there will be an index.html file in /var/www/mywebsite

If you aren’t interested in running the backend and are happy enough just serving static files where they are already, skip to the updated server block config.

Add a new folder called app:

mkdir /var/www/mywebsite/app/

A NodeJS app will live there. We’ll also serve an HTML and CSS file.

Change your index.html to this:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="styles.css" />
    <title>My List</title>
  </head>
  <body>
    <p>Making Fetch Happen</p>
    <button onclick="makeRequest()">Make Request</button>

    <p id="result"></p>

    <script>
      function makeRequest() {
        fetch("http://0.0.0.0:8000") // replace 0.0.0.0 with your IP address
          .then(response => response.json())
          .then(data => {
            const listElement = document.getElementById("result");
            while (listElement.firstChild) {
              listElement.removeChild(listElement.firstChild);
            }
            data.forEach(item => {
              const listItem = document.createElement("li");
              listItem.textContent = item.trim();
              listElement.appendChild(listItem);
            });
          })
          .catch(error => {
            console.error("Error:", error);
          });
      }
    </script>
  </body>
</html>

Woahhhhhhh, what is THAT? Is that VANILLA JavaScript? Yes it is. Do not be scared.

Add a file: styles.css

Make your own styles if you want. Or just don’t bother including CSS at all if you are lazy. 🙃

body {
  font-family: Arial, sans-serif;
}

button {
  background-color: #4caf50;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  padding: 8px;
  margin-bottom: 7px;
  background-color: #eee;
  color: #333;
}

Move into the app folder:

cd /var/www/mywebsite/app/

If you don’t have Node installed already, follow these steps:

sudo apt update
sudo apt install nodejs

Check if the installation was successful by obtaining the Node version number:

node -v

Now, install its package manager, NPM:

sudo apt install npm

Initialize a new Node project with:

npm init -y

Install all the required packages:

npm install express cors nodemon

Create a file named server.js and place the following code in it:

const express = require("express");
const cors = require("cors");
const app = express();
const port = 8000;

const corsOptions = {
  origin: "http://0.0.0.0", // replace 0.0.0.0 with your IP address
  optionsSuccessStatus: 200,
};

app.use(cors(corsOptions));

app.get("/", (req, res) => {
  const shoppingList = [
    "Milk",
    "Bread",
    "Cheese",
    "Vegetables",
    "Fruits",
    "Cereal",
  ];
  res.send(shoppingList);
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Open the package.json file created during project initialization and add the following script:

"scripts": {
    "start": "nodemon server.js"
}

The updated package.json file should look like this (along with a bunch of other stuff it added on initialize):

{
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "nodemon": "^3.0.3"
  },
  "scripts": {
    "start": "nodemon server.js"
  }
}

In short, Express allows us to create an API, CORS restricts requests to the backend to only be allowed from your IP, and Nodemon serves the API while automatically restarting the backend server upon code changes. That’s about as much detail as I’ll go into here because this is meant to be an Nginx tutorial and not a Node tutorial.

Start the backend with the created script:

npm start

The app is served from your local IP address, which you can obtain with:

hostname -I

Copy and insert this IP into the code where it says [your local IP].

Update the Nginx configuration by editing the file:

sudo nano /etc/nginx/sites-available/myconfig

Replace the previous configuration with this:

server {
    listen [your local IP]:80;
    server_name _;

    root /var/www/mywebsite;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /app/ {
        proxy_pass http://localhost:8000/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Ensure the trailing slashes (/) after “app” and “localhost” are included.

Restart the Nginx process:

sudo systemctl restart nginx

What exactly is going on in this slightly new and improved server block? It’s not too different to the first one we tried.

To troubleshoot, check the syntax:

sudo nginx -t

View the app by entering your local IP in the browser. Press the ‘Make Request’ button, and if all goes well, your shopping list will be fetched from the backend and displayed on the frontend.

Screenshot of how the app should look The desired result.

In part 2, we’ll cover getting a domain name to point to it and other important steps.

Source code: GitHub