2026年11月12日

2026年11月12日

Nginx Server Blocks: How to Write and Set Them Up (HTTP, HTTPS, WordPress)

This article is a localized edition of the Japanese original. Read the Japanese original

An Nginx server block is the unit of configuration that defines "how to handle requests for which domain (or IP address and port)." It is the equivalent of an Apache virtual host. To run several sites on one server, you write one server block per site.

This article covers, in order: where configuration files go, a basic HTTP-only block, how to enable and syntax-check it, HTTPS settings, settings for WordPress (PHP-FPM), and how requests are routed to a server block. The examples passed a syntax check with nginx -t on the Nginx stable release (1.30) as of October 2026.

Where the configuration files go

Where you write server blocks depends on how Nginx was installed. Look at the include lines inside http { … } in /etc/nginx/nginx.conf to see which locations are loaded.

Installation Location How to enable
Standard Debian / Ubuntu package Create in /etc/nginx/sites-available/ Create a symbolic link in /etc/nginx/sites-enabled/
Standard package on RHEL-based systems (AlmaLinux, etc.), official nginx.org packages /etc/nginx/conf.d/ Files with the .conf extension are loaded just by placing them there

The examples below use the domain example.com with public files in /var/www/example.com/html. Replace the domain and paths with your own. For the overall structure of the configuration files, see also The structure of Nginx configuration files.

Step 1: Prepare the files to serve

sudo mkdir -p /var/www/example.com/html
echo 'Hello from example.com' | sudo tee /var/www/example.com/html/index.html

Step 2: Write an HTTP server block

First, write a basic block that works over HTTP (port 80) only. Save it as /etc/nginx/sites-available/example.com on Debian / Ubuntu or /etc/nginx/conf.d/example.com.conf on RHEL-based systems.

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

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

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

    location / {
        try_files $uri $uri/ =404;
    }
}
Directive Meaning
listen The port to listen on. [::]:80 is for IPv6. Remove this line if IPv6 is disabled on the server
server_name The domain names this server block handles. List several separated by spaces
root The directory of files to serve
index The file returned when a URL points to a directory
access_log / error_log Separate logs per site make troubleshooting easier
try_files Looks for the requested file, then the directory, and returns 404 if neither exists

Step 3: Enable, syntax-check, and reload

On Debian / Ubuntu, enable the site by creating a symbolic link in sites-enabled. If you placed the file in conf.d on a RHEL-based system, skip this step.

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

Check the syntax before applying the configuration. If you reload with an error in place, the configuration is not applied.

sudo nginx -t

When you see syntax is ok and test is successful, reload the configuration. reload switches to the new configuration without dropping connections in progress.

sudo systemctl reload nginx

Access the site with curl -H 'Host: example.com' http://YOUR_SERVER_IP/ (replace YOUR_SERVER_IP with your server's IP address) or a browser, and confirm that the contents of index.html are returned. For how to read errors, see How to test Nginx configuration files.

Step 4: Write HTTPS server blocks

HTTPS requires a server certificate. Getting one from Let's Encrypt with certbot is explained in How to set up an SSL certificate on Nginx with certbot (certbot's --nginx option also edits the server block for you). Here is what it looks like when written by hand, assuming the certificate is in /etc/letsencrypt/live/example.com/.

# HTTP: redirect everything to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    return 301 https://example.com$request_uri;
}

# HTTPS: redirect www to the non-www host
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    return 301 https://example.com$request_uri;
}

# HTTPS: main site
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

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

