Hello, All. This is my first post.
The Setup
I’m in the process of setting up my first server hosted on my home network. My goal is to have a Nextcloud server that my family and I can access from anywhere using a normal web URL. I want to host the server on my local network for all the usual reasons; however, I don’t want to give out my network’s public IP. To avoid putting my public IP out directly, I’ve set up a VPS with a WireGuard connection between the VPS and my Nextcloud server. I’ve set the DNS record to point to the IP of the VPS instance and configured the VPS to forward everything on ports 80 and 443 over the WG tunnel to the Nextcloud server. The Nextcloud server is running Fedora 44 Server Edition.
The Problem
I can access the server just fine over port 80 when I change my Apache config to not redirect to https or when I access it directly via local IP on port 80. But, when accessing over port 443, it gives ERR_SSL_PROTOCOL_ERROR in Chrome and SSL_ERROR_RX_RECORD_TOO_LONG in Firefox; if I access it via local IP on 443, it serves up the default Apache welcome page with the default unsigned cert.
What I’ve Tried
I’ve tried revoking and re-issuing certs (I’m using acme.sh and letsencrypt), changing my Apache config, and double-checking my firewall settings. By all accounts, the connections seem to be going through fine on port 80 and 443 both. Something seems to be up with how Apache is handling the cert or SSL connection, but I can’t figure out where it’s going wrong. Of note, I originally had my domain proxied by Cloudflare and I tried issuing a cert using the Cloudflare API; issuing the cert seemed successful, but Cloudflare never showed that the connection between their proxy servers and my server was encrypted – only the connection between clients and Cloudflare was encrypted.
Anyone have an idea where the problem is? I’m out of ideas.
The Configs
WireGuard connection(s)
Cloud VPS:
[Interface]
Address = 10.168.24.1/32
ListenPort = 24837
PrivateKey = <Private key>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;iptables -A FORWARD -o %i -j ACCEPT; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;ip6tables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.168.24.2:80; iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.168.24.2:80; iptables -t nat -A POSTROUTING -o wg0 -p tcp -d 10.168.24.2 --dport 80 -j SNAT --to-source 10.168.24.1; iptables -t nat -A POSTROUTING -o wg0 -p tcp -d 10.168.24.2 --dport 443 -j SNAT --to-source 10.168.24.1; iptables -A FORWARD -i eth0 -o wg0 -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT; iptables -A FORWARD -i eth0 -o wg0 -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT; iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; iptables -A FORWARD -i wg0 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;iptables -D FORWARD -o %i -j ACCEPT; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;ip6tables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.168.24.2:80; iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.168.24.2:80; iptables -t nat -D POSTROUTING -o wg0 -p tcp -d 10.168.24.2 --dport 80 -j SNAT --to-source 10.168.24.1; iptables -t nat -D POSTROUTING -o wg0 -p tcp -d 10.168.24.2 --dport 443 -j SNAT --to-source 10.168.24.1; iptables -D FORWARD -i eth0 -o wg0 -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT; iptables -D FORWARD -i eth0 -o wg0 -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT; iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; iptables -D FORWARD -i wg0 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
[Peer]
PublicKey = <Public key>
AllowedIPs = 10.168.24.2/32
Nextcloud server:
[Interface]
Address = 10.168.24.2/32
ListenPort = 24837
PrivateKey = <Private key>
[Peer]
Endpoint = <IP here>:24837
PublicKey = <Public key>
AllowedIPs = 10.168.24.2/32
PersistentKeepalive = 25
Apache config
<VirtualHost *:80>
ServerName nextcloud.example.com
Redirect permanent / https://nextcloud.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName nextcloud.example.com
SSLEngine on
DocumentRoot /var/www/nextcloud
# SSL Configuration
SSLCertificateFile "/etc/ssl/nextcloud.example.com/nextcloud.example.com.cer"
SSLCertificateKeyFile "/etc/ssl/nextcloud.example.com/nextcloud.example.com.key"
SSLCertificateChainFile "/etc/ssl/nextcloud.example.com/ca.crt"
SSLCACertificatePath "/etc/letsencrypt/nextcloud.example.com/"
SSLCACertificateFile "/etc/letsencrypt/nextcloud.example.com/fullchain.cer"
<Directory /var/www/nextcloud>
Require all granted
AllowOverride All
Options FollowSymLinks MultiViews
<IfModule mod_dav.c>
Dav off
</IfModule>
</Directory>
Protocols h2 http/1.1
</VirtualHost>
LoadModule ssl_module "/etc/httpd/modules/mod_ssl.so"
LoadModule rewrite_module "/etc/httpd/modules/mod_rewrite.so"
LoadModule headers_module "/etc/httpd/modules/mod_headers.so"
LoadModule env_module "/etc/httpd/modules/mod_env.so"
LoadModule dir_module "/etc/httpd/modules/mod_dir.so"
LoadModule mime_module "/etc/httpd/modules/mod_mime.so"
Cert issuing/config
nextcloud:~$ acme.sh --issue -k 4096 -d nextcloud.example.com -w /var/www/nextcloud
[Wed Aug 5 04:47:07 PM CDT 2026] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Wed Aug 5 04:47:07 PM CDT 2026] Creating domain key
[Wed Aug 5 04:47:08 PM CDT 2026] The domain key is here: /etc/letsencrypt/nextcloud.example.com/nextcloud.example.com.key
[Wed Aug 5 04:47:08 PM CDT 2026] Single domain='nextcloud.example.com'
[Wed Aug 5 04:47:09 PM CDT 2026] Getting webroot for domain='nextcloud.example.com'
[Wed Aug 5 04:47:09 PM CDT 2026] nextcloud.example.com is already verified, skipping http-01.
[Wed Aug 5 04:47:09 PM CDT 2026] Verification finished, beginning signing.
[Wed Aug 5 04:47:09 PM CDT 2026] Let's finalize the order.
[Wed Aug 5 04:47:09 PM CDT 2026] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/'
[Wed Aug 5 04:47:11 PM CDT 2026] Downloading cert.
[Wed Aug 5 04:47:11 PM CDT 2026] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/'
[Wed Aug 5 04:47:11 PM CDT 2026] Cert success.
-----BEGIN CERTIFICATE----- [0/245]
<Cert here>
-----END CERTIFICATE-----
[Wed Aug 5 04:47:11 PM CDT 2026] Your cert is in: /etc/letsencrypt/nextcloud.example.com/nextcloud.example.com.cer
[Wed Aug 5 04:47:11 PM CDT 2026] Your cert key is in: /etc/letsencrypt/nextcloud.example.com/nextcloud.example.com.key
[Wed Aug 5 04:47:11 PM CDT 2026] The intermediate CA cert is in: /etc/letsencrypt/nextcloud.example.com/ca.cer
[Wed Aug 5 04:47:11 PM CDT 2026] And the full-chain cert is in: /etc/letsencrypt/nextcloud.example.com/fullchain.cer
[Wed Aug 5 04:47:12 PM CDT 2026] ARI suggestedWindow: 2026-10-03T23:32:44Z to 2026-10-05T18:43:34Z
[Wed Aug 5 04:47:12 PM CDT 2026] Next renewal time picked from ARI window: 2026-10-03T23:55:46Z
nextcloud:~$ acme.sh --install-cert -d nextcloud.example.com --cert-file /etc/ssl/nextcloud.example.com/nextcloud.example.com.cer --key-file /etc/ssl/nextcloud.example.com/nextcloud.example.com.key --fullchain-file /etc/ssl/nextcloud.example.com/ca.crt