How to Deploy a Website on Nginx: Server Block Setup

07 Apr 2026 By Edgars Kalējs

Run on the server with one command (check the script contents below first):

curl -sSL https://cp.lv/scripts/razvernut-sajt-na-nginx.sh | sudo bash

…or with wget:

wget -qO- https://cp.lv/scripts/razvernut-sajt-na-nginx.sh | sudo bash

Script contents razvernut-sajt-na-nginx.sh:

#!/usr/bin/env bash
set -e

# 1. Site directory and permissions
sudo mkdir -p /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
echo "<h1>example.com works</h1>" | sudo tee /var/www/example.com/index.html > /dev/null

# 2. server block (quoted 'EOF' leaves Nginx variables alone)
sudo tee /etc/nginx/sites-available/example.com > /dev/null << 'EOF'
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/ =404;
    }

    location ~* \.(jpg|jpeg|png|webp|css|js|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

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

# 3. Enable the site and disable the default config
sudo ln -sf /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
echo "Site deployed: open http://example.com"

For Nginx to serve your website, you have to describe a virtual host — a server block: where the files live, which domain to serve and how to handle requests. Below is a working scheme for both static and PHP sites.

Step 1. Prepare the site directory

Put the site files in a separate directory and make the web user www-data their owner:

sudo mkdir -p /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com

Upload the files (with rsync or git clone, for example). To test, create a landing page:

echo "<h1>example.com works</h1>" | sudo tee /var/www/example.com/index.html

Step 2. Server block for a static site

Open /etc/nginx/sites-available/example.com in an editor:

sudo nano /etc/nginx/sites-available/example.com

File contents:

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/ =404;
    }

    location ~* \.(jpg|jpeg|png|webp|css|js|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

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

Or write the whole config with a single command — copy it and paste it into the console (the quotes around 'EOF' matter, so that the Nginx variables $uri stay as they are):

sudo tee /etc/nginx/sites-available/example.com > /dev/null << 'EOF'
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/ =404;
    }

    location ~* \.(jpg|jpeg|png|webp|css|js|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

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

The try_files ... =404 directive serves the file if it exists and returns an honest 404 otherwise. The expires block turns on browser caching for static assets.

Step 3. The PHP variant

If the site runs on PHP, add handling through PHP-FPM and index.php. Open the same file in an editor (sudo nano /etc/nginx/sites-available/example.com) and make it look like this:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

Or write the PHP variant with a single command (the quotes around 'EOF' matter, so that the Nginx variables $uri stay as they are):

sudo tee /etc/nginx/sites-available/example.com > /dev/null << 'EOF'
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}
EOF

Here try_files with a fallback to /index.php is the standard for CMSes and frameworks with pretty URLs (WordPress, Laravel).

Step 4. Enable and verify

Enable the site with a symlink, check the syntax and reload:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

nginx -t should answer syntax is ok and test is successful. If it does not, the error points at the file and the line.

Step 5. Disable the default site

So that Nginx stops serving its default placeholder, remove the default config:

sudo rm /etc/nginx/sites-enabled/default
sudo systemctl reload nginx

Common errors

Symptom Cause Fix
403 Forbidden no permissions on the files chown www-data and chmod 755
404 on every URL wrong root check the directory path
PHP downloads as a file no location ~ \.php$ block add FPM handling
Another site opens default is still enabled remove the default config

Step 6. Logs for troubleshooting

If something does not work, look at the error log of that specific site:

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

FAQ

How does a server block differ from an Apache virtual host? It is the same concept — the description of a single site. In Nginx one server { } block equals one virtual host.

Do I have to restart Nginx after every change? systemctl reload nginx is enough: it applies the config without dropping active connections. A full restart is rarely needed.

How do I host several sites on one server? Create a separate file in sites-available for each domain with a unique server_name and root, then enable them with symlinks in sites-enabled.

Why does the browser show an old version of the page? The cache from the expires block kicked in. Clear the browser cache or shorten the cache lifetime while developing.

Once deployed, add HTTPS — Nginx works well with Let’s Encrypt certificates. For production sites, look at the plans on the VPS hosting page, and for a CMS there is WordPress VPS.

Edgars Kalējs