Application Setup
Setup Nginx Web Page
A. Why We Need a Web Server
At this point, the server exists, but it is not serving a web page yet.
To respond to requests coming from the internet, we need a web server.
Two major web servers are:
- Apache
- Nginx
For this setup, we use Nginx.
Nginx is powerful and can work as:
- a web server
- a reverse proxy
- a forward proxy
- an email proxy
It is written in C and is designed to handle web traffic efficiently.
Apache is also a popular option and has many built-in features, including good support for technologies such as Java and PHP.
For this project, Nginx is chosen because it is fast and highly configurable.
B. What Does Nginx Do?
When a request comes from the internet and reaches our server, something needs to decide where that request should go.
For example:
A web server such as Nginx can handle this routing.
A request could potentially be routed to:
Nginx
├── Application
├── API service
└── Another Server
Databases are normally reached by the application over their own database protocols; public web requests should not be proxied directly to a database.
Conceptually, Nginx receives a request and asks:
Where should I route this request?
It can then forward the request to the appropriate destination.
This is one of the roles Nginx can perform as a reverse proxy.
C. Why Not Send Requests Directly to Node.js?
Later, we will create a Node.js application.
Technically, requests from the internet could be sent directly to the Node.js application.
However, the course does not consider this good practice.
Instead, we will place Nginx in front of Node.js.
Nginx is specialized for handling and routing web traffic and provides capabilities that would be inconvenient to implement ourselves in Node.js.
So our eventual architecture will look like:
D. Install Nginx
The first step is to install Nginx.
Because we are no longer logged in as root, we need sudo:
sudo apt install nginx
APT will ask for confirmation during the installation.
After the installation finishes, start Nginx:
sudo service nginx start
Nginx may already have started automatically, but the course runs the command to make sure it is running.
E. Verify Nginx in the Browser
Once Nginx is running, copy the server's IP address and open it in a browser.
Instead of seeing a blank or unavailable page, you should now see the default:
Welcome to nginx!
This confirms that:
The domain may not work immediately because DNS changes can take time to propagate.
However, successfully loading the Nginx welcome page through the server IP confirms that the web server is running.
F. Explore the Default Nginx Configuration
Nginx configuration is located under:
/etc/nginx/
Navigate there:
cd /etc/nginx
Then inspect the directory:
ls
Among the directories, you will find:
/etc/nginx/
├── sites-available/
└── sites-enabled/
The course notes that this structure can initially feel confusing and will be handled more clearly later.
For now, inspect the default configuration under sites-available.
The default configuration defines how Nginx handles incoming requests.
G. The Nginx root Directive
Inside the default Nginx configuration, one important setting is:
root /var/www/html;
This tells Nginx where the files for the website are located.
Conceptually:
The default Nginx welcome page is stored under:
/var/www/html
Nginx reads an HTML file from this directory and sends it back to the browser.
If all we wanted was a static HTML website, Nginx could serve those HTML files directly without requiring a Node.js application.
H. The location Block
Nginx can also route requests based on URL paths.
A location block describes how Nginx should handle requests for a particular path.
For example:
location / {
...
}
The / represents the root path.
Conceptually:
https://example.com/
↑
/
We could also create another location:
location /gojemgo {
...
}
Then Nginx could handle that path differently.
/
└── Default behavior
/gojemgo
└── Different behavior
Routing can be handled either:
- inside Nginx
- inside the application itself
Nginx has the capability to do both kinds of routing.
I. Nginx Directives
Statements inside Nginx configuration blocks are called directives.
They tell Nginx what to do inside a configuration context such as:
server block
location block
One example from the default configuration is try_files.
Conceptually:
try_files ...
Nginx can try to locate a requested file.
If the requested file does not exist, it can return a:
404 Not Found
The course does not focus heavily on try_files.
A much more important directive for this project will be:
proxy_pass
proxy_pass will later allow Nginx to forward incoming requests to our Node.js application.
The architecture will become:
J. Create a Simple Web Page
The default web files are located under:
/var/www/html
Navigate to that directory and create an index.html file.
The existing installation may contain a file similar to:
index.nginx-debian.html
For this exercise, we can create our own:
index.html
and add:
Hello World
This is valid HTML for the purpose of the exercise.
The course initially attempts to edit the file without elevated permissions and runs into a permission problem.
Because /var/www/html is not currently owned by the regular user, modifying files there may require sudo.
So the file should be edited with elevated permissions when necessary:
sudo vi index.html
If you already opened the file without sudo and cannot save it, press Esc, enter :q!, and press Enter to discard the unsaved edit. Reopen it with sudo vi index.html instead.
K. Nginx Serves index.html
After saving the file, return to the browser and refresh the page.
Instead of the default Nginx welcome page, the browser now displays:
Hello World
Nginx picks up the new index.html before the existing Debian Nginx page.
The flow is now:
At this point, we have successfully:
The domain may still be propagating, but the server itself is successfully serving our page.
L. Moving from Static HTML to Node.js
Nginx is capable of serving complete static websites by itself.
However, we want to move into the JavaScript world and host an actual application using Node.js.
Requests will first reach Nginx.
Nginx will then proxy those requests to Node.js:
This allows Nginx to handle incoming web traffic while Node.js handles the application logic.
M. Preparing to Install a Recent Version of Node.js
The next step will be installing Node.js.
However, simply running:
apt install nodejs
may install an older version maintained by the default package repository.
The course wants a more recent version of Node.js.
Instead of relying only on the default APT source, it prepares to use NodeSource as an additional package source.
Conceptually:
The course plans to use curl to retrieve the NodeSource setup script and execute it through a shell with elevated permissions.
After NodeSource has been configured, APT will know about that package source.
Then, when we install Node.js through APT, it can retrieve the version associated with the newly configured source.
That way, when we use apt-get, it will install the newer version linked to the newly configured source rather than relying only on the older default package source.
apt and apt-get use the same APT package system. apt provides a friendlier interactive interface, while apt-get has a more stable command-line interface commonly used by scripts. Either can install the package in this exercise.
Node.js 19, shown in the original course, is now end-of-life. For a real server, use NodeSource's moving LTS channel rather than copying that old version number:
sudo apt install -y curl
curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node --version
npm --version
rm nodesource_setup.sh
N. Install Git
Git may already be installed. Check it first, then install it only if necessary:
git --version
sudo apt install git
O. Create the Application Directory
The application will live under /var/www/app. Create that directory and give the current user ownership of the application directory—not the whole /var/www tree:
sudo mkdir -p /var/www/app
sudo chown -R "$USER":"$USER" /var/www/app
cd /var/www/app
Keeping the ownership change narrowly scoped avoids unexpectedly changing Nginx's existing web-root permissions.
Initialize Git and npm, then create the application entry point:
git init
npm init -y
touch app.js
The transcript runs npm init interactively and accepts the default answers. The -y option shown here produces the same default package.json without asking each question.
P. Create a Basic Node.js Server
Add the following code to app.js:
const http = require('node:http');
const server = http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
response.end('On the way to being a full-stack engineer!');
});
server.listen(3000, '127.0.0.1', () => {
console.log('Server started at http://127.0.0.1:3000');
});
Binding to 127.0.0.1 keeps port 3000 private to the server. Public requests will reach it through Nginx in the next section.
Run it temporarily to verify that it starts:
node app.js
You can test it from another SSH session with:
curl http://127.0.0.1:3000
Stop it with Ctrl+C after the test. Later, PM2 will keep the process running independently of the SSH session.