GUI Hardening
The Syncthing web GUI is a powerful API surface. By default it binds to 127.0.0.1:8384 with no password. On servers, leaving this unsecured is a critical risk.
Lock down the GUI so it is accessible only through an SSH tunnel or a hardened reverse proxy, with a strong password and HTTPS enforced.
| Property | Default | Hardened Target |
|---|---|---|
| Bind address | 127.0.0.1:8384 | 127.0.0.1:8384 (keep localhost) |
| Authentication | None | Username + bcrypt password |
| HTTPS | Disabled | Enabled (self-signed) |
| CSRF protection | Enabled | Enabled — never disable |
| Remote access | Direct port | SSH tunnel or nginx reverse proxy |
Step 1 — Set a Username and Password
The safest method is through the GUI on first access. For scripted/headless servers, edit config.xml directly:
# Stop Syncthing before editing config
systemctl --user stop syncthing
# Generate a bcrypt hash of your password (Python 3)
python3 -c "import bcrypt; print(bcrypt.hashpw(b'yourStrongP@ss!', bcrypt.gensalt()).decode())"
# Output: $2b$12$...
# Edit config.xml
nano ~/.local/share/syncthing/config.xml
Find the <gui> block and add:
<gui enabled="true" tls="true">
<address>127.0.0.1:8384</address>
<user>admin</user>
<password>$2b$12$REPLACE_WITH_YOUR_BCRYPT_HASH</password>
<theme>default</theme>
</gui>
# Restart and verify
systemctl --user start syncthing
Always use a bcrypt hash, never plaintext. Syncthing will silently accept plaintext but it is stored readable in config.xml.
Step 2 — Enable HTTPS on the GUI
<gui enabled="true" tls="true">
<address>127.0.0.1:8384</address>
...
</gui>
Syncthing will generate a self-signed HTTPS certificate for the GUI. Your browser will show a certificate warning on first access — accept the exception once and it will not reappear.
Step 3 — Access via SSH Tunnel
On a remote VPS, never expose port 8384 to the internet:
# Forward remote port 8384 to local port 8384
ssh -L 8384:127.0.0.1:8384 user@your-vps-ip -N &
# Now open in browser
# https://localhost:8384
For persistent access, add this to your ~/.ssh/config:
Host myvps-syncthing
HostName your-vps-ip
User ubuntu
LocalForward 8384 127.0.0.1:8384
ServerAliveInterval 60
Then simply run ssh myvps-syncthing -N and browse to https://localhost:8384.
Step 4 — Nginx Reverse Proxy (Optional)
For teams needing browser-based access without per-user SSH tunnels:
server {
listen 443 ssl;
server_name sync.internal.example.com;
ssl_certificate /etc/letsencrypt/live/sync.internal.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sync.internal.example.com/privkey.pem;
# Only allow your office/VPN IP range
allow 10.0.0.0/8;
deny all;
location / {
proxy_pass http://127.0.0.1:8384;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Required for Syncthing's CSRF validation
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
sudo nginx -t && sudo systemctl reload nginx
Syncthing's CSRF protection checks the Origin or Referer header. Always proxy with the correct Host header or the GUI will reject requests with a 403 CSRF Error.
GUI Hardening Checklist
| Action | Status Check |
|---|---|
| Password set (bcrypt) | grep "<user>" ~/.local/share/syncthing/config.xml |
| TLS enabled on GUI | grep 'tls="true"' ~/.local/share/syncthing/config.xml |
| GUI bound to 127.0.0.1 | grep 127.0.0.1:8384 ~/.local/share/syncthing/config.xml |
| Port 8384 blocked externally | sudo ufw status — 8384 should NOT be in ALLOW list |
| Access via SSH tunnel or proxy | Test from external IP — should timeout or 403 |
Common Mistakes
| Mistake | Risk | Fix |
|---|---|---|
| No password on GUI | Anyone on LAN can control Syncthing | Set bcrypt password in <gui> block |
| Port 8384 open in firewall | Remote attackers can access GUI | sudo ufw deny 8384/tcp |
| Using HTTP (no TLS) | Token and password sent in plaintext | Set tls="true" in <gui> block |
| Disabling CSRF protection | XSS and CSRF attacks become possible | Never set insecureSkipHostcheck in production |