$linuxjunkies
>

How to Install and Configure nginx

Install nginx, configure server blocks and document roots, set up a reverse proxy to a local application, and verify everything with nginx -t and curl.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • A Linux server with sudo or root access
  • A working package manager connection to the internet
  • Basic familiarity with a terminal text editor (nano, vim)
  • An application listening on a local port if configuring the reverse proxy section

nginx is a high-performance web server and reverse proxy used on a significant share of the internet's busiest sites. Installing it takes seconds; configuring it correctly—server blocks, document roots, SSL-aware reverse proxy passes—takes a clear mental model. This guide covers a full working setup: default server block, a named virtual host, a reverse proxy to an upstream application, and validating every change before reloading.

Installation

Debian / Ubuntu

sudo apt update
sudo apt install -y nginx

Fedora / RHEL 9 / Rocky Linux

sudo dnf install -y nginx

Arch Linux

sudo pacman -S nginx

Enable and start the service with systemd on all distros:

sudo systemctl enable --now nginx

Confirm it is running:

systemctl status nginx

You should see active (running). Hit http://localhost in a browser or with curl and you will get the default nginx welcome page.

Understanding the Directory Layout

nginx's layout differs slightly between distros, but the logical structure is the same:

PathPurposeDistro note
/etc/nginx/nginx.confMain config, sets global directives and includes site configsAll
/etc/nginx/sites-available/Server block files (not yet active)Debian/Ubuntu
/etc/nginx/sites-enabled/Symlinks to active server blocksDebian/Ubuntu
/etc/nginx/conf.d/Drop-in config files (all loaded automatically)Fedora/RHEL/Arch
/var/www/html/Default document rootDebian/Ubuntu
/usr/share/nginx/html/Default document rootFedora/RHEL/Arch

On Fedora/RHEL/Arch you can adopt the sites-available / sites-enabled pattern by adding the appropriate include lines to nginx.conf, or simply drop files into conf.d/. This guide uses conf.d/ as a common denominator and notes the Debian symlink step where it differs.

Creating a Server Block (Virtual Host)

A server block tells nginx which hostname and port to answer on, and where to find files. Create a document root and a minimal HTML page first:

sudo mkdir -p /var/www/example.com/html
echo '<h1>It works</h1>' | sudo tee /var/www/example.com/html/index.html
sudo chown -R www-data:www-data /var/www/example.com   # Debian/Ubuntu
# On Fedora/RHEL/Arch the web user is 'nginx':
# sudo chown -R nginx:nginx /var/www/example.com

Now write the server block. On Debian/Ubuntu use /etc/nginx/sites-available/example.com; on Fedora/RHEL/Arch use /etc/nginx/conf.d/example.com.conf:

sudo nano /etc/nginx/conf.d/example.com.conf
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html index.htm;

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log warn;

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

On Debian/Ubuntu, activate the block with a symlink:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# then remove or disable the default symlink if you want this to be the only site:
sudo rm /etc/nginx/sites-enabled/default

Testing the Configuration

Always test before reloading. nginx -t parses every included file and reports syntax errors with file names and line numbers:

sudo nginx -t

Expected output (will vary slightly):

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 without dropping connections:

sudo systemctl reload nginx

Never use restart in production for config changes. reload sends SIGHUP, which gracefully reopens files and applies the new config while existing connections finish.

Configuring a Reverse Proxy

A common pattern is nginx sitting in front of an application server (Node.js, Gunicorn, a Java service, etc.) running on a local port. nginx handles TLS termination, compression, and static file serving; the application handles dynamic requests.

Assume your application listens on 127.0.0.1:3000. Create a new server block:

sudo nano /etc/nginx/conf.d/app.example.com.conf
upstream app_backend {
    server 127.0.0.1:3000;
    # Add more servers here for basic round-robin load balancing:
    # server 127.0.0.1:3001;
}

