# Two-VPS Private Proxy Architecture: Nginx Reverse Proxy Over Wireguard VPN

### Overview <a href="#id-1-overview" id="id-1-overview"></a>

* **Proxy VPS:** Public VPS running Nginx reverse proxy with its own public IP.
* **Backend VPS:** Origin VPS running your service (e.g., Gitea, Forgejo), completely private.
* **Wireguard VPN:** Encrypted private tunnel connecting Proxy VPS and Backend VPS.
* **Traffic flow:** Client → Proxy VPS → Wireguard VPN → Backend VPS (service).

***

### Prerequisites <a href="#id-2-prerequisites" id="id-2-prerequisites"></a>

* Two Linux VPS servers (Ubuntu recommended).
* A domain or subdomain for your service.
* Basic Linux command line knowledge.

***

### 1. Generate Wireguard Keys on Both VPSes <a href="#id-3-generate-wireguard-keys-on-both-vpses" id="id-3-generate-wireguard-keys-on-both-vpses"></a>

```bash
wg genkey | tee privatekey | wg pubkey > publickey
chmod 600 privatekey publickey
```

* `privatekey` holds your private key.
* `publickey` holds your public key.

***

### 2. Configure Wireguard on Proxy VPS <a href="#id-4-configure-wireguard-on-proxy-vps" id="id-4-configure-wireguard-on-proxy-vps"></a>

Create `/etc/wireguard/wg0.conf`:

```
[Interface]
PrivateKey = <Proxy VPS private key>
Address = 10.200.200.1/24
ListenPort = 51820

[Peer]
PublicKey = <Backend VPS public key>
AllowedIPs = 10.200.200.2/32
```

Enable and start:

```bash
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
```

***

### 3. Configure Wireguard on Backend VPS <a href="#id-5-configure-wireguard-on-backend-vps" id="id-5-configure-wireguard-on-backend-vps"></a>

Create `/etc/wireguard/wg0.conf`:

```
[Interface]
PrivateKey = <Backend VPS private key>
Address = 10.200.200.2/24

[Peer]
PublicKey = <Proxy VPS public key>
Endpoint = <Proxy VPS Public IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
```

Enable and start:

```bash
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
```

***

### 4. Ensure Backend App Port Is Not Publicly Exposed <a href="#id-6-ensure-backend-app-port-is-not-publicly-exposed" id="id-6-ensure-backend-app-port-is-not-publicly-exposed"></a>

#### 4.1 Bind the Service to the VPN IP

Configure your app (Gitea, forgejo, etc) to listen only on VPN IP `10.200.200.2`.

Example for Gitea (`app.ini`):

```
[server]
HTTP_ADDR = 10.200.200.2
HTTP_PORT = 3000
```

This restricts the app to accept connections only over the secure VPN.

#### 4.2 Firewall Configuration on Backend VPS

Block all incoming traffic except from Proxy VPS VPN IP on the service port (3000):

```bash
sudo ufw default deny incoming
sudo ufw allow from 10.200.200.1 to any port 3000 proto tcp
sudo ufw allow 22/tcp  # SSH access
sudo ufw enable
sudo ufw reload
```

Only the Proxy VPS can access your backend service port.

#### 4.3 Verify Port Binding and Traffic Restrictions

Check that service is listening correctly:

```bash
sudo ss -tuln | grep 3000
```

Expected output shows service bound to `10.200.200.2:3000` or `127.0.0.1:3000`, NOT all interfaces (`0.0.0.0`).

#### 4.4 Optional: NAT Port Forwarding on Proxy VPS

Alternatively or additionally, on Proxy VPS use iptables to forward incoming public port 443 traffic to backend VPN IP + port 3000:

```bash
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.200.200.2:3000
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
```

Add to Wireguard config `/etc/wireguard/wg0.conf` on Proxy VPS for automation:

```
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.200.200.2:3000
PostUp = iptables -t nat -A POSTROUTING -j MASQUERADE
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.200.200.2:3000
PostDown = iptables -t nat -D POSTROUTING -j MASQUERADE
```

***

### 5. Configure Nginx on Proxy VPS <a href="#id-7-configure-nginx-on-proxy-vps" id="id-7-configure-nginx-on-proxy-vps"></a>

Install Nginx and configure `/etc/nginx/sites-available/gitproxy`:

```
server {
    listen 80;
    server_name git.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name git.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://10.200.200.2:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
```

Enable site and reload Nginx:

```
sudo ln -s /etc/nginx/sites-available/gitproxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

***

### 6. Configure DNS and SSL <a href="#id-8-configure-dns-and-ssl" id="id-8-configure-dns-and-ssl"></a>

* Point your domain DNS A record to Proxy VPS public IP.
* Obtain SSL with Certbot on Proxy VPS:

```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d git.yourdomain.com
```

***

### 7. Test Your Setup <a href="#id-9-test-your-setup" id="id-9-test-your-setup"></a>

Visit:

```
https://git.yourdomain.com
```

Access your service securely via the Proxy VPS. Origin VPS IP remains hidden.

***

### Summary Table <a href="#summary-table" id="summary-table"></a>

<table><thead><tr><th width="210.99993896484375">Step</th><th>Purpose</th></tr></thead><tbody><tr><td>Wireguard key gen</td><td>Secure keys for VPN connection</td></tr><tr><td>Wireguard VPN config</td><td>Encrypted tunnel between VPSes</td></tr><tr><td>Bind app to VPN IP</td><td>Restrict app accessibility</td></tr><tr><td>Backend firewall rules</td><td>Allow service port only from Proxy VPN IP</td></tr><tr><td>Nginx reverse proxy</td><td>Proxy requests securely</td></tr><tr><td>DNS and SSL setup</td><td>Secure domain pointing and encryption</td></tr></tbody></table>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://martian1337.gitbook.io/notes/digital-privacy/self-hosting/two-vps-private-proxy-architecture-nginx-reverse-proxy-over-wireguard-vpn.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
