NGINX Reverse Proxy & SSL/TLS Configuration¶
NGINX reverse proxy setup with SSL termination and WebSocket support.
Overview¶
The recommended production deployment runs Faraday behind NGINX for:
- SSL/TLS termination — Encrypt client connections with HTTPS.
- WebSocket proxying — Real-time notifications and agent communication via Socket.IO.
- Static file serving — Serve the frontend directly from NGINX.
- Security headers — Add HSTS, X-Frame-Options, and other protections.
- Upload limits — Control maximum file upload size.
Architecture¶
Client (HTTPS :443)
│
▼
┌──────────┐
│ NGINX │ SSL termination, static files, security headers
│ :443 │
└────┬─────┘
│ HTTP (internal)
├──────────────────► Faraday API (localhost:5985/_api/)
└──────────────────► Socket.IO (localhost:5985/socket.io)
Prerequisites¶
- A working Faraday Server installation (listening on
localhost:5985). - Root or sudo privileges.
- A domain name or FQDN configured for your server.
- SSL certificate (self-signed for testing, CA-issued for production).
Install NGINX¶
Debian/Ubuntu:
sudo apt install nginx
RHEL/CentOS/Fedora:
sudo dnf install nginx
After installation, enable and start the service:
sudo systemctl enable nginx
sudo systemctl start nginx
SSL Certificate Setup¶
Self-Signed Certificate (Development/Testing)¶
# Generate private key
sudo openssl genrsa -out /etc/ssl/faraday.key 2048
# Create certificate signing request
sudo openssl req -new -key /etc/ssl/faraday.key -out /etc/ssl/faraday.csr
# Generate self-signed certificate (valid 365 days)
sudo openssl x509 -req -days 365 \
-in /etc/ssl/faraday.csr \
-signkey /etc/ssl/faraday.key \
-out /etc/ssl/faraday.crt
# Set permissions
sudo chmod 600 /etc/ssl/faraday.key
sudo chmod 644 /etc/ssl/faraday.crt
Common Name
When prompted for the Common Name (CN), enter your exact FQDN (e.g.,
faraday.example.com). A mismatched CN will cause browser certificate
warnings.
Production Certificate (Let's Encrypt)¶
For production, use a trusted Certificate Authority. With Certbot:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d faraday.example.com
Certbot automatically configures NGINX and sets up auto-renewal.
NGINX Configuration¶
Automatic Generation¶
Faraday includes a command to generate an NGINX configuration:
faraday-manage generate-nginx-config \
--fqdn faraday.example.com \
--port 5985 \
--ssl-certificate /etc/ssl/faraday.crt \
--ssl-key /etc/ssl/faraday.key
The command outputs the configuration to stdout. Review it, then save to your NGINX sites directory.
Manual Configuration¶
Create a configuration file at /etc/nginx/sites-available/faraday (Debian) or
/etc/nginx/conf.d/faraday.conf (RHEL):
# Cache expiry map for static assets
map $sent_http_content_type $expires {
default off;
text/html max;
text/css max;
application/javascript max;
~image/ max;
}
server {
server_name faraday.example.com;
listen 443 ssl;
# --- Security Headers ---
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options nosniff;
# --- Upload Limit ---
# Maximum file upload size (reports, evidence).
# Increase if you upload large reports.
client_max_body_size 500M;
# --- SSL Configuration ---
ssl_session_cache shared:SSL:50m;
ssl_certificate /etc/ssl/faraday.crt;
ssl_certificate_key /etc/ssl/faraday.key;
# --- Compression ---
gzip on;
gzip_types application/javascript text/css;
expires $expires;
index index.html index.htm;
# --- Frontend Static Files ---
# For bare-metal installations, serve the frontend directly.
# Adjust the path to match your Python installation.
location / {
alias /opt/faraday/lib/python3.11/site-packages/faraday/server/www/;
try_files $uri $uri/ /index.html;
}
# --- API Proxy ---
location /_api {
proxy_pass http://127.0.0.1:5985/_api;
proxy_redirect http:// $scheme://;
proxy_read_timeout 300;
proxy_cookie_path / "/; secure";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Proto $scheme;
}
# --- WebSocket Proxy (Socket.IO) ---
location /socket.io {
proxy_set_header Host $http_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_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://127.0.0.1:5985;
}
}
# HTTP to HTTPS redirect
server {
server_name faraday.example.com;
listen 80;
listen [::]:80;
if ($host = faraday.example.com) {
return 301 https://$host$request_uri;
}
return 404;
}
Enable the Configuration¶
Debian/Ubuntu (sites-available/sites-enabled):
sudo ln -s /etc/nginx/sites-available/faraday /etc/nginx/sites-enabled/faraday
sudo nginx -t # Test configuration
sudo systemctl reload nginx
RHEL/CentOS (conf.d):
sudo nginx -t # Test configuration
sudo systemctl reload nginx
Docker Deployments¶
When running Faraday in Docker, NGINX sits outside the container (or in a separate container) and proxies to the published port.
Key differences from bare-metal:
- No static file serving — The Docker container serves the frontend on port
- Change the
location /block to proxy to the container:
location / {
proxy_pass http://127.0.0.1:5985/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
- Container hostname — Replace
127.0.0.1:5985with the container's published address if not using host networking.
OpenAPI Swagger Configuration¶
After configuring NGINX with a domain, update the OpenAPI spec to reflect the HTTPS URL:
faraday-manage openapi-swagger --server https://faraday.example.com
This ensures the Swagger UI at /_api/v3/swagger uses the correct server URL.
Key Configuration Parameters¶
client_max_body_size¶
Controls maximum upload size. The NGINX template uses 150M; the Advanced guide
uses 500M. Set according to your report sizes:
| Use Case | Recommended Value |
|---|---|
| Standard reports | 150M |
| Large Nessus/Qualys scans | 500M |
| Very large enterprise scans | 1G |
proxy_read_timeout¶
Time in seconds NGINX waits for a response from Faraday. Default is 300 (5
minutes). Increase for long-running API operations:
proxy_read_timeout 600; # 10 minutes for large imports
WebSocket Timeouts¶
The WebSocket proxy should not have a proxy_read_timeout set, or it should
be set very high, to keep long-lived connections open. The Faraday server handles
its own ping/pong with socketio_ping_interval=60 and
socketio_ping_timeout=220.
Multitenant Configuration¶
For multitenant deployments, Faraday supports a URL prefix. The NGINX template
(nginx_config.j2) supports an optional multitenant_url variable:
# With multitenant prefix "tenant1":
location /tenant1/ {
try_files $uri $uri/ /index.html;
}
location /tenant1/_api/ {
proxy_pass http://localhost:5985/_api/;
# ... same proxy headers
}
location /tenant1/socket.io {
# ... same WebSocket proxy
proxy_pass http://localhost:5985;
}
Troubleshooting¶
502 Bad Gateway¶
Faraday Server is not running or not accessible on port 5985:
# Check server status
systemctl status faraday-server
# Check if port is listening
ss -tlnp | grep 5985
WebSocket connection failed¶
Verify the /socket.io proxy block includes proxy_http_version 1.1 and the
Upgrade / Connection headers. Also check that the server's
socketio_ping_timeout (default 220s) is not being interrupted by an
intermediate proxy or firewall.
413 Request Entity Too Large¶
The uploaded file exceeds client_max_body_size. Increase the value in the NGINX
configuration:
client_max_body_size 500M;
Mixed content warnings¶
Ensure X-Forwarded-Proto is set to $scheme and X-Forwarded-Ssl is on.
The server uses these headers to generate correct URLs in responses.
DNS resolution for local FQDN¶
If using a local FQDN (e.g., faraday.local), add it to /etc/hosts:
127.0.0.1 faraday.local
NGINX configuration test¶
Always test before reloading:
sudo nginx -t