Synology Subdomains: SSL, Reverse Proxy, and Dynamic DNS

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:

  1. Click “Configure” (not “Add” or “Edit”—Configure)
  2. Find the reverse proxy entry for your subdomain
  3. Change the certificate from “synology.com” (the default) to your subdomain’s certificate
  4. 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:

  1. Source:

    • Protocol: HTTPS
    • Hostname: cb.mydomain.com
    • Port: 443
  2. Destination:

    • Protocol: HTTP (usually—most internal services don’t need HTTPS)
    • Hostname: localhost or internal IP
    • Port: whatever your service uses
  3. Custom Headers (often necessary):

    • X-Forwarded-For: $remote_addr
    • X-Forwarded-Proto: $scheme
    • Upgrade: $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:

  1. Add DNS record in Cloudflare, note the record ID
  2. Add record ID to the update script
  3. Generate certificate in Synology
  4. Assign certificate to the new reverse proxy entry
  5. 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.