Nginx Proxy Setup
Configure Nginx reverse proxy to serve analytics-tube tracking and bypass ad blockers
Nginx is a powerful web server and reverse proxy that makes it easy to proxy analytics-tube tracking through your own domain. This guide shows how to configure Nginx to forward tracking requests to analytics-tube servers while caching static scripts for better performance.
Overview
Nginx's proxy_pass directive lets you forward requests from your domain to analytics-tube's servers. Combined with caching, this provides excellent performance and reliability for proxying analytics.
What you'll achieve:
- Proxy all analytics-tube endpoints through your domain
- Cache static scripts for better performance
- Forward necessary headers for accurate tracking
- Support all analytics-tube features (session replay, Web Vitals, custom events)
Prerequisites
- Nginx installed (version 1.18 or later recommended)
- SSL/TLS certificate configured (Let's Encrypt recommended)
- Your analytics-tube instance URL:
- Cloud hosted:
https://app.analytics.tube - Self-hosted: Your instance URL
- Cloud hosted:
- Your analytics-tube site ID (found in your dashboard)
Implementation
Configure Nginx Proxy
Add a new location block (or update existing configuration) in your Nginx site configuration:
# /etc/nginx/sites-available/yourdomain.com
server {
listen 443 ssl http2;
server_name yourdomain.com;
# Your SSL configuration
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Your main site configuration
location / {
# Your regular site config...
}
# analytics-tube Analytics Proxy
# Main tracking script (cached)
location = /analytics/script.js {
proxy_pass https://app.analytics.tube/api/script.js;
proxy_set_header Host app.analytics.tube;
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_ssl_server_name on;
# Cache for 1 hour
proxy_cache_valid 200 1h;
proxy_cache_bypass $http_cache_control;
add_header X-Cache-Status $upstream_cache_status;
}
# Session replay script (cached)
location = /analytics/replay.js {
proxy_pass https://app.analytics.tube/api/replay.js;
proxy_set_header Host app.analytics.tube;
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_ssl_server_name on;
proxy_cache_valid 200 1h;
}
# Web Vitals metrics script (cached)
location = /analytics/metrics.js {
proxy_pass https://app.analytics.tube/api/metrics.js;
proxy_set_header Host app.analytics.tube;
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_ssl_server_name on;
proxy_cache_valid 200 1h;
}
# Event tracking endpoint (not cached)
location = /analytics/track {
proxy_pass https://app.analytics.tube/api/track;
proxy_set_header Host app.analytics.tube;
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_set_header Content-Type application/json;
proxy_ssl_server_name on;
}
# User identification endpoint (not cached)
location = /analytics/identify {
proxy_pass https://app.analytics.tube/api/identify;
proxy_set_header Host app.analytics.tube;
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_set_header Content-Type application/json;
proxy_ssl_server_name on;
}
# Session replay recording endpoint (not cached)
location ~ ^/analytics/session-replay/record/(.*)$ {
proxy_pass https://app.analytics.tube/api/session-replay/record/$1;
proxy_set_header Host app.analytics.tube;
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_set_header Content-Type application/json;
proxy_ssl_server_name on;
# Allow larger uploads for session replay
client_max_body_size 10M;
}
# Site configuration endpoint (cached briefly)
location ~ ^/analytics/site/tracking-config/(.*)$ {
proxy_pass https://app.analytics.tube/api/site/tracking-config/$1;
proxy_set_header Host app.analytics.tube;
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_ssl_server_name on;
# Cache for 5 minutes
proxy_cache_valid 200 5m;
}
}Replace app.analytics.tube with your self-hosted analytics-tube instance URL if you're not using the cloud version.
Minimal configuration for basic tracking:
# /etc/nginx/sites-available/yourdomain.com
server {
listen 443 ssl http2;
server_name yourdomain.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Main tracking script
location = /analytics/script.js {
proxy_pass https://app.analytics.tube/api/script.js;
proxy_set_header Host app.analytics.tube;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
proxy_cache_valid 200 1h;
}
# Event tracking
location = /analytics/track {
proxy_pass https://app.analytics.tube/api/track;
proxy_set_header Host app.analytics.tube;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
}
}For better performance with caching, define a cache zone:
# /etc/nginx/nginx.conf (http block)
http {
# Define cache zone for analytics-tube
proxy_cache_path /var/cache/nginx/analytics-tube
levels=1:2
keys_zone=analytics-tube_cache:10m
max_size=100m
inactive=60m
use_temp_path=off;
# ... rest of http config
}Then in your site config:
# /etc/nginx/sites-available/yourdomain.com
server {
# ... server config
location = /analytics/script.js {
proxy_pass https://app.analytics.tube/api/script.js;
proxy_set_header Host app.analytics.tube;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
# Use the cache zone
proxy_cache analytics-tube_cache;
proxy_cache_valid 200 1h;
proxy_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
# ... other locations
}Important: The proxy_set_header X-Real-IP and X-Forwarded-For headers are critical for accurate geolocation and user identification. Without these, all traffic will appear to come from your Nginx server's IP.
Create Cache Directory (Optional)
If using a cache zone, create the cache directory:
sudo mkdir -p /var/cache/nginx/analytics-tube
sudo chown www-data:www-data /var/cache/nginx/analytics-tubeTest Nginx Configuration
Before reloading Nginx, test the configuration for syntax errors:
sudo nginx -tYou should see:
nginx: configuration file /etc/nginx/nginx.conf test is successfulReload Nginx
Apply the changes by reloading Nginx:
sudo systemctl reload nginxOr if you prefer:
sudo nginx -s reloadUpdate Your Tracking Script
Update your HTML to load the script from your proxied domain:
<!DOCTYPE html>
<html>
<head>
<!-- ... other head elements -->
<script src="/analytics/script.js" async data-site-id="YOUR_SITE_ID"></script>
</head>
<body>
<!-- Your content -->
</body>
</html>Replace YOUR_SITE_ID with your actual site ID from the analytics-tube dashboard.
Verify the Setup
Test your configuration:
-
Check Nginx is running:
sudo systemctl status nginx -
Test script loading:
curl -I https://yourdomain.com/analytics/script.jsYou should see:
HTTP/2 200 content-type: application/javascript x-cache-status: MISS # First request -
Test again to verify caching:
curl -I https://yourdomain.com/analytics/script.jsYou should see:
HTTP/2 200 x-cache-status: HIT # Cached! -
Open your website in a browser with Developer Tools and verify:
- Script loads from
/analytics/script.js(your domain) - Tracking requests go to
/analytics/track(your domain) - Data appears in your analytics-tube dashboard after 1-2 minutes
- Script loads from
How It Works
Nginx's proxy_pass directive transparently forwards requests to analytics-tube's servers:
- Browser requests
https://yourdomain.com/analytics/script.js - Nginx matches the location block and forwards to
https://app.analytics.tube/api/script.js - Nginx sets necessary headers (
X-Real-IP,X-Forwarded-For) for accurate tracking - Response is cached (if configured) and sent back to browser
- All subsequent tracking requests follow the same pattern
The analytics-tube script auto-detects it's being served from your domain and sends all tracking data to your domain's /analytics/* endpoints.
Performance Optimization
Caching Strategy
Configure different cache durations based on content type:
# Scripts - cache for 1 hour
location ~ ^/analytics/(script|replay|metrics)\.js$ {
proxy_pass https://app.analytics.tube/api/$1.js;
# ... proxy headers
proxy_cache_valid 200 1h;
proxy_cache_valid 404 1m; # Cache 404s briefly
}
# Config - cache for 5 minutes (changes when you update dashboard settings)
location ~ ^/analytics/site/tracking-config/(.*)$ {
proxy_pass https://app.analytics.tube/api/site/tracking-config/$1;
# ... proxy headers
proxy_cache_valid 200 5m;
}
# Tracking - never cache (each request is unique)
location ~ ^/analytics/(track|identify)$ {
proxy_pass https://app.analytics.tube/api/$1;
# ... proxy headers
# No caching directives
}Gzip Compression
Enable compression for JavaScript files:
# In http or server block
gzip on;
gzip_vary on;
gzip_types application/javascript text/javascript;
gzip_min_length 1000;Connection Pooling
Keep connections to analytics-tube backend alive for better performance:
upstream analytics-tube_backend {
server app.analytics.tube:443;
keepalive 32;
}
location /analytics/ {
proxy_pass https://analytics-tube_backend/api/;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... other proxy headers
}Proxy Buffering
For better performance with larger session replay uploads:
location ~ ^/analytics/session-replay/record/ {
proxy_pass https://app.analytics.tube/api/session-replay/record/;
# ... proxy headers
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
client_max_body_size 10M;
}Security Considerations
Rate Limiting
Protect your proxy from abuse with rate limiting:
# In http block
http {
limit_req_zone $binary_remote_addr zone=analytics_limit:10m rate=10r/s;
# ... rest of config
}
# In server block
location = /analytics/track {
limit_req zone=analytics_limit burst=20 nodelay;
proxy_pass https://app.analytics.tube/api/track;
# ... proxy headers
}This limits to 10 requests per second per IP, with bursts up to 20.
Request Size Limits
Set appropriate limits for different endpoints:
# Default for most endpoints
client_max_body_size 1M;
# Larger limit for session replay
location ~ ^/analytics/session-replay/record/ {
client_max_body_size 10M;
# ... proxy config
}Security Headers
Add security headers to your responses:
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header Referrer-Policy strict-origin-when-cross-origin;Troubleshooting
502 Bad Gateway
Problem: Nginx returns 502 error when accessing analytics endpoints.
Solution:
- Check analytics-tube backend is accessible:
curl -I https://app.analytics.tube/api/script.js - Verify
proxy_ssl_server_name onis set - Check Nginx error logs:
sudo tail -f /var/log/nginx/error.log
Cache not working
Problem: X-Cache-Status always shows MISS.
Solution:
- Verify cache directory exists and is writable
- Check
proxy_cache_pathis defined in http block - Ensure
proxy_cachedirective uses correct zone name - Check cache key is consistent:
proxy_cache_key "$scheme$request_method$host$request_uri";
Incorrect geolocation in analytics-tube
Problem: All visitors show same location (your server's location).
Solution: Ensure you're forwarding client IP headers:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;SSL errors in Nginx logs
Problem: Logs show SSL verification errors when connecting to analytics-tube.
Solution:
Add proxy_ssl_server_name on to enable SNI:
location /analytics/ {
proxy_pass https://app.analytics.tube/api/;
proxy_ssl_server_name on; # Enable SNI
# ... other config
}Advanced Configuration
Subdomain Proxy
Use a dedicated subdomain for analytics:
# /etc/nginx/sites-available/analytics.yourdomain.com
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;
# Proxy all requests to analytics-tube
location / {
proxy_pass https://app.analytics.tube/api/;
proxy_set_header Host app.analytics.tube;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
}
}Then use:
<script src="https://analytics.yourdomain.com/script.js" data-site-id="123"></script>Multiple analytics-tube Instances
If you need to proxy multiple analytics-tube instances:
location /analytics-prod/ {
proxy_pass https://prod.analytics.tube/api/;
# ... proxy config
}
location /analytics-staging/ {
proxy_pass https://staging.analytics.tube/api/;
# ... proxy config
}Related Resources
- Nginx Custom Configuration Guide - Self-hosting analytics-tube with Nginx
- Tracking Script Documentation - Script configuration options
- Generic Proxy Guide - Framework-agnostic concepts
- Nginx Proxy Module - Official Nginx documentation