# HTTPS certificates

## When to use this

You want to know when certificates expire, renew one, or install your own. Scopes: `sites:read` (and `servers:read` for the server view), `certificates:renew` to renew, `sites:write` to turn HTTPS on or install certificates.

## Automatic HTTPS

Sites pfcloud created get a Let's Encrypt certificate by themselves once the domain points at the server. To ask right now: `POST /sites/{site}/https` (`202`, or `409` when not possible, for example the domain does not point at the server yet).

## Watch expiry

For one server, soonest expiry first:

```bash
curl -s -H "Authorization: Bearer $TOKEN" $MYFORGE/servers/SERVER_ID/certificates
```

Needs both `servers:read` and `sites:read`. Each item has `site`, `domain`, `type` (`letsencrypt`, `custom`, `wildcard`, `moved`), `issuer`, `expires_at`, `days_left`, `checked_at` and `can_renew`.

For one site, with history:

```bash
curl -s -H "Authorization: Bearer $TOKEN" $MYFORGE/sites/SITE_ID/certificate
```

The same fields plus `history`: up to 20 events, newest first, each with `kind`, `expires_at`, `previous_expires_at`, `issuer`, `detail` and `at` (first seen, renewed, renewal requested or failed).

pfcloud also checks every certificate daily and emails the team at 14, 3 and 0 days.

## Renew

```bash
curl -s -X POST $MYFORGE/sites/SITE_ID/certificate/renew \
  -H "Authorization: Bearer $TOKEN" -H "Idempotency-Key: renew-shop-1"
```

Answers `202`. It takes about a minute and the site stays online. Check `GET /sites/{site}/certificate` afterwards: `expires_at` should move and the history shows the renewal. At most one renewal per site every 10 minutes. Only Let's Encrypt certificates can be renewed (`can_renew`).

## Custom and wildcard certificates

- `POST /sites/{site}/certificate` with `certificate` (PEM chain) and `private_key` (PEM). pfcloud checks the key matches, the certificate covers the site and has not expired. The key is never stored or logged.
- `POST /sites/{site}/certificate/wildcard` asks for a wildcard certificate through Cloudflare DNS. It needs a Cloudflare connection in team settings that manages the zone.

> Sending a private key through an AI assistant means the key passes through that assistant's conversation. Do this yourself in the dashboard or from a script, not through a chat.

## What can go wrong

| Problem | Fix |
| --- | --- |
| `409` when asking for HTTPS | Domain does not point at the server. See [DNS and domains](/developers/dns-and-domains). |
| `can_renew` is false | The certificate is custom or moved. Renew it where it came from. |
| Renewal fails | Check the history `detail`, make sure port 80 is reachable and the domain still points to the server. |
