Fixing Ghost SSL Certificate Issues on YunoHost: A Complete Guide

Date

Date

Date

October 14, 2025

October 14, 2025

October 14, 2025

Author

Author

Author

Lisa Zhao

Lisa Zhao

Lisa Zhao

If you've just installed Ghost on YunoHost and are facing SSL certificate errors, HSTS issues or 502 Bad Gateway messages, you're not alone. This is a common issue that stems from how YunoHost handles SSL certificates and Single Sign-On (SSO) protection. Here's how to fix it properly.

Understanding the Problem

What's Actually Happening?

When you install Ghost on YunoHost, several things need to work together:

  1. Ghost runs as a Node.js application on a local port (typically 26561)

  2. Nginx acts as a reverse proxy, routing web traffic to Ghost

  3. SSOwat (YunoHost's SSO system) protects your apps with authentication

  4. SSL certificates encrypt the connection between visitors and your server

The problem arises because:

  • Port forwarding issues: YunoHost's default Let's Encrypt method uses HTTP-01 challenge, which requires port 80 to be accessible from the internet. Many home networks or ISPs block this, causing certificate installation to fail.

  • SSO interference: YunoHost's SSOwat redirects all traffic to the admin login page by default, preventing Ghost from being accessible.

  • Certificate caching: Even after fixing the certificate, browsers and nginx might serve cached, self-signed certificates.

Why You See These Errors

502 Bad Gateway: This means nginx can't reach Ghost, usually because SSOwat is blocking access or Ghost crashed during startup.

ERR_CERT_AUTHORITY_INVALID: Your browser sees YunoHost's self-signed certificate instead of a trusted Let's Encrypt certificate.

The Solution: DNS Validation

Instead of relying on HTTP-01 validation (which requires open ports), we'll use DNS-01 validation. This method proves domain ownership by adding a TXT record to your DNS, completely bypassing port forwarding issues.

Why This Works Better

  • No port forwarding required: Works even if port 80/443 are blocked

  • More reliable: DNS validation rarely fails

  • Same certificate quality: You get the same trusted Let's Encrypt certificates

  • Works for subdomains: Perfect for apps installed on subdomains like ghost.yourdomain.com

Step-by-Step Solution

Prerequisites

Before starting, make sure you have:

  • SSH access to your YunoHost server

  • Access to your domain's DNS settings

  • Ghost installed on YunoHost (even if it's not working yet)

Step 1: Request Certificate via DNS Challenge

SSH into your server and run:

sudo certbot certonly --manual --preferred-challenges dns -d

Replace ghost.yourdomain.com with your actual Ghost subdomain.

What happens: Certbot will generate a unique validation string and ask you to add it as a DNS TXT record.

Step 2: Add DNS TXT Record

You'll see instructions like:


Log into your DNS provider (Cloudflare, Namecheap, etc.) and add:

  • Type: TXT

  • Name: _acme-challenge.ghost (or _acme-challenge.ghost.yourdomain.com depending on your provider)

  • Value: The string certbot provided

  • TTL: 300 (5 minutes) or Auto

Important: Don't press Enter in certbot yet!

Step 3: Verify DNS Propagation

DNS changes take time to spread across the internet. Verify your record is live:

Look for your validation string in the output. If you don't see it, wait 2-5 minutes and try again.

Why this matters: If you continue before DNS propagates, certbot will fail validation and you'll have to start over.

Step 4: Complete Certificate Issuance

Once you see your TXT record, go back to the certbot terminal and press Enter. Certbot will:

  1. Verify the DNS record

  2. Request the certificate from Let's Encrypt

  3. Save the certificate to /etc/letsencrypt/live/ghost.yourdomain.com/

You should see: "Successfully received certificate"

Step 5: Install Certificate in YunoHost

YunoHost stores certificates in its own directory structure. Copy your new Let's Encrypt certificate there:

sudo cp /etc/letsencrypt/live/ghost.yourdomain.com/fullchain.pem /etc/yunohost/certs/ghost.yourdomain.com/crt.pem
sudo cp /etc/letsencrypt/live/ghost.yourdomain.com/privkey.pem /etc/yunohost/certs/ghost.yourdomain.com/key.pem
sudo chown root:ssl-cert /etc/yunohost/certs/ghost.yourdomain.com/*.pem
sudo chmod 640

What this does:

  • Copies the full certificate chain (needed for browsers to trust it)

  • Copies the private key

  • Sets correct ownership (nginx needs to read these)

  • Sets secure permissions (only root and ssl-cert group can access)

Step 6: Configure SSOwat to Allow Public Access

Ghost is a public blog platform, so it shouldn't be behind YunoHost's login page:

yunohost app setting ghost unprotected_uris -v

What this does:

  • Marks Ghost's root path (/) as "unprotected"

  • Regenerates SSOwat configuration to apply changes

  • Allows visitors to access Ghost without YunoHost login

Note: The Ghost admin panel (ghost.yourdomain.com/ghost) still has its own secure login.

Step 7: Force Nginx to Use New Certificate

Sometimes nginx caches SSL sessions. Force a complete reload:

sudo systemctl stop nginx
sudo systemctl start

Why stop/start instead of reload: This ensures nginx completely releases the old certificate from memory.

Step 8: Verify and Access

Check that the new certificate is being served:

openssl s_client -connect ghost.yourdomain.com:443 -servername ghost.yourdomain.com </dev/null 2>/dev/null | openssl x509 -noout -issuer

You should see: issuer=C = US, O = Let's Encrypt, CN = E7

If you still see issuer=CN = yunohost.org, nginx hasn't fully reloaded. Try:

sudo systemctl restart

Step 9: Clear Browser Cache

Your browser might have cached the old certificate:

  1. Clear cache: Press Ctrl+Shift+Delete, select "Cached images and files"

  2. Or use incognito mode: Open a private/incognito window

  3. Or use mobile data: Access from your phone (not on WiFi)

Visit https://ghost.yourdomain.com - you should see Ghost with a valid SSL certificate! 🎉

Troubleshooting Common Issues

Ghost Shows 502 Bad Gateway

Check if Ghost is actually running:

sudo

If it's stopped or failed, restart it:

sudo systemctl restart

View recent logs to see what went wrong:

sudo journalctl -u ghost -n 50 --no-pager

Still Getting Certificate Warnings

Verify what certificate is actually being served:

openssl s_client -connect ghost.yourdomain.com:443 -servername ghost.yourdomain.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates -subject

If it shows "yunohost.org" instead of "Let's Encrypt":

  1. Double-check you copied the certificates correctly

  2. Restart nginx: sudo systemctl restart nginx

  3. Clear your browser's SSL cache completely

DNS Record Not Propagating

Some DNS providers are slower than others. If dig doesn't show your TXT record after 10 minutes:

  • Check you added it to the correct domain

  • Verify the record name format (some providers want just _acme-challenge.ghost, others want the full _acme-challenge.ghost.yourdomain.com)

  • Try using your provider's DNS checker tool

Certificate Expires in 90 Days

Let's Encrypt certificates are valid for 90 days. To renew:

sudo certbot renew --manual --preferred-challenges dns -d

You'll need to update the DNS TXT record with a new value each time. Consider setting a calendar reminder for 60 days from now.

For automatic renewal, you'd need to use your DNS provider's API with certbot plugins (more advanced setup).

Understanding the Technical Details

Why DNS Validation Works

HTTP-01 Challenge (YunoHost's default):

  • Let's Encrypt connects to http://yourdomain.com/.well-known/acme-challenge/

  • Requires port 80 accessible from internet

  • Fails if firewall/ISP blocks port 80

  • Fails if port forwarding misconfigured

DNS-01 Challenge (our solution):

  • Let's Encrypt queries DNS for TXT record

  • No ports needed

  • Works from anywhere

  • Only requires DNS control

How SSOwat Works

SSOwat is YunoHost's Single Sign-On system that:

  • Intercepts all HTTP requests via Lua scripts in nginx

  • Checks if the user is authenticated

  • Redirects to login page if not

  • Allows access if the URI is marked "unprotected"

For Ghost, we need the entire site unprotected because:

  • Blog content should be publicly accessible

  • Ghost has its own admin authentication

  • Readers shouldn't need a YunoHost account

The nginx Proxy Chain

When someone visits your Ghost blog:

  1. Browser connects to nginx (port 443)

  2. nginx terminates SSL using the certificate

  3. SSOwat Lua script checks authentication

  4. Request passes to Ghost (port 26561)

  5. Ghost generates the page

  6. Response travels back through nginx to browser

If any step fails, you get errors.

Applying This to Other YunoHost Apps

This same approach works for any YunoHost app with certificate issues:

For n8n:

sudo certbot certonly --manual --preferred-challenges dns -d n8n.yourdomain.com
# Then follow the same copy steps, adjusting paths

For Nextcloud:

sudo certbot certonly --manual --preferred-challenges dns -d cloud.yourdomain.com
# Same process

The key steps remain the same:

  1. Request certificate with DNS validation

  2. Copy to YunoHost cert directory

  3. Configure app visibility (unprotected if needed)

  4. Reload nginx

Best Practices

Security Considerations

  • Keep certificates renewed: Set calendar reminders

  • Secure your DNS account: Use 2FA on your DNS provider

  • Monitor expiration: Check certificate status monthly

  • Backup your config: Save your nginx and YunoHost configs

When to Use This Method

Use DNS validation when:

  • Port forwarding is complex or unavailable

  • Behind CGNAT (Carrier-Grade NAT)

  • ISP blocks ports 80/443

  • Using IPv6 without proper forwarding

  • Want more reliable certificate issuance

Use HTTP validation when:

  • Ports 80/443 are reliably forwarded

  • Want automatic renewal (with certbot renewal hooks)

  • Have simple network setup

Conclusion

SSL certificate issues on YunoHost often stem from network configuration challenges that HTTP-01 validation can't overcome. By using DNS-01 validation, you bypass these limitations entirely while getting the same trusted Let's Encrypt certificates.

The key takeaways:

  • DNS validation works without open ports

  • SSOwat needs configuration for public apps

  • Nginx caching can cause confusion

  • The same method works for any YunoHost app

Once you've done this process once, it becomes straightforward for future apps. Keep this guide handy for when you install your next YunoHost application!

Additional Resources

Have questions or run into issues not covered here? The YunoHost forum and Ghost community are great places to get help. Include your error messages and what you've already tried for the best assistance.

Related posts

November 11, 2025

How to Automatically Share Your Ghost Blog Posts on LinkedIn, Twitter, and Facebook with N8N

Description

November 11, 2025

How to Automatically Share Your Ghost Blog Posts on LinkedIn, Twitter, and Facebook with N8N

Description

November 11, 2025

How to Automatically Share Your Ghost Blog Posts on LinkedIn, Twitter, and Facebook with N8N

Description

November 10, 2025

Compassionate Systems: Clear is Kind , Unclear is Unkind

Description

November 10, 2025

Compassionate Systems: Clear is Kind , Unclear is Unkind

Description

November 10, 2025

Compassionate Systems: Clear is Kind , Unclear is Unkind

Description

Got questions?

I’m always excited to collaborate on innovative and exciting projects!

Got questions?

I’m always excited to collaborate on innovative and exciting projects!

Got questions?

I’m always excited to collaborate on innovative and exciting projects!

Lisa Zhao, 2025

XX

Lisa Zhao, 2025

XX

Lisa Zhao, 2025

XX