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;: Thessl 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 errorunknown directive "ssl". Also, puttingssl on;in alisten 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 withnginx -v; if it's older than 1.25.1, remove thehttp2 on;line and writelisten 443 ssl http2;. On 1.25.1 and later, writinglisten … http2produces a deprecation warning. For details, see How to enable HTTP/2 in Nginx. - Point
ssl_certificateto a file that includes the intermediate certificate: For Let's Encrypt, that'sfullchain.pem, notcert.pem. Specifyingcert.pemcauses certificate errors in some environments. - TLS versions: According to the official documentation as of October 2026, the default for
ssl_protocolsisTLSv1.2 TLSv1.3. Ifnginx.confor a file carried over from an old setup contains a line likessl_protocols TLSv1 TLSv1.1 …;, change it toTLSv1.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'sindex.php. This makes permalink settings such as "Post name" work.- The socket path for
fastcgi_passvaries by environment. On Debian / Ubuntu it's under/run/php/with a name likephp8.3-fpm.sock; on standard RHEL-based packages/run/php-fpm/www.sockis common. Check the actual name withls /run/php/ /run/php-fpm/. - Regex
locationblocks are evaluated in the order written, and the first match wins. That's why thelocationthat 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:
- It narrows down to server blocks whose
listenmatches the IP address and port that received the request. - Among those, it picks the one whose
server_namematches the request'sHostheader. - If none match, the port's default server block is used. The default is the one with
default_serveron itslisten, 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 -treports an error: The message includes the file name and line number.unknown directive "ssl"meansssl on;is still there.- Another site or the Nginx welcome page appears: Check the spelling of
server_name, whether the symbolic link exists, and duplicatedefault_serverentries.sudo nginx -Tprints the entire configuration actually loaded. - 403 Forbidden: Check that the file named in
indexexists in therootdirectory 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_passis correct.
Summary
- A server block is the per-site unit of configuration. On Debian / Ubuntu, write it in
sites-availableand link it intosites-enabled; on RHEL-based systems and official packages, place it inconf.d/*.conf. - Run
sudo nginx -tbefore applying andsudo systemctl reload nginxto apply. - Enable HTTPS with
listen 443 ssl;.ssl on;no longer works in current Nginx. For HTTP/2 on 1.25.1 and later, usehttp2 on;. - For WordPress, set
try_files $uri $uri/ /index.php?$args;andfastcgi_passto PHP-FPM. - Routing is decided by
listen, thenserver_name, thendefault_server.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)