Setting up HTTPS on your Shelly device
(Since firmware 2.0.0)
What is an HTTPS certificate and why do I need one?
When you access your Shelly device through a web browser or an API call, the communication normally happens over plain HTTP — unencrypted and readable by anyone on the same network. An HTTPS certificate enables encrypted (TLS) communication between your browser or application and the device, preventing eavesdropping and tampering.
Devices shipped from factory with firmware 2.0.0+ come with HTTPS already enabled using certificates issued by Shelly's internal PKI. Devices that were updated to firmware 2.0.0+ (but not originally shipped with it) do not have factory-provisioned certificates and serve only plain HTTP by default.
Without a certificate your device continues to work normally over HTTP — no action is required. Setting up HTTPS is only needed if you want encrypted access to your device.
Ways to obtain a certificate
There are two main approaches to enabling HTTPS on your device:
| Approach | Difficulty | Browser warnings? | Requirements |
|---|---|---|---|
| Self-signed certificate | Basic | Yes — browsers will show a security warning because the certificate is not issued by a trusted authority. The connection is still encrypted. | OpenSSL on your workstation |
| Trusted certificate (e.g. Let's Encrypt) | Advanced | No — browsers trust the certificate automatically. | A domain name pointing to the device's local IP (e.g. via a local DNS server) |
Both paths use the same RPC methods to upload the certificate to the device. You can also upload certificates through the Web UI under Settings → TLS Configuration.
RPC Methods for Server Certificates
Three dedicated RPC methods are available for managing the HTTPS server certificate. They follow the same interface as the existing Shelly.PutTLSClientCert and related methods used for outbound TLS.
Shelly.PutHTTPServerCert
Uploads or removes the HTTPS server certificate. The certificate is written to a file on the device's flash storage. A device reboot is required for changes to take effect.
Removal behavior: When removing the certificate (passing null or ""), the server key and CA bundle are also removed automatically. If the device has factory-provisioned certificates, they are restored; otherwise HTTPS is disabled.
Request
Parameters:
| Property | Type | Description |
|---|---|---|
| string or null | PEM-encoded server certificate contents. Pass |
| boolean | If |
Response
When uploading (data is a non-empty string):
| Property | Type | Description |
|---|---|---|
| number | Total size of the certificate file in bytes. |
| boolean | Always |
When removing (data is null or ""):
| Property | Type | Description |
|---|---|---|
| boolean | Always |
Shelly.PutHTTPServerKey
Uploads or removes the HTTPS server private key. The key is written to a file on the device's flash storage. A device reboot is required for changes to take effect.
Removal behavior: When removing the key (passing null or ""), the server certificate and CA bundle are also removed automatically. If the device has factory-provisioned certificates, they are restored; otherwise HTTPS is disabled.
Request
Parameters:
| Property | Type | Description |
|---|---|---|
| string or null | PEM-encoded private key contents. Pass |
| boolean | If |
Response
When uploading (data is a non-empty string):
| Property | Type | Description |
|---|---|---|
| number | Total size of the key file in bytes. |
| boolean | Always |
When removing (data is null or ""):
| Property | Type | Description |
|---|---|---|
| boolean | Always |
Shelly.PutHTTPServerCABundle
Uploads or removes a CA certificate bundle used for client certificate authentication (mutual TLS). When a CA certificate is configured, the device will require connecting HTTPS clients to present a valid client certificate signed by this CA. Clients that do not present a valid certificate will be rejected.
This is optional — if no CA certificate is set, the device accepts any HTTPS connection without verifying the client's identity. Setting a CA certificate is useful when you want to restrict access to the device to clients that hold a certificate issued by your own certificate authority.
The CA certificate bundle is written to a file on the device's flash storage. The configuration stores a reference to this file, and the TLS library loads the certificate on demand during the TLS handshake.
Removal behavior: Removing the CA bundle only removes the CA — the server certificate and key remain intact.
Request
Parameters:
| Property | Type | Description |
|---|---|---|
| string, empty string, or null | PEM-encoded CA certificate contents. Pass |
| boolean | If |
Response
When uploading (data is a non-empty string):
| Property | Type | Description |
|---|---|---|
| number | Total size of the CA certificate file in bytes. |
| boolean | Always |
When removing (data is null or ""):
| Property | Type | Description |
|---|---|---|
| boolean | Always |
When you remove the certificate or key, the device automatically removes all three files (certificate, key, and CA bundle) to avoid leaving the device in a half-configured state. Removing only the CA bundle removes just the CA — the certificate and key are left intact.
If the device was shipped with factory-provisioned HTTPS certificates, removing user-uploaded certificates restores the factory certificates. Devices without factory certificates revert to HTTP-only operation.
After uploading a certificate and key and rebooting, HTTPS becomes available on port 443. The plain HTTP listener on port 80 continues to operate but will redirect all requests to HTTPS when enhanced_security is enabled. This applies to devices with factory-provisioned HTTPS certificates (where enhanced_security is enabled by default) or when the user manually enables enhanced_security. Both cases apply starting from firmware 2.0.x. Use https://<device-ip> to access the device. Browsers and curl will show certificate warnings for self-signed certificates — use curl -k or add the certificate to your trust store.
Option A: Self-signed certificate
A self-signed certificate encrypts the connection but is not issued by a trusted certificate authority. Browsers and tools like curl will show a warning — this is expected and does not mean the encryption is weak.
Prerequisites
- OpenSSL command-line tool installed on your workstation
- Network access to the Shelly device
Step 1: Generate a Private Key
Generate an EC private key (P-256 curve). EC keys are preferred on embedded devices because they are smaller and faster than RSA:
openssl ecparam -genkey -name prime256v1 -noout -out server.key
We strongly recommend using ECC (Elliptic Curve) private keys rather than RSA. ECC keys are significantly smaller and faster to process, which matters on resource-constrained embedded devices.
The following EC curves are supported:
| Curve | OpenSSL name | Recommended | Notes |
|---|---|---|---|
| P-256 (secp256r1) | prime256v1 | Yes | Best choice — smallest key, hardware-accelerated on Gen4 (ESP32-C6) devices |
| P-384 (secp384r1) | secp384r1 | Suitable | Stronger security margin, software-only |
| P-521 (secp521r1) | secp521r1 | Suitable | Largest key size, software-only |
RSA keys (2048-bit and above) are supported but not recommended — the TLS handshake will be noticeably slower and consume more RAM.
Step 2: Create an OpenSSL Configuration File
Create a file named openssl.cnf with the following contents. Replace 192.168.1.100 with the actual IP address of your Shelly device:
[req]
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = v3_req
[dn]
CN = Shelly Device
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.1.100
You can add multiple IP addresses or DNS names by adding more entries:
[alt_names]
IP.1 = 192.168.1.100
IP.2 = 10.0.0.50
DNS.1 = myshelly.local
Step 3: Generate a Certificate Signing Request (CSR)
openssl req -new -key server.key -out server.csr -config openssl.cnf
Step 4: Self-Sign the Certificate
Sign the certificate with a validity of 10 years (3650 days):
openssl x509 -req -in server.csr -signkey server.key \
-out server.crt -days 3650 \
-extensions v3_req -extfile openssl.cnf
Step 5: Verify the Certificate
openssl x509 -in server.crt -text -noout
Check that the output includes:
Subject: CN = Shelly DeviceX509v3 Subject Alternative Name:with your IP address
Step 6: Upload the Certificate and Key to the Device
Upload the certificate using Shelly.PutHTTPServerCert:
curl -X POST -d "{
\"id\": 1,
\"method\": \"Shelly.PutHTTPServerCert\",
\"params\": {
\"data\": $(python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' < server.crt)
}
}" http://${SHELLY}/rpc
Upload the private key using Shelly.PutHTTPServerKey:
curl -X POST -d "{
\"id\": 1,
\"method\": \"Shelly.PutHTTPServerKey\",
\"params\": {
\"data\": $(python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' < server.key)
}
}" http://${SHELLY}/rpc
Step 7: Reboot the Device
The HTTP server reads certificate configuration at startup. A reboot is required for changes to take effect:
- Shelly.Reboot HTTP GET Request
- Shelly.Reboot Curl Request
- Shelly.Reboot Mos Request
http://192.168.33.1/rpc/Shelly.Reboot
curl -X POST -d '{"id":1,"method":"Shelly.Reboot"}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.Reboot
Step 8: Verify HTTPS is Working
After the device reboots, connect using HTTPS. The -k flag is needed because self-signed certificates are not trusted by default:
curl -k https://${SHELLY}/rpc/Shelly.GetDeviceInfo
To verify the certificate being served matches your generated certificate:
openssl s_client -connect ${SHELLY}:80 -showcerts </dev/null 2>/dev/null | \
openssl x509 -text -noout | grep -E "Subject:|Alternative|IP Address"
Option B: Trusted certificate (Let's Encrypt)
A certificate issued by a trusted authority such as Let's Encrypt eliminates browser warnings entirely. This approach is more involved because Let's Encrypt must verify that you control the domain name associated with the certificate.
Prerequisites
- A domain name (e.g.
myshelly.example.com) that resolves to your device's local IP address. This is typically configured on a local DNS server or in your router's DNS settings. - Certbot or another ACME client installed on your workstation.
- The ability to complete a DNS-01 challenge (required because the device is on a local network and not reachable from the internet).
Step 1: Request a Certificate via DNS-01 Challenge
Because the device is on a private network, you cannot use the default HTTP-01 challenge. Use the DNS-01 challenge instead — this requires adding a TXT record to your domain's DNS:
certbot certonly --manual --preferred-challenges dns \
-d myshelly.example.com
Certbot will ask you to create a DNS TXT record for _acme-challenge.myshelly.example.com. Add the record through your DNS provider, wait for propagation, then press Enter to continue.
Step 2: Locate the Certificate Files
After successful verification, Certbot stores the certificate files (typically under /etc/letsencrypt/live/myshelly.example.com/):
fullchain.pem— the server certificate + intermediate CA chainprivkey.pem— the private key
Step 3: Upload to the Device
Upload using the same RPC methods as for self-signed certificates. Use fullchain.pem (not cert.pem) to include the full certificate chain:
curl -X POST -d "{
\"id\": 1,
\"method\": \"Shelly.PutHTTPServerCert\",
\"params\": {
\"data\": $(python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' < /etc/letsencrypt/live/myshelly.example.com/fullchain.pem)
}
}" http://${SHELLY}/rpc
curl -X POST -d "{
\"id\": 1,
\"method\": \"Shelly.PutHTTPServerKey\",
\"params\": {
\"data\": $(python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' < /etc/letsencrypt/live/myshelly.example.com/privkey.pem)
}
}" http://${SHELLY}/rpc
Step 4: Reboot and Verify
- Shelly.Reboot HTTP GET Request
- Shelly.Reboot Curl Request
- Shelly.Reboot Mos Request
http://192.168.33.1/rpc/Shelly.Reboot
curl -X POST -d '{"id":1,"method":"Shelly.Reboot"}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.Reboot
After reboot, verify with:
curl https://myshelly.example.com/rpc/Shelly.GetDeviceInfo
No -k flag is needed — the certificate is trusted.
Let's Encrypt certificates are valid for 90 days. You will need to repeat the renewal and upload process before the certificate expires. Consider automating this with a script that runs certbot renew and re-uploads the new certificate via Shelly.PutHTTPServerCert.
Client Certificate Authentication (Mutual TLS)
By default, HTTPS on the device only authenticates the server — the client knows it is talking to the right device, but the device accepts connections from anyone. By uploading a CA certificate with Shelly.PutHTTPServerCABundle, you can enable mutual TLS (mTLS): the device will require each connecting client to present a certificate signed by that CA.
When to use this
- You want to restrict API access to authorized clients only (e.g. a home automation controller that holds a client certificate).
- You are deploying devices on a network where password-based authentication alone is not sufficient.
How it works
- You create your own Certificate Authority (CA) and generate client certificates signed by it.
- Upload the CA certificate to the device using
Shelly.PutHTTPServerCABundle. - Reboot the device.
- When a client connects over HTTPS, it must present a certificate signed by your CA. If it does not, the TLS handshake is rejected.
Connecting with a client certificate
After uploading the CA and rebooting, curl without a client certificate will fail:
# This will fail — no client certificate presented
curl -k https://${SHELLY}/rpc/Shelly.GetDeviceInfo
Provide the client certificate and key to authenticate:
curl -k --cert client.crt --key client.key \
https://${SHELLY}/rpc/Shelly.GetDeviceInfo
Removing client certificate authentication
To stop requiring client certificates, remove the CA by passing null or an empty string, then reboot:
- Shelly.PutHTTPServerCABundle HTTP GET Request
- Shelly.PutHTTPServerCABundle Curl Request
- Shelly.PutHTTPServerCABundle Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCABundle?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCABundle","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCABundle '{"data":null}'
Response
- Shelly.PutHTTPServerCABundle HTTP GET Response
- Shelly.PutHTTPServerCABundle Curl Response
- Shelly.PutHTTPServerCABundle Mos Response
{
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"restart_required": true
}
}
{
"restart_required": true
}
- Shelly.Reboot HTTP GET Request
- Shelly.Reboot Curl Request
- Shelly.Reboot Mos Request
http://192.168.33.1/rpc/Shelly.Reboot
curl -X POST -d '{"id":1,"method":"Shelly.Reboot"}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.Reboot
Both null and an empty string "" are accepted and have the same effect — the CA certificate file is deleted from flash and the configuration is reset to default. After reboot, the device will accept HTTPS connections without requiring a client certificate.
Removing a Custom Certificate
To revert to plain HTTP, remove the certificate and key by passing null, then reboot:
- Shelly.PutHTTPServerCert HTTP GET Request
- Shelly.PutHTTPServerCert Curl Request
- Shelly.PutHTTPServerCert Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCert?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCert","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCert '{"data":null}'
- Shelly.PutHTTPServerKey HTTP GET Request
- Shelly.PutHTTPServerKey Curl Request
- Shelly.PutHTTPServerKey Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerKey?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerKey","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerKey '{"data":null}'
- Shelly.Reboot HTTP GET Request
- Shelly.Reboot Curl Request
- Shelly.Reboot Mos Request
http://192.168.33.1/rpc/Shelly.Reboot
curl -X POST -d '{"id":1,"method":"Shelly.Reboot"}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.Reboot
After reboot, the device will be accessible via plain HTTP again on port 80.
If you also had a CA certificate configured for client authentication, remove it as well:
- Shelly.PutHTTPServerCABundle HTTP GET Request
- Shelly.PutHTTPServerCABundle Curl Request
- Shelly.PutHTTPServerCABundle Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCABundle?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCABundle","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCABundle '{"data":null}'
Examples
Shelly.PutHTTPServerCert example
- Shelly.PutHTTPServerCert HTTP GET Request
- Shelly.PutHTTPServerCert Curl Request
- Shelly.PutHTTPServerCert Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCert?data="-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCert","params":{"data":"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCert '{"data":"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"}'
Response
- Shelly.PutHTTPServerCert HTTP GET Response
- Shelly.PutHTTPServerCert Curl Response
- Shelly.PutHTTPServerCert Mos Response
{
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"restart_required": true
}
}
{
"restart_required": true
}
Shelly.PutHTTPServerKey example
- Shelly.PutHTTPServerKey HTTP GET Request
- Shelly.PutHTTPServerKey Curl Request
- Shelly.PutHTTPServerKey Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerKey?data="-----BEGIN EC PRIVATE KEY-----\nMHQC...\n-----END EC PRIVATE KEY-----\n"
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerKey","params":{"data":"-----BEGIN EC PRIVATE KEY-----\nMHQC...\n-----END EC PRIVATE KEY-----\n"}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerKey '{"data":"-----BEGIN EC PRIVATE KEY-----\nMHQC...\n-----END EC PRIVATE KEY-----\n"}'
Response
- Shelly.PutHTTPServerKey HTTP GET Response
- Shelly.PutHTTPServerKey Curl Response
- Shelly.PutHTTPServerKey Mos Response
{
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"restart_required": true
}
}
{
"restart_required": true
}
Shelly.PutHTTPServerCABundle example
- Shelly.PutHTTPServerCABundle HTTP GET Request
- Shelly.PutHTTPServerCABundle Curl Request
- Shelly.PutHTTPServerCABundle Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCABundle?data="-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCABundle","params":{"data":"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCABundle '{"data":"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"}'
Response
- Shelly.PutHTTPServerCABundle HTTP GET Response
- Shelly.PutHTTPServerCABundle Curl Response
- Shelly.PutHTTPServerCABundle Mos Response
{
"len": 753,
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"len": 753,
"restart_required": true
}
}
{
"len": 753,
"restart_required": true
}
Removing the server certificate
- Shelly.PutHTTPServerCert HTTP GET Request
- Shelly.PutHTTPServerCert Curl Request
- Shelly.PutHTTPServerCert Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCert?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCert","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCert '{"data":null}'
Response
- Shelly.PutHTTPServerCert HTTP GET Response
- Shelly.PutHTTPServerCert Curl Response
- Shelly.PutHTTPServerCert Mos Response
{
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"restart_required": true
}
}
{
"restart_required": true
}
Removing the CA certificate (disable client authentication)
Using null:
- Shelly.PutHTTPServerCABundle HTTP GET Request
- Shelly.PutHTTPServerCABundle Curl Request
- Shelly.PutHTTPServerCABundle Mos Request
http://192.168.33.1/rpc/Shelly.PutHTTPServerCABundle?data=null
curl -X POST -d '{"id":1,"method":"Shelly.PutHTTPServerCABundle","params":{"data":null}}' http://${SHELLY}/rpc
mos --port ${PORT} call Shelly.PutHTTPServerCABundle '{"data":null}'
Response
- Shelly.PutHTTPServerCABundle HTTP GET Response
- Shelly.PutHTTPServerCABundle Curl Response
- Shelly.PutHTTPServerCABundle Mos Response
{
"restart_required": true
}
{
"id": 1,
"src": "shellydev1-xxxxxxxxxxxx",
"params": {
"restart_required": true
}
}
{
"restart_required": true
}