# Nginx: no "ssl_certificate" is defined in server on SSL port

> Nginx logs this when a TLS handshake reaches a port 443 server block without a certificate, often the default server. Add a certificate or reject the handshake.

Source: https://ssl.sh/errors/nginx-no-ssl-certificate-defined/
Last updated: 2026-11-10

**This error means a TLS client reached a server block on an SSL port that has no `ssl_certificate`, so Nginx had nothing to present and aborted the handshake.** Most often that block is the default server for port 443, which receives connections by IP address, without [SNI](https://ssl.sh/glossary/sni/), or for hostnames you do not serve.

## Quick fix

The port 443 server block without `ssl_certificate` needs a certificate or `ssl_reject_handshake on;`.

List every server block on port 443 and see which ones lack a certificate:

```bash
sudo nginx -T 2>/dev/null | grep -nE "server_name|listen .*443|ssl_certificate |default_server"
```

Then either give that block a certificate, or make your catch-all reject TLS handshakes explicitly (Nginx 1.19.4 or later):

```nginx
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
}
```

Run `sudo nginx -t && sudo systemctl reload nginx`. The [SSL checker](https://ssl.sh/tools/ssl-checker/) shows which certificate each hostname now receives.

## Causes

### 1. A catch-all or default server without a certificate

When any server uses `listen 443 ssl`, every server on that address and port handles TLS. A catch-all such as `server_name _;` copied from a port 80 template, or the distribution's default site, often has no certificate.

- **Confirm:** the error's `server:` field is `0.0.0.0:443` or `[::]:443`, and `nginx -T` shows a block on that port without `ssl_certificate`.
- **Fix:** add `ssl_reject_handshake on;` to that block, or remove it if you do not need it.

### 2. Clients that do not send SNI, or connect by IP

Scanners, health checks and old clients connect without [SNI](https://ssl.sh/glossary/sni/). Nginx cannot pick the named server and falls back to the default.

- **Confirm:** `openssl s_client -connect 203.0.113.10:443 -noservername` fails, while `-servername example.com` works.
- **Fix:** mark the real site as `default_server` if it should answer IP connections, or keep the rejecting catch-all. Make health checks send the hostname.

### 3. Certificate directives in the wrong place

`ssl_certificate` belongs in `http` or `server` context. It is not inherited from another server block, and a `location` block cannot hold it.

- **Confirm:** the certificate is configured only in a different server block for the same name, or in an include file that is not loaded (`nginx -T` does not show it).
- **Fix:** move the directives into the server block that has the matching `server_name`, or include the snippet there.

### 4. Duplicate server blocks for the same name

Tools such as Certbot's Nginx plugin edit one block; if you have two blocks with the same `server_name` on 443, Nginx uses the first and warns about a conflicting name.

- **Confirm:** `nginx -t` prints `conflicting server name "example.com" on 0.0.0.0:443, ignored`.
- **Fix:** merge the blocks so one block per name and port holds the certificate.

### 5. Certificate loaded from variables

Since Nginx 1.15.9 `ssl_certificate` can contain variables. If a variable resolves to an empty value or missing file, the handshake fails for those names.

- **Fix:** check the map that sets the variable and provide a default.

## Fixes by environment

On every platform, give the default server on port 443 a certificate or `ssl_reject_handshake on;`.

**Debian / Ubuntu packages:** the `default` site in `/etc/nginx/sites-enabled/` listens on port 80 only by default, but edited copies often add 443. Disable it or give it `ssl_reject_handshake on;`.

**Docker / Nginx Proxy Manager:** the generated default server may have no certificate. Configure a default certificate or a rejecting default in the proxy settings.

**Kubernetes ingress-nginx:** the controller serves its own fake default certificate for unknown hosts, so this message rarely appears; a name mismatch for unknown hosts is expected there.

**Older Nginx (before 1.19.4):** `ssl_reject_handshake` is not available; give the catch-all a self-signed certificate and return 444 to close connections.

## Don't do this

- **Serve your real certificate on the catch-all for every unknown name.** Visitors reaching the wrong name get a [name mismatch](https://ssl.sh/errors/net-err-cert-common-name-invalid/), and you advertise your domains to IP scanners.
- **Remove `ssl` from `listen` to make the error disappear.** The port would then speak plain HTTP to TLS clients and every HTTPS visitor would fail.
- **Ignore the log entirely.** Usually harmless, but the same message also appears when a real site loses its certificate during a config change.
