# TLS and domains

> Serve the panel over HTTPS with ACME, your own certificate, or a reverse proxy.

Source: https://joinnasnet.com/en/guides/nasnet-linux/tls-and-domains/
Last updated: 2026-08-14

---

## Overview

You should serve the panel over HTTPS in production, because secure session
cookies require it. There are three ways to get there, and the app picks one at
startup in a fixed order: **manual certificate files → ACME → plain HTTP**. This
page is about TLS for the panel itself; certificates used by your proxy inbounds
are managed on the [Certificates](/en/guides/nasnet-linux/certificates/) page.

## Before you start

- A domain whose DNS already points at the server, if you want ACME.
- The ACME challenge must be able to reach the server from the internet.
- Decide whether the panel terminates TLS itself or sits behind nginx, Caddy,
  Traefik or a CDN.

## Steps

### Automatic certificates with ACME

1. Point your domain at the server and confirm DNS has propagated.
2. Set the ACME keys in `.env`:

   ```dotenv
   APP_BASE_URL=https://panel.example.com
   ACME_ENABLED=true
   ACME_EMAIL=you@example.com
   ACME_STAGING=true
   ACME_AUTO_RENEW=true
   ACME_CACHE_DIR=/app/data/acme
   JWT_COOKIE_DOMAIN=example.com
   JWT_COOKIE_SECURE=true
   ```

3. Restart and confirm a staging certificate is issued. Then set
   `ACME_STAGING=false` and restart again for the real one. Staging first keeps
   you from burning Let's Encrypt rate limits while you debug DNS.
4. Issued certificates are cached in `ACME_CACHE_DIR`, which is a persistent
   volume under Docker, so restarts do not re-issue.

ACME settings are also editable in **Settings → Server → HTTPS** and applied on
the next restart, so you can flip ACME on or off without touching `.env`.

### Bringing your own certificate

Point the app at your PEM files. Manual files take precedence over ACME.

```dotenv
TLS_CERT_FILE=/path/to/fullchain.pem
TLS_KEY_FILE=/path/to/privkey.pem
```

### Behind a reverse proxy

Terminate TLS at the proxy and let the app speak plain HTTP locally, but still
set `APP_BASE_URL` to the public HTTPS URL.

```dotenv
APP_BASE_URL=https://panel.example.com
ACME_ENABLED=false
JWT_COOKIE_SECURE=true
JWT_COOKIE_DOMAIN=example.com
```

The panel uses WebSockets and server-sent events for the live terminal, charts
and events, so the proxy must pass `Upgrade` and `Connection` headers and use a
generous read timeout.

```nginx
server {
    listen 443 ssl http2;
    server_name panel.example.com;

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

    location / {
        proxy_pass http://127.0.0.1:9761;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}
```

## Reference

| Goal | Set |
| ---- | --- |
| Automatic HTTPS on a domain | `ACME_ENABLED=true`, `ACME_EMAIL`, `APP_BASE_URL=https://…` |
| Your own certificate | `TLS_CERT_FILE`, `TLS_KEY_FILE` |
| Behind a proxy or CDN | `APP_BASE_URL=https://…`, ACME off, secure cookies on |
| Plain IP, for testing only | `APP_BASE_URL=http://ip:port`, ACME off, `JWT_COOKIE_SECURE=false` |

| Variable | What it does | Default |
| -------- | ------------ | ------- |
| `ACME_ENABLED` | Turns automatic issuance on. With it off, no certificate is issued even if `APP_BASE_URL` is HTTPS. | `false` |
| `ACME_EMAIL` | Contact address for Let's Encrypt notices. Required when ACME is on. | empty |
| `ACME_STAGING` | Uses the staging directory while you test. | `false` |
| `ACME_AUTO_RENEW` | Keeps the certificate renewed. | `true` |
| `ACME_CACHE_DIR` | Where issued certificates are cached. | `/app/data/acme` |
| `TLS_CERT_FILE` | Path to your certificate chain. Overrides ACME. | unset |
| `TLS_KEY_FILE` | Path to your private key. | unset |
| `JWT_COOKIE_SECURE` | Marks session cookies secure. Needs HTTPS. | `false` |
| `JWT_COOKIE_DOMAIN` | Cookie domain; leave empty in IP mode. | empty |

## Troubleshooting

**Certificate issuance fails.** Confirm the domain resolves to this server and
that the ACME challenge can reach it, because a firewall or a proxy in front of the
panel that does not pass the challenge path is the usual cause. Test with
`ACME_STAGING=true` so failures do not count against your rate limit.

## Related

- [First run and admin sign-in](/en/guides/nasnet-linux/first-run/)
- [Certificates](/en/guides/nasnet-linux/certificates/)
- [Settings](/en/guides/nasnet-linux/settings/)
