Nginx Configuration Structure
Nginx (pronounced “engine-x”) is a web server: a program that listens for incoming requests from browsers and sends back web pages, files, or responses from your app. Before you can confidently change how Nginx behaves, you need a clear mental map of where its configuration lives and how the pieces fit together. On Ubuntu, all of this sits under one directory: /etc/nginx. This page walks through that directory, explains the sites-available / sites-enabled symlink pattern, and shows the block hierarchy that every Nginx config follows.
The /etc/nginx directory layout
When you install Nginx on Ubuntu with apt, it creates a tidy directory structure under /etc/nginx. Let’s look at it.
ls -l /etc/nginx
Output:
drwxr-xr-x 2 root root 4096 Jun 15 09:12 conf.d
drwxr-xr-x 2 root root 4096 Jun 15 09:12 sites-available
drwxr-xr-x 2 root root 4096 Jun 15 09:12 sites-enabled
-rw-r--r-- 1 root root 1447 Jun 15 09:12 nginx.conf
-rw-r--r-- 1 root root 2837 Jun 15 09:12 mime.types
-rw-r--r-- 1 root root 664 Jun 15 09:12 fastcgi_params
Here is what each piece does:
| Path | What it is | When you touch it |
|---|---|---|
/etc/nginx/nginx.conf | The main, top-level config. The starting point Nginx reads first. | Rarely — only for global settings (worker counts, logging). |
/etc/nginx/conf.d/ | A folder for extra global config snippets. Every *.conf file here is loaded. | For settings that apply site-wide (e.g. gzip, rate limits). |
/etc/nginx/sites-available/ | Storage for your per-website config files. Files here are NOT active by default. | Every time you add or edit a website. |
/etc/nginx/sites-enabled/ | Active websites. Contains symlinks (shortcuts) pointing back to sites-available. | To switch a site on or off. |
/etc/nginx/mime.types | A lookup table mapping file extensions to content types. | Almost never. |
Editing
nginx.confdirectly is tempting, but resist it for per-site changes. Keeping each website in its own file undersites-availablemakes configs far easier to read, version, and disable without losing your work.
nginx.conf — the entry point
nginx.conf is the first file Nginx reads on startup. It sets global behaviour and then includes the other config files so they get loaded too. A trimmed Ubuntu default looks like this:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
sendfile on;
keepalive_timeout 65;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
The two include lines at the bottom are the magic. They tell Nginx: “also read every file in conf.d/ and every enabled site.” That is how your per-site files get pulled in without ever editing nginx.conf again.
sites-available vs sites-enabled — the symlink pattern
This pair of directories is the most important Ubuntu-specific convention to understand.
sites-availableis a library of every site config you have written, whether it’s live or not.sites-enabledholds only the sites that are currently active. But instead of copying files here, you create a symlink (a symbolic link — a pointer that acts like a shortcut to the real file).
This separation means you can write and store a config without activating it, and you can switch a site off by deleting one symlink — your real config stays safe in sites-available.
When to use this: always, for any real website or app. It is the standard Ubuntu workflow.
Here is how you enable a site. Suppose you’ve written /etc/nginx/sites-available/myapp:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
ls -l /etc/nginx/sites-enabled/
Output:
lrwxrwxrwx 1 root root 34 Jun 15 10:02 myapp -> /etc/nginx/sites-available/myapp
The -> arrow confirms it’s a symlink, not a copy. To disable the site, just remove the symlink (the source file is untouched):
sudo rm /etc/nginx/sites-enabled/myapp
Ubuntu ships with a default site already enabled. To turn it off:
sudo rm /etc/nginx/sites-enabled/default
The block hierarchy: http → server → location
Every Nginx config is built from nested blocks — sections wrapped in { } braces. There are three you must know, and they nest from broadest to narrowest.
httpblock — wraps all web (HTTP/HTTPS) settings. Defined once innginx.conf. Settings here apply to every site.serverblock — defines one website (one domain). This is what you write insites-available. Often called a “virtual host.”locationblock — matches specific URL paths within a site (e.g./,/images/,/api/) and decides how to handle them.
Think of it as: the building (http), the apartments inside it (server), and the rooms inside each apartment (location).
A typical per-site file in sites-available contains a server block with location blocks inside:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /images/ {
expires 30d;
}
}
listen 80— wait for requests on port 80 (standard HTTP).server_name— which domain(s) this block answers for.root— the folder on disk where the site’s files live.location /— the catch-all rule for every URL.location /images/— a more specific rule, here caching images for 30 days.
Nginx always picks the most specific matching location for each request.
Testing and reloading config
Nginx will not pick up your edits automatically. After changing any file, the rule is: test, then reload. First, validate the syntax — this catches typos before they take your site 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 it reports syntax is ok and test is successful, apply the change with a reload. A reload re-reads the config gracefully, without dropping any in-flight connections:
sudo systemctl reload nginx
Always run
sudo nginx -tbefore reloading. If the config has an error,reloadwill refuse to apply it and your old (working) config keeps running — but only the-ttest tells you clearly what is broken and on which line.
Use reload (not restart) for config changes — restart fully stops and starts Nginx, briefly dropping connections. Reserve restart for upgrades or when Nginx is in a stuck state.
Best Practices
- Keep one
serverblock per website in its own file undersites-available; never bundle unrelated sites together. - Activate sites only via symlinks in
sites-enabled— disabling a site should never mean deleting your real config. - Put truly global settings (gzip, rate limits, logging) in
conf.d/, and leavenginx.confclose to its Ubuntu default. - Always run
sudo nginx -tafter every edit and before every reload — make it a reflex. - Prefer
systemctl reload nginxoverrestartso live visitors are never disconnected. - Remove the default
defaultsite once you have your own configured, to avoid serving the placeholder page by accident.