Points to watch when writing it:

  • Enable SSL with listen 443 ssl;: The ssl on; found in older articles was deprecated in Nginx 1.15.0 and removed in 1.25.1. In current Nginx it fails to start with the error unknown directive "ssl". Also, putting ssl on; in a listen 80; server block makes port 80 expect HTTPS, so normal requests fail.
  • HTTP/2 syntax depends on the version: http2 on; is the syntax for Nginx 1.25.1 and later. Check with nginx -v; if it's older than 1.25.1, remove the http2 on; line and write listen 443 ssl http2;. On 1.25.1 and later, writing listen … http2 produces a deprecation warning. For details, see How to enable HTTP/2 in Nginx.
  • Point ssl_certificate to a file that includes the intermediate certificate: For Let's Encrypt, that's fullchain.pem, not cert.pem. Specifying cert.pem causes certificate errors in some environments.
  • TLS versions: According to the official documentation as of October 2026, the default for ssl_protocols is TLSv1.2 TLSv1.3. If nginx.conf or a file carried over from an old setup contains a line like ssl_protocols TLSv1 TLSv1.1 …;, change it to TLSv1.2 TLSv1.3.

After editing, run sudo nginx -t and sudo systemctl reload nginx as in Step 3.

Using it with WordPress (PHP-FPM)

To run WordPress, change the index and location settings in the main server block as follows. PHP processing is handed to PHP-FPM.

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

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

    # Don't serve dotfiles such as .htaccess or .git (except .well-known)
    location ~ /\.(?!well-known/) {
        deny all;
    }

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

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~* \.(css|js|png|jpe?g|gif|svg|webp|ico|woff2?)$ {
        expires 30d;
        access_log off;
    }
}
  • try_files $uri $uri/ /index.php?$args; passes URLs that don't exist as files to WordPress's index.php. This makes permalink settings such as "Post name" work.
  • The socket path for fastcgi_pass varies by environment. On Debian / Ubuntu it's under /run/php/ with a name like php8.3-fpm.sock; on standard RHEL-based packages /run/php-fpm/www.sock is common. Check the actual name with ls /run/php/ /run/php-fpm/.
  • Regex location blocks are evaluated in the order written, and the first match wins. That's why the location that denies dotfiles is placed first.
  • expires 30d; tells browsers to cache CSS and images for 30 days. If an old file still shows after an update, check whether the version number WordPress adds to the file URL (?ver=) has changed.

PHP-FPM configuration and tuning are covered in How to set up Nginx and PHP-FPM.

How Nginx picks a server block

When a server has several server blocks, Nginx decides where a request goes in this order:

  1. It narrows down to server blocks whose listen matches the IP address and port that received the request.
  2. Among those, it picks the one whose server_name matches the request's Host header.
  3. If none match, the port's default server block is used. The default is the one with default_server on its listen, or if none has it, the first server block loaded for that port.

To keep requests for unconfigured domain names or bare IP addresses from reaching an unintended site, add a default server block that returns nothing. return 444; is an Nginx-specific instruction that closes the connection without sending a response.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

On Debian / Ubuntu, /etc/nginx/sites-enabled/default, which is enabled out of the box, already has default_server. Two default_server entries on the same port make nginx -t fail, so if you add the block above, either delete the default link (sudo rm /etc/nginx/sites-enabled/default) or remove default_server from one of them.

Troubleshooting

  • nginx -t reports an error: The message includes the file name and line number. unknown directive "ssl" means ssl on; is still there.
  • Another site or the Nginx welcome page appears: Check the spelling of server_name, whether the symbolic link exists, and duplicate default_server entries. sudo nginx -T prints the entire configuration actually loaded.
  • 403 Forbidden: Check that the file named in index exists in the root directory and that the Nginx user can read the files.
  • 502 Bad Gateway (WordPress): Check that PHP-FPM is running and that the socket path in fastcgi_pass is correct.

Summary

  • A server block is the per-site unit of configuration. On Debian / Ubuntu, write it in sites-available and link it into sites-enabled; on RHEL-based systems and official packages, place it in conf.d/*.conf.
  • Run sudo nginx -t before applying and sudo systemctl reload nginx to apply.
  • Enable HTTPS with listen 443 ssl;. ssl on; no longer works in current Nginx. For HTTP/2 on 1.25.1 and later, use http2 on;.
  • For WordPress, set try_files $uri $uri/ /index.php?$args; and fastcgi_pass to PHP-FPM.
  • Routing is decided by listen, then server_name, then default_server.

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