Restricting access to a self-hosted Nextcloud instance to a WireGuard VPN is a critical security measure in environments where sensitive data is stored or shared. By default, exposing Nextcloud directly to the public internet significantly increases the attack surface, making it susceptible to credential stuffing, zero-day exploits, automated scanning, and abuse of application-level vulnerabilities.
Using WireGuard as the primary access boundary ensures that only authenticated and explicitly authorized clients can reach the service at the network level, before any application authentication occurs. This model aligns with zero-trust and defense-in-depth principles by eliminating unauthenticated public access entirely rather than relying solely on application security controls.
In this deployment, Cloudflare remains in use for DNS management and TLS termination, but it must not be treated as an access control mechanism. The challenge is enforcing VPN-only reachability while Cloudflare proxying obscures client IP addresses and VPN traffic may loop back through the public interface. This document describes a practical, production-ready approach to solving that problem and ensuring Nextcloud is accessible exclusively to WireGuard VPN users.
Architecture #
The setup consists of multiple components working together. Nextcloud runs inside Docker, while Nginx is deployed directly on the host as a reverse proxy in front of the container. WireGuard provides VPN access and is managed using wg-easy, which also runs in Docker. DNS and HTTPS proxying are handled by Cloudflare, with the domain configured as proxied.
This architecture allows the service to remain private to VPN users while still benefiting from Cloudflare features such as certificate management, DNS abstraction, and edge-layer protection.
Problem Statement #
Cloudflare Masks Client IPs #
When Cloudflare proxying is enabled, all incoming connections to Nginx appear to originate from Cloudflare IP addresses. As a result, Nginx does not see the real client IP by default. Traditional IP-based access control using allow and deny directives therefore become ineffective, since every request appears to come from Cloudflare rather than the actual client.
VPN and Cloudflare Loopback Behavior #
When a user connected to the WireGuard VPN accesses https://nextcloud.domain, the request exits the VPN tunnel, reaches Cloudflare, and is then routed back to the same server. In this scenario, Nginx sees the serverโs own public IP address as the client IP instead of the WireGuard address. Without explicitly accounting for this behavior, legitimate VPN traffic would be denied.
Solution Overview #
The solution consists of three coordinated steps:
- Configure Nginx to trust Cloudflare and restore the real client IP.
- Enforce strict access control rules in the Nextcloud virtual host.
- Validate and apply the configuration changes safely.
1. Configure Nginx to Trust Cloudflare (Restore Real Client IP) #
To make IP-based access control reliable, Nginx must be configured to trust Cloudflare and use the CF-Connecting-IP header as the authoritative source of the client IP. This is achieved by explicitly allowing Cloudflareโs published IP ranges and enabling Nginxโs real_ip module.
A dedicated configuration file was created at:
/etc/nginx/conf.d/cloudflare.conf
This file contains Cloudflareโs IPv4 and IPv6 address ranges and instructs Nginx to treat CF-Connecting-IP as the real client IP header. The following command was used to generate the file automatically:
echo "# Cloudflare Trusted IPs" | sudo tee /etc/nginx/conf.d/cloudflare.conf && \
curl -s https://www.cloudflare.com/ips-v4 | sed 's/^/set_real_ip_from /; s/$/;/' | sudo tee -a /etc/nginx/conf.d/cloudflare.conf && \
curl -s https://www.cloudflare.com/ips-v6 | sed 's/^/set_real_ip_from /; s/$/;/' | sudo tee -a /etc/nginx/conf.d/cloudflare.conf && \
echo "real_ip_header CF-Connecting-IP;" | sudo tee -a /etc/nginx/conf.d/cloudflare.conf
With this configuration in place, Nginx correctly restores the real client IP for all requests forwarded by Cloudflare.
2. Enforce Access Control in the Nextcloud Virtual Host #
Once Nginx can reliably determine the real client IP, strict access control rules can be applied at the virtual host level.
The Nextcloud site configuration was edited at:
/etc/nginx/sites-available/nextcloud-axsu.xc1.app
Inside the server {} block, the following rules were added:
# WireGuard subnet (direct VPN access)
allow 10.42.44.0/24;
# Localhost
allow 127.0.0.1;
# Server public IP (critical for VPN โ Cloudflare โ server loopback)
allow 139.84.141.111;
# Block all other traffic
deny all;
The WireGuard subnet allows direct VPN access. Localhost access is permitted for internal services and health checks. The serverโs own public IP is explicitly allowed to accommodate VPN traffic that exits through Cloudflare and returns to the same host. All other traffic is denied, ensuring the service is not publicly reachable.
3. Apply and Validate Changes #
After updating the configuration, Nginx was tested and reloaded to safely apply the changes:
sudo nginx -t
sudo systemctl reload nginx
This validation step ensures that configuration errors do not disrupt the running service.
Maintenance Considerations #
If temporary or additional non-VPN access is required, an additional allow x.x.x.x; directive can be added to the virtual host configuration, followed by an Nginx reload. This is useful for controlled maintenance or access from trusted static IPs.
Cloudflare IP ranges rarely change, but if access issues arise after extended periods of uptime, the Cloudflare IP fetch command can be rerun to refresh the trusted address ranges.
This configuration ensures that Nextcloud is accessible only to authenticated WireGuard VPN clients while preserving the operational and security benefits provided by Cloudflare. Real client IPs are correctly restored, VPN loopback behavior is explicitly handled, and all unintended public access paths are effectively blocked. The result is a secure, maintainable, and production-ready deployment suitable for environments with strict access control requirements.


