server {
    listen 80;
    listen [::]:80;

    server_name app.example.com;

    access_log /var/log/nginx/app.example.com.access.log;
    error_log  /var/log/nginx/app.example.com.error.log warn;

    # Optional: serve static assets directly from disk
    location /static/ {
        root /var/www/app.example.com;
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    location / {
        proxy_pass         http://app_backend;
        proxy_http_version 1.1;

        # Pass real client info to the application
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_set_header   Upgrade           $http_upgrade;
        proxy_set_header   Connection        "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;
    }
}

The upstream block is optional for a single backend, but it makes adding servers later trivial and lets you name the backend clearly in logs. The Upgrade and Connection headers are required for WebSocket proxying and harmless otherwise.

Test and reload again:

sudo nginx -t && sudo systemctl reload nginx

Opening the Firewall

If a firewall is active, allow HTTP (and HTTPS when you add TLS).

ufw (Debian / Ubuntu)

sudo ufw allow 'Nginx Full'
sudo ufw reload

firewalld (Fedora / RHEL / Rocky)

sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload

nftables (Arch and anywhere running nftables directly)

# Add to your nftables ruleset, e.g. /etc/nftables.conf
# tcp dport { 80, 443 } accept

Verification

Confirm nginx is serving the right content for each hostname. If you do not have DNS set up yet, pass the Host header manually:

curl -H 'Host: example.com' http://127.0.0.1/
curl -H 'Host: app.example.com' http://127.0.0.1/

Check that the access log for each site is receiving entries:

sudo tail -f /var/log/nginx/example.com.access.log

Verify the full config parse including all included files:

sudo nginx -T | less

-T (uppercase) dumps the parsed, concatenated config to stdout—useful for auditing exactly what nginx sees at runtime.

Troubleshooting

  • 502 Bad Gateway from the reverse proxy: The upstream application is not listening. Confirm with ss -tlnp | grep 3000. Check the application's own logs.
  • 403 Forbidden on static files: Almost always a file-system permission problem. nginx worker processes run as www-data (Debian/Ubuntu) or nginx (Fedora/RHEL/Arch). The document root and all parent directories must be executable by that user: sudo chmod o+x /var/www/example.com.
  • Changes not taking effect: You may have edited the wrong file. Run sudo nginx -T and search for the relevant server_name to confirm which file is actually loaded.
  • SELinux blocking connections (Fedora/RHEL/Rocky): If nginx cannot connect to the upstream, SELinux may be enforcing. Check with sudo ausearch -m avc -ts recent and allow with sudo setsebool -P httpd_can_network_connect 1.
  • Port 80 already in use: Run sudo ss -tlnp | grep ':80' to find the occupying process. Apache (apache2/httpd) is the usual culprit—stop and disable it before starting nginx.
tested on:Ubuntu 24.04Debian 12Fedora 40Rocky 9

Frequently asked questions

What is the difference between nginx reload and nginx restart?
reload sends SIGHUP to the master process, which re-reads config and gracefully hands off to new worker processes while existing connections finish. restart kills all processes immediately, dropping active connections—avoid it in production for config changes.
Can I run multiple sites on the same server with nginx?
Yes. Create one server block file per site, each with a unique server_name. nginx matches incoming requests to the correct block based on the Host header. If no block matches, nginx falls back to the default_server block.
How do I add HTTPS / TLS to a server block?
The recommended approach is Certbot with the nginx plugin (sudo certbot --nginx -d example.com), which obtains a Let's Encrypt certificate and rewrites your server block automatically. It adds a listen 443 ssl block and configures renewal.
Why do I get a 502 Bad Gateway when using the reverse proxy?
nginx reached the upstream address but got no response. The most common causes are: the backend application is not running, it is listening on a different port than configured, or on Fedora/RHEL SELinux is blocking the outbound connection (fix with setsebool -P httpd_can_network_connect 1).
What does the try_files directive actually do?
try_files $uri $uri/ =404 tells nginx to first look for an exact file match, then a directory with an index file, and return a 404 if neither exists. Without it, nginx may pass requests for missing static files to an index script unintentionally.

Related guides