The subdomain existed. The DNS record was there. The certificate was generated. And yet: SSL error, connection refused, nothing worked.
This is the story of getting cb.mydomain.com working on a Synology NAS—a journey through certificate assignment, dynamic DNS scripts, and the reverse proxy configuration that Synology’s documentation barely mentions.
The Problem
I had a working setup: files.mydomain.com pointed to my Synology NAS with a valid Let’s Encrypt certificate. Everything worked. Then I tried to add a second subdomain for a different service.
Created the DNS record in Cloudflare. Generated a new certificate in Synology’s Control Panel. Set up the reverse proxy entry. Tested the URL.
SSL error. Invalid certificate.
The browser showed the certificate was for files.mydomain.com, not the new subdomain. Synology was serving the wrong certificate.
Synology’s Certificate Architecture
Here’s what the documentation doesn’t clearly explain: Synology stores certificates in /usr/syno/etc/certificate/_archive/, and each certificate gets assigned to specific services. Having a certificate doesn’t mean it’s being used.
First, I checked what certificates actually existed:
sudo find /usr/syno/etc/certificate/_archive/ -name "cert.pem" -exec openssl x509 -in {} -noout -subject -dates \;
Both certificates were there. Valid dates. Correct subjects. The certificates weren’t the problem—the assignment was.
The Assignment Mystery
Synology’s Control Panel > Security > Certificate shows your certificates, but there’s a “Configure” button that’s easy to miss. That’s where you assign certificates to services.
The issue: my new subdomain’s reverse proxy entry was using the default certificate, not the one I’d generated for it.
# Check which certificate is assigned to what
sudo grep -r "cb.mydomain.com" /usr/syno/etc/certificate/_archive/
The certificate existed in the archive but wasn’t linked to the reverse proxy service. Synology had generated it, stored it, and then… done nothing with it.
The Fix: Explicit Assignment
In Control Panel > Security > Certificate:
- Click “Configure” (not “Add” or “Edit”—Configure)
- Find the reverse proxy entry for your subdomain
- Change the certificate from “synology.com” (the default) to your subdomain’s certificate
- Apply
That’s it. The SSL error disappeared immediately.
But Wait, There’s More: Dynamic DNS
The subdomain worked locally. From outside the network? Nothing. DNS wasn’t resolving to my current IP.
My ISP doesn’t provide static IPs. The address changes every few days. Cloudflare’s dynamic DNS needed updating, and Synology’s built-in DDNS doesn’t support custom Cloudflare configurations well.
I wrote a script:
#!/bin/bash
# Cloudflare API settings
CF_API_TOKEN="your-api-token"
CF_ZONE_ID="your-zone-id"
# DNS Records to update
declare -A RECORDS
RECORDS["files.mydomain.com"]="record-id-1"
RECORDS["cb.mydomain.com"]="record-id-2"
# Get current public IP
CURRENT_IP=$(curl -s https://api.ipify.org)
update_record() {
local name=$1
local record_id=$2
curl -s -X PUT \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${record_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{
\"type\": \"A\",
\"name\": \"${name}\",
\"content\": \"${CURRENT_IP}\",
\"proxied\": true,
\"ttl\": 1
}"
}
for name in "${!RECORDS[@]}"; do
echo "Updating ${name} to ${CURRENT_IP}"
update_record "$name" "${RECORDS[$name]}"
done
Saved to /usr/local/bin/update_dns.sh, made executable, and scheduled via Synology’s Task Scheduler to run every 15 minutes.
Getting the Cloudflare Record IDs
The script needs record IDs, which aren’t visible in Cloudflare’s dashboard. You have to query the API:
curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/dns_records" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" | jq '.result[] | {name: .name, id: .id}'
This dumps all your DNS records with their IDs. Copy the ones you need into the script.
The Reverse Proxy Configuration
Synology’s reverse proxy is under Control Panel > Login Portal > Advanced > Reverse Proxy. Not Application Portal. Not Network. Login Portal, Advanced tab. Because of course it is.
For each subdomain:
-
Source:
- Protocol: HTTPS
- Hostname:
cb.mydomain.com - Port: 443
-
Destination:
- Protocol: HTTP (usually—most internal services don’t need HTTPS)
- Hostname:
localhostor internal IP - Port: whatever your service uses
-
Custom Headers (often necessary):
X-Forwarded-For:$remote_addrX-Forwarded-Proto:$schemeUpgrade:$http_upgrade(for WebSocket support)Connection:$connection_upgrade(for WebSocket support)
The WebSocket headers matter for services like code-server, Jupyter, or anything else that maintains persistent connections.
Testing the Full Chain
After configuration, verify each layer:
DNS Resolution:
nslookup cb.mydomain.com
# Should return your public IP (or Cloudflare's proxy IP if proxied)
Certificate Validity:
echo | openssl s_client -servername cb.mydomain.com -connect cb.mydomain.com:443 2>/dev/null | openssl x509 -noout -subject -dates
HTTP Response:
curl -I https://cb.mydomain.com
# Should return 200 or redirect, not SSL errors
From Inside the Network: This is where NAT hairpinning becomes relevant. If you can’t access your own subdomains from inside your network, your router isn’t reflecting the traffic back properly. Either enable NAT reflection or add local DNS overrides.
The Lessons
Synology certificates must be explicitly assigned. Creating a certificate doesn’t automatically use it. The “Configure” button in the Certificate panel is essential.
Dynamic DNS needs scripting. Synology’s built-in DDNS is limited. A simple bash script with Cloudflare’s API is more reliable and handles multiple subdomains.
The reverse proxy is hidden. Login Portal > Advanced > Reverse Proxy. Memorize it, because you’ll forget.
WebSocket headers are often required. Modern web apps love WebSockets. Add the Upgrade and Connection headers preemptively.
Test from outside your network. Internal testing misses NAT and DNS propagation issues. Use your phone’s cellular data or a VPN to test the public path.
The Result
Two subdomains, both with valid SSL certificates, both updating DNS automatically, both proxying to internal services correctly. What should have been a 20-minute configuration became a 3-hour education in Synology’s architecture.
Now when I add a new subdomain, it takes 5 minutes:
- Add DNS record in Cloudflare, note the record ID
- Add record ID to the update script
- Generate certificate in Synology
- Assign certificate to the new reverse proxy entry
- Create the reverse proxy rule
The system works. It just doesn’t explain itself very well.
The “Configure” button for certificate assignment has no tooltip, no explanation, and no indication that it’s the key to making SSL work. Synology’s UX remains… an adventure.