---
title: Installing a TLS certificate
slug: how-tos/KXtR-certificate-installation
createdAt: 2025-09-18T09:59:18.000Z
---

This guide describes how to serve Factry Historian and Grafana over HTTPS using a TLS certificate you provide, for example one issued by your organization's CA. It builds on the [Caddy reverse proxy setup](docId\:Hzv6xWojQnCTwCthjRrJt), which does not provision a certificate on its own. Follow this page to add the certificate that serves Factry Historian and Grafana over HTTPS.

## Prerequisites

- The [Caddy reverse proxy](docId\:Hzv6xWojQnCTwCthjRrJt) is installed and serving Grafana.
- A certificate and its private key in PEM format (`cert.pem` and `key.pem`).

If you received a `.pfx` or `.p12` file (common on Windows), it bundles the certificate and key together. Convert it to PEM:

```bash
# restrict permissions on the files created below (the key is written unencrypted)
umask 077
# leaf certificate
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out leaf.pem
# intermediate CA chain
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -out chain.pem
# full chain, leaf first (this is what Caddy serves)
cat leaf.pem chain.pem > cert.pem
# private key (unencrypted)
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out key.pem
```

:::hint{type="info"}
`-clcerts` exports only the leaf certificate. Most corporate CAs issue through an intermediate, so serving the leaf alone produces an incomplete chain and clients fail with `unable to verify the first certificate`. Exporting the chain and concatenating it (leaf first) avoids this.
:::

:::hint{type="info"}
If conversion fails with `unsupported algorithm RC2-40-CBC`, the file uses an older format. Add the `-legacy` flag to each `openssl` command.
:::

## 1. Place the certificate on the server

Create a dedicated folder and copy the certificate and key into it.

```bash
sudo mkdir -p /etc/ssl/factry
sudo cp cert.pem key.pem /etc/ssl/factry/
```

Remove the loose copies left in your working directory. The unencrypted key in particular must not stay behind.

```bash
shred -u cert.pem key.pem
rm -f leaf.pem chain.pem   # only exist if you converted from .pfx
```

:::hint{type="info"}
Use `shred -u` to overwrite and delete, or `rm` if `shred` is unavailable.
:::

:::hint{type="info"}
Use a dedicated folder like `/etc/ssl/factry`. The `/etc/ssl/certs` folder is managed by `update-ca-certificates`, so keep the served certificate out of it.
:::

## 2. Set permissions

Caddy needs to read both files. Give the `caddy` group read access and keep the key private.

```bash
sudo chown -R root:caddy /etc/ssl/factry
sudo chmod 750 /etc/ssl/factry
sudo chmod 640 /etc/ssl/factry/cert.pem /etc/ssl/factry/key.pem
```

## 3. Point Caddy at the certificate

Open the Caddy configuration file.

```bash
sudo vi /etc/caddy/Caddyfile
```

In the `:443` block from the [reverse proxy setup](docId\:Hzv6xWojQnCTwCthjRrJt), add the paths to your certificate and key. Add a second site block to serve Factry Historian over HTTPS on `18000`, proxying to the REST API on `localhost:8000`:

```nginx
:443 {
    tls /etc/ssl/factry/cert.pem /etc/ssl/factry/key.pem {
        alpn http1.1 h2
    }

    reverse_proxy http://localhost:3000
}

:18000 {
    tls /etc/ssl/factry/cert.pem /etc/ssl/factry/key.pem
    reverse_proxy http://localhost:8000
}
```

Validate the configuration, then stop and start Caddy for the change to take effect (intentionally no `restart` is used).

```bash
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl stop caddy && sudo systemctl start caddy
```

## 4. Update service URLs to HTTPS

Point the services at their new HTTPS addresses. Replace `<DNS_NAME>` with your server's FQDN.

**Grafana**

- In `/etc/grafana/grafana.ini`, set `root_url` to `https://<DNS_NAME>/`.
- Restart Grafana: `sudo systemctl restart grafana-server`.

**Factry Historian datasource plugin (Grafana)**

- When Grafana and Historian run on the same server, point the datasource URL at `http://127.0.0.1:<HISTORIAN_HTTP_PORT>`.
- When they run on different servers, use `https://<DNS_NAME>:<HISTORIAN_HTTPS_PORT>`.

## 5. Allow the HTTPS ports

If `ufw` is enabled, clients cannot reach the HTTPS ports until you allow them, so open these before validating. See [Enable Ubuntu firewall](docId\:oOLMnbb6Av_1WYgYMiC9H) for the `ufw` basics.

Allow the HTTPS ports used by your setup:

- Grafana HTTPS webpage: `443`
- Factry Historian HTTPS webpage: `18000`

```bash
sudo ufw allow 443       # Grafana over HTTPS
sudo ufw allow 18000     # Factry Historian over HTTPS
sudo ufw allow 5432      # PostgreSQL, if remote access is configured
```

Leave the plaintext HTTP ports open for now, so you can fall back if HTTPS does not come up. You block them in step 7, after validation passes.

## 6. Validate

Confirm each web interface is reachable over HTTPS:

```bash
curl -I https://<DNS_NAME>           # Grafana
curl -I https://<DNS_NAME>:18000     # Factry Historian
```

Also open Factry Historian and Grafana in your browser to confirm they load over HTTPS. If a page is not reachable, revert the `tls` change in the Caddyfile (stop and start Caddy) to return to the previous situation, then investigate using the troubleshooting steps below.

## 7. Block the plaintext HTTP ports

Only after step 6 passes, block the plaintext HTTP ports the HTTPS ports replace, for example:

```bash
sudo ufw deny 3000       # Grafana HTTP, now superseded
sudo ufw deny 8000       # Factry Historian HTTP, now superseded
```

:::hint{type="warning"}
Never block port `8001` (the Factry Historian gRPC port). Collectors rely on it.
:::

## Troubleshooting

### Caddy fails to start after adding the certificate

If Caddy cannot read the certificate or key, it logs the error on startup. Re-check the path and the permissions from steps 1 and 2.

```bash
sudo caddy validate --config /etc/caddy/Caddyfile
```

### Certificate signed by unknown authority

If a service reports `x509: certificate signed by unknown authority` (for example, the Grafana datasource reaching Historian over HTTPS), the server's trust store is missing the signing CA certificate. Add the root (and any intermediate) CA certificate to the trust store:

```bash
sudo cp root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
sudo systemctl restart <service>
```

### Old certificate still shown in the browser

Clear the browser cache or use a private window. If a corporate proxy sits in front of the server, it may cache the old certificate. Verify directly, bypassing the proxy, or confirm with an end user who has access without it.

### Inspect a certificate

Print the contents of a certificate to check its subject, validity and issuer:

```bash
openssl x509 -in /etc/ssl/factry/cert.pem -text -noout
```

## Good to know

- `.pfx` and `.p12` files bundle the certificate and private key, protected by a password. Convert them to PEM as shown in the prerequisites.
- Certificates expire. Track the expiry date and replace the certificate before it lapses, following the same steps.
