By Akshay Singh
How to Host a Static Website on a VPS Using Nginx
When it comes to deploying a static website—whether it's raw HTML/CSS/JS or the built output of a framework like Next.js, Astro, or React—many developers rely on managed services like Vercel or Netlify. While those platforms are fantastic for ease of use, they abstract away the underlying infrastructure.
Hosting your own static website on a Virtual Private Server (VPS) using Nginx gives you complete control over your environment. There are no vendor-imposed bandwidth limits, you get hands-on experience with Linux system administration, and it's incredibly cost-effective at scale.
In this guide, I'll walk you through the end-to-end process of setting up a fresh Linux VPS to serve a static website using Nginx, securing it with a free SSL certificate from Let's Encrypt, configuring your firewall, and adding performance optimizations.
Why Nginx?
Nginx (pronounced "engine-ex") is a high-performance web server that also functions as a reverse proxy, load balancer, and HTTP cache. It's the most popular web server in the world for high-traffic sites, and for good reason:
- Asynchronous, event-driven architecture — Nginx handles thousands of concurrent connections with minimal memory usage, unlike older process-based servers.
- Unbeatable static file performance — It was explicitly designed to serve static files efficiently.
- Modern syntax — Nginx configuration files are generally considered cleaner and more readable than Apache's.
- Reverse proxy capabilities — If you ever want to run a Node.js or Python API alongside your static site, Nginx makes it trivial to route traffic.
Prerequisites
Before starting, ensure you have:
- A Linux VPS — Ubuntu 24.04 LTS or 22.04 LTS is highly recommended. You can get one from DigitalOcean, Hetzner, Linode, AWS EC2, or any other provider.
- SSH access — You need terminal access to your server.
- A registered domain name — Pointed to your VPS's public IP address via an
Arecord in your DNS settings. - A static website — Your plain HTML/CSS/JS files or the
dist/outfolder from a build process.
Step 1: Connect and Update Your Server
First, open your terminal and connect to your server via SSH:
ssh root@your-server-ip
If you configured a non-root user (which is the recommended security practice), use:
ssh your-username@your-server-ip
Once logged in, it's crucial to update your package lists and install any pending security upgrades:
sudo apt update && sudo apt upgrade -y
This step ensures your system has the latest security patches before we start installing new software.
Step 2: Install and Start Nginx
Next, install Nginx from the official Ubuntu repositories:
sudo apt install nginx -y
Once the installation is complete, start the Nginx service and enable it to start automatically whenever the server reboots:
sudo systemctl start nginx
sudo systemctl enable nginx
To verify that Nginx is running correctly:
sudo systemctl status nginx
You should see an active (running) status in green. At this point, if you visit your server's IP address in your web browser (e.g., http://your-server-ip), you will see the default "Welcome to nginx!" page.
Step 3: Configure the Firewall (UFW)
Ubuntu comes with Uncomplicated Firewall (UFW) pre-installed. It's best practice to enable it immediately, but we must ensure we don't lock ourselves out.
First, allow SSH connections so you don't lose access to the terminal:
sudo ufw allow OpenSSH
Next, allow Nginx traffic. Nginx registers several profiles with UFW during installation. We'll allow "Nginx Full", which opens both port 80 (HTTP) and port 443 (HTTPS):
sudo ufw allow 'Nginx Full'
Now, enable the firewall:
sudo ufw enable
Type y when prompted. You can check the active rules at any time:
sudo ufw status
Step 4: Upload Your Static Website
By default, Nginx serves files from /var/www/html. However, the standard practice for hosting multiple sites is to create a dedicated directory for your domain under /var/www/.
Create a directory for your domain (replace example.com with your actual domain):
sudo mkdir -p /var/www/example.com
Next, assign ownership of the directory to your current user so you can upload files without needing root permissions:
sudo chown -R $USER:$USER /var/www/example.com
Now, you need to upload your website files to this directory. If your files are on your local machine, you can use scp (Secure Copy) or rsync from your local terminal.
Run this on your local machine, replacing paths and IPs:
# Using scp to upload a directory
scp -r /path/to/your/local/website/* your-username@your-server-ip:/var/www/example.com/
If you just want a quick test file to verify everything works, create a temporary index.html on the server:
nano /var/www/example.com/index.html
And paste this simple HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome to My VPS</title>
</head>
<body>
<h1>Success! The Nginx server block is working!</h1>
</body>
</html>
Save and exit (Ctrl+O, Enter, Ctrl+X).
Step 5: Create an Nginx Server Block
Nginx uses "server blocks" (similar to Apache's Virtual Hosts) to encapsulate configuration details and host multiple domains on a single server.
Create a new configuration file for your domain in the sites-available directory:
sudo nano /etc/nginx/sites-available/example.com
Paste the following configuration, making sure to replace example.com with your domain name:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
Understanding the Configuration:
listen 80;— Tells Nginx to listen on port 80 for IPv4 traffic.server_name— The domain names that should match this server block.root— The directory where your website files live.index— The default file to serve when a directory is requested.try_files $uri $uri/ /index.html;— This is crucial, especially for Single Page Applications (SPAs) like React or Next.js. It checks if a file exists; if not, it checks if a directory exists; if neither exists, it routes the request toindex.htmlto let your client-side router handle the 404 or the route.
Save and exit.
Now, enable this server block by creating a symbolic link from sites-available to sites-enabled:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Test the Nginx configuration for syntax errors:
sudo nginx -t
If the test is successful, reload Nginx to apply the changes:
sudo systemctl reload nginx
Visit http://example.com in your browser. You should now see your website!
Step 6: Secure Your Site with HTTPS (Let's Encrypt)
Serving traffic over HTTP is no longer acceptable for modern websites. We'll use Certbot to provision a free SSL certificate from Let's Encrypt.
Install Certbot and its Nginx plugin:
sudo apt install certbot python3-certbot-nginx -y
Now, run Certbot to automatically fetch the certificate and configure Nginx to use it:
sudo certbot --nginx -d example.com -d www.example.com
Certbot will ask for your email address (for renewal notices) and ask you to agree to the terms of service. It will then automatically rewrite your Nginx configuration in /etc/nginx/sites-available/example.com to enforce HTTPS.
Let's Encrypt certificates are valid for 90 days. Certbot automatically creates a systemd timer to renew them before they expire. You can test the automatic renewal process with:
sudo certbot renew --dry-run
If there are no errors, your SSL setup is complete and maintenance-free.
Step 7: Nginx Performance Optimization (Optional but Recommended)
Nginx is fast out of the box, but we can make it even faster for static sites by enabling Gzip compression and configuring browser caching.
Open the main Nginx configuration file:
sudo nano /etc/nginx/nginx.conf
Find the Gzip Settings section. Uncomment and modify it to look like this:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
This ensures that Nginx compresses HTML, CSS, and JS files before sending them to the user, drastically reducing bandwidth and improving load times.
To add caching for static assets, reopen your domain's server block:
sudo nano /etc/nginx/sites-available/example.com
Add the following location block inside the server block:
# Cache static assets for 1 year
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
This tells the visitor's browser to cache images, fonts, stylesheets, and scripts for a year, speeding up repeat visits.
Save the file, test your configuration, and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Conclusion
Congratulations! You have successfully configured a Linux VPS to serve a static website using Nginx, secured it with Let's Encrypt, locked it down with a UFW firewall, and applied performance best practices.
By hosting your own site, you've removed platform limitations and gained valuable server administration experience. You can now use this same server to host multiple domains, deploy an API, or experiment with new technologies, all on your own terms.
