QZ Tray shows "untrusted" NOT because of your website's SSL certificate (Let's Encrypt), but because of the application signing certificate used to authenticate your POS app with QZ Tray.
- Website SSL/TLS (Let's Encrypt) ✅ - For HTTPS connections
- QZ Tray App Signing (Currently self-signed)
⚠️ - For QZ Tray authentication
Your app uses a self-signed RSA key pair:
server/private-key.pem- Private key (server signs requests)server/public-key.pem- Public key (QZ Tray verifies signatures)- Copied to
src/assets/digital-certificate.txt(loaded by frontend)
This works but shows a warning because QZ Tray doesn't recognize self-signed certificates by default.
Best for: Internal use, development, or small businesses where you control all devices.
Status: Already applied in qz-tray.service.ts
How it works:
- Sets signature algorithm to SHA512
- QZ Tray will still show a warning but will allow the connection
- Users may need to click "Allow" once per device
Manual Override (if needed): Users can create an override file to permanently trust your certificate:
On Windows:
C:\Users\<Username>\.qz\override.crt
On Mac:
/Users/<Username>/.qz/override.crt
On Linux:
/home/<username>/.qz/override.crt
Content: Copy your entire public-key.pem file into override.crt
Best for: Open source projects or public-facing applications.
Steps:
- Go to: https://qz.io/developers/
- Sign up for a free community certificate
- Receive certificate + private key from QZ Industries
- Replace your current keys with QZ's certificate
- Update
digital-certificate.txtwith the new public key
Pros:
- ✅ Trusted by QZ Tray automatically
- ✅ Free for open source
- ✅ No user warnings
Cons:
- ❌ Requires registration
- ❌ May have usage limits for commercial use
Best for: Commercial applications with many users.
Steps:
-
Purchase a code signing certificate from:
- DigiCert (https://www.digicert.com/signing/code-signing-certificates)
- Sectigo (https://sectigo.com/ssl-certificates-tls/code-signing)
- GlobalSign
- Other trusted CA
-
Generate certificate signing request (CSR)
-
Receive signed certificate
-
Convert to PEM format if needed
-
Update your keys
Cost: $200-$500/year
Pros:
- ✅ Fully trusted
- ✅ Professional
- ✅ No warnings ever
- ✅ Best for commercial products
Cons:
- ❌ Expensive
- ❌ Annual renewal
Best for: Small deployments where you can guide users.
Status: This is what you have now.
User Instructions:
When QZ Tray shows "untrusted certificate":
-
First time setup:
- Click "Advanced" or "Show Details"
- Click "Allow this website"
- Check "Remember this decision"
-
Permanent trust (recommended for your business):
- Create override file at
C:\Users\<Username>\.qz\override.crt - Copy the public key from
server/public-key.peminto this file - Restart QZ Tray
- No more warnings!
- Create override file at
Pros:
- ✅ Free
- ✅ Full control
- ✅ Works immediately with override file
Cons:
- ❌ Shows warning to users initially
- ❌ Requires manual trust configuration
For your current situation (local business with Let's Encrypt SSL):
- ✅ Keep current setup with the override fix I just applied
- Create override.crt files on all your POS terminals:
# Run this on each Windows terminal $overridePath = "$env:USERPROFILE\.qz" New-Item -ItemType Directory -Force -Path $overridePath Copy-Item "server\public-key.pem" "$overridePath\override.crt"
- Restart QZ Tray on each terminal
- ✅ Done! No more warnings
For future (if scaling to multiple locations):
- Consider QZ Tray's free community certificate
- Or purchase a code signing certificate if budget allows
After applying the fix:
-
Open your POS app in the browser
-
Try to print a receipt
-
Check QZ Tray icon in system tray:
- Green = Connected and trusted ✅
- Yellow/Orange = Connected but warning
⚠️ - Red = Not connected ❌
-
Console logs (F12 → Console):
QZ Tray certificate and signature configured QZ Tray connected Signature received from backend
- Create
override.crtfile (see above) - Restart QZ Tray application
- Clear browser cache
- Check
server/private-key.pemexists - Verify
/api/signendpoint is working:POST https://your-domain.com/api/sign Body: { "toSign": "test" } Should return: { "signature": "..." }
- Ensure QZ Tray is running (check system tray)
- Check firewall allows localhost:8182 and 8181
- Try: https://localhost:8182/
| File | Purpose | Location |
|---|---|---|
private-key.pem |
Signs requests (server) | server/private-key.pem |
public-key.pem |
Verifies signatures (QZ) | server/public-key.pem |
digital-certificate.txt |
Public key copy (frontend) | src/assets/digital-certificate.txt |
override.crt |
User trust override | ~/.qz/override.crt |
✅ QZ Tray service updated with SHA512 signature algorithm
✅ Self-signed certificate configured and working
Your Let's Encrypt certificate is not the issue - it's working perfectly for HTTPS. The QZ Tray certificate is separate and only affects printer communication.