Back to blog
InfraFebruary 25, 20254 min read

Building a Catch-All Landing Page for Unknown Subdomains

Instead of a 404 or a blank nginx page, unknown subdomains on our VPS get a branded experience. Here is how the nginx config and the page itself are set up.

When you run a bunch of subdomains off a single domain, there are always requests coming in for subdomains that do not exist. Old links, typos, bots scanning for common subdomains. By default nginx either returns a 444 (connection closed) or serves whatever the first server block is, which is usually not what you want.

A better approach is a dedicated catch-all server block that serves a branded page for any subdomain that does not match a real service.

nginx catch-all config

The key is using an underscore as the server_name, which nginx uses as the default catch-all:

nginx
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name _;

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

    root /var/www/catchall;
    index index.html;

    location / {
        try_files $uri /index.html;
    }
}

This requires a wildcard SSL cert (*.execross.com) to avoid certificate errors on unknown subdomains. Without the wildcard cert, visitors will get a browser security warning before even seeing your page.

The catch-all page

We built a dark cosmos-themed page with an animated canvas star field. It shows the Execross brand and a link back to the main site. Nothing interactive, just a clean branded experience instead of a generic error.

The canvas animation is pure JavaScript, no dependencies:

javascript
const canvas = document.getElementById("stars");
const ctx = canvas.getContext("2d");
const stars = [];

function resize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}

function createStars(count) {
  for (let i = 0; i < count; i++) {
    stars.push({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      r: Math.random() * 1.5,
      alpha: Math.random(),
      delta: (Math.random() * 0.01) + 0.002,
    });
  }
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (const star of stars) {
    star.alpha += star.delta;
    if (star.alpha >= 1 || star.alpha <= 0) star.delta *= -1;
    ctx.beginPath();
    ctx.arc(star.x, star.y, star.r, 0, Math.PI * 2);
    ctx.fillStyle = `rgba(200, 180, 255, ${star.alpha})`;
    ctx.fill();
  }
  requestAnimationFrame(draw);
}

resize();
window.addEventListener("resize", resize);
createStars(200);
draw();

The whole page is a single HTML file with embedded CSS and JavaScript. No build step, no dependencies. It gets deployed to /var/www/catchall/index.html and nginx serves it statically.

Testing it

To test locally, add a fake subdomain to /etc/hosts pointing to your VPS IP:

1.2.3.4 nonexistent.execross.com

Then visit it in a browser. You should see the catch-all page instead of a browser error. If you see a certificate warning, your wildcard cert is not installed correctly.

The 444 option (silently dropping connections) is better for security against bots but worse for user experience. The branded catch-all is our preference for anything under a real domain.

More articles