Serving a Static Site with Nginx
A static site is a website made of plain files — HTML (the page structure), CSS (the styling), and JavaScript (the browser-side behavior) — that the server sends to the browser exactly as they are, with no per-request processing. Nginx (pronounced “engine-x”) is a web server, which is a program that listens for incoming web requests and returns files or responses. Nginx is one of the fastest, most efficient tools for this job, which is why it powers a huge share of the internet. This page walks you through the entire process on Ubuntu: placing your files, setting permissions, writing a configuration block, enabling it, and visiting your live site.
When to use Nginx for a static site
A static site is the right fit when your content does not change based on who is visiting or what they typed — think landing pages, documentation, portfolios, marketing sites, or the compiled output of a single-page application (SPA) built with React, Vue, or Angular. Nginx is ideal here because it serves files directly from disk with very little CPU and memory, so a small server can handle thousands of visitors.
| Scenario | Use Nginx static serving? |
|---|---|
| Plain HTML/CSS/JS site | Yes — perfect fit |
| Compiled React/Vue/Angular SPA build output | Yes — with a fallback (shown below) |
| Server-rendered app (PHP, Node, Python) | No — Nginx forwards to that app instead (reverse proxy) |
| Content that changes per user on every request | No — you need a backend application |
If your site needs a database, user logins, or server-side code, you do not have a static site. In that case Nginx acts as a reverse proxy (a server that sits in front of your app and forwards requests to it) rather than serving files directly.
Step 1 — Create the site directory
By convention on Ubuntu, websites live under /var/www. We will create a dedicated folder for this site so each site stays separate and tidy.
sudo mkdir -p /var/www/site
Now add a simple index.html. The index.html file is special — when a visitor requests your site’s root URL (like https://example.com/), Nginx looks for index.html by default.
sudo tee /var/www/site/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Static Site</title>
</head>
<body>
<h1>Hello from Nginx!</h1>
<p>This page is served as a static file.</p>
</body>
</html>
EOF
If you have an existing build (for example a React app), copy its output instead. The -r flag means “recursive,” so it copies the folder and everything inside it.
sudo cp -r ./dist/* /var/www/site/
Step 2 — Set ownership and permissions
Nginx runs as a low-privilege system user called www-data on Ubuntu. For Nginx to read your files, that user needs access. We give ownership of the folder to www-data and set sensible permissions.
sudo chown -R www-data:www-data /var/www/site
sudo chmod -R 755 /var/www/site
chown -R www-data:www-data sets both the owner and group to www-data recursively (-R). chmod -R 755 means the owner can read/write/execute, while everyone else can read and execute — which is exactly what a web server needs.
Never use
chmod 777. That makes files writable by every user on the system, which is a serious security hole.755for directories and644for files is the safe, standard choice for web content.
Step 3 — Write the server block
A server block is the chunk of Nginx configuration that defines one website — its domain name, where its files live, and how to handle requests. On Ubuntu, each site gets its own file in /etc/nginx/sites-available. Create one for your site:
sudo nano /etc/nginx/sites-available/site
Paste in the following configuration:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/site;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Here is what each line does:
listen 80;tells Nginx to accept normal HTTP traffic on port 80. The[::]:80line does the same for IPv6 (the newer internet addressing scheme).server_namelists the domains this block answers for. Replaceexample.comwith your real domain, or use_to match any hostname while testing.rootis the folder on disk that holds your files.indexnames the default file to serve for a directory request.try_files $uri $uri/ =404;tells Nginx: try the exact requested file ($uri), then try it as a directory ($uri/), and if neither exists return a 404 “Not Found” error.
SPA fallback
If you are hosting a single-page application, the browser handles routing itself, so URLs like /dashboard have no matching file on disk. Without help, Nginx would return a 404. The fix is to fall back to index.html so the app can take over routing:
location / {
try_files $uri $uri/ /index.html;
}
Now any unknown path serves index.html, and your SPA’s router displays the correct page.
Step 4 — Enable the site
Ubuntu’s Nginx only serves sites that are “enabled,” meaning a link to them exists in /etc/nginx/sites-enabled. We create that link with ln -s (a symbolic link, which is a pointer to the original file).
sudo ln -s /etc/nginx/sites-available/site /etc/nginx/sites-enabled/
Ubuntu ships with a default site that may grab requests first. Remove its link so your site responds:
sudo rm -f /etc/nginx/sites-enabled/default
Step 5 — Test and reload
Always test your configuration before applying it. A typo could otherwise take Nginx down.
sudo nginx -t
Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If the test passes, reload Nginx so it picks up the new configuration. reload applies changes without dropping active connections, unlike a full restart.
sudo systemctl reload nginx
Step 6 — Open the firewall and visit
If you use UFW (Uncomplicated Firewall, Ubuntu’s simple firewall tool), allow web traffic so the outside world can reach your site:
sudo ufw allow 'Nginx HTTP'
Now confirm it works from the server itself with curl, a command-line tool that fetches URLs:
curl -I http://localhost
Output:
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/html
Content-Length: 213
A 200 OK status means success. Open http://your-server-ip (or your domain) in a browser and you should see your page.
Best Practices
- Keep each site in its own folder under
/var/wwwand its own file insites-available— never edit the mainnginx.conffor individual sites. - Always run
sudo nginx -tbefore reloading; a broken config that reloads can take every site on the server offline. - Use
chown www-data:www-dataand755/644permissions; avoid777entirely. - Use
try_files ... /index.htmlonly for SPAs — for plain static sites keep=404so missing pages return a real 404. - Add HTTPS with a free Let’s Encrypt certificate (via
certbot) before going to production, so traffic is encrypted. - Prefer
systemctl reload nginxoverrestartto apply changes without interrupting live visitors.