Create an Nginx Default Catch-All Site on Ubuntu
When Nginx receives a request for an unknown domain, it may serve the first enabled site if there is no explicit default server. On a server with multiple WordPress sites or web apps, that can expose the wrong site for a domain that is not configured.
A default catch-all site fixes this by handling unmatched HTTP and HTTPS requests before they fall through to a real site.
What the Script Does
The deployment script this article is based on:
- Creates
/etc/nginx/sites-available/000-catch-all.conf. - Enables it with a symlink in
/etc/nginx/sites-enabled/. - Removes Ubuntu's default enabled site if present.
- Supports a dry run mode.
- Requires confirmation before making changes.
- Backs up existing files.
- Runs
nginx -t. - Rolls back if validation fails.
- Reloads Nginx only after validation succeeds.
Why a Catch-All Site Helps
Without a catch-all, an unknown hostname can accidentally show whichever Nginx server block is first.
That can cause problems such as:
- A staging site responding to the wrong hostname.
- A WordPress site appearing for unrelated DNS records.
- SSL confusion when an unmatched HTTPS request reaches a real site.
- Extra noise in access logs.
The catch-all gives Nginx a deliberate default.
Create the Catch-All Config
Create the file:
sudo nano /etc/nginx/sites-available/000-catch-all.conf
Add:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_reject_handshake on;
}
For HTTP, return 444 tells Nginx to close the connection without sending a normal response.
For HTTPS, ssl_reject_handshake on; rejects unmatched TLS handshakes instead of serving a certificate for the wrong site.
Enable the Catch-All Site
Create the symlink:
sudo ln -sfn /etc/nginx/sites-available/000-catch-all.conf /etc/nginx/sites-enabled/000-catch-all.conf
If the default Ubuntu site is still enabled, back it up and remove the enabled symlink:
sudo mkdir -p /opt/server-backups/nginx
timestamp="$(date +%Y%m%d%H%M%S)"
if [ -e /etc/nginx/sites-enabled/default ] || [ -L /etc/nginx/sites-enabled/default ]; then
sudo cp -a /etc/nginx/sites-enabled/default "/opt/server-backups/nginx/sites-enabled-default.before.${timestamp}.bak"
sudo rm -f /etc/nginx/sites-enabled/default
fi
Validate and Reload
Test the config:
sudo nginx -t
If validation succeeds, reload Nginx:
sudo systemctl reload nginx
If validation fails, remove the catch-all symlink and restore your backup before reloading.
Safer Script Pattern
For production servers, use a script pattern with explicit flags:
sudo bash ./fix-nginx-catch-all.sh --dry-run sudo bash ./fix-nginx-catch-all.sh --confirm
A --dry-run mode lets you preview changes. A --confirm flag prevents accidental changes when someone runs the script without reading it.
You can also support a no-reload option:
sudo bash ./fix-nginx-catch-all.sh --confirm --no-reload
That is useful if you want a deployment system to validate now and reload Nginx later.
Test the Result
After reloading Nginx, test an unmatched HTTP host:
curl -I -H "Host: unknown.example.com" http://127.0.0.1/
For HTTPS, test with a hostname that should not match a real site:
curl -k -I --resolve unknown.example.com:443:127.0.0.1 https://unknown.example.com/
The exact client output can vary, but the important result is that the request should not serve one of your real websites.
Quick Reference
sudo nginx -t sudo ln -sfn /etc/nginx/sites-available/000-catch-all.conf /etc/nginx/sites-enabled/000-catch-all.conf sudo systemctl reload nginx
A default catch-all site is a small Nginx hardening step. It makes unmatched domains fail intentionally instead of drifting into the first configured website.