Reverse Proxy Configuration
In production environments, it's recommended to expose Shortlinker service through a reverse proxy.
Important: Reverse Proxy Configuration Requirements
When deploying behind a reverse proxy, you must set X-Real-IP and X-Forwarded-For headers. These headers are required for obtaining the client's real IP address, which is essential for the login rate limiting feature to function properly. Missing these headers will result in a 500 error during login.
Caddy Configuration
Basic Configuration
# TCP port
esap.cc {
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
# Optional: Add cache control
header {
Cache-Control "no-cache, no-store, must-revalidate"
}
}
# Unix socket
esap.cc {
reverse_proxy unix//tmp/shortlinker.sock {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
header {
Cache-Control "no-cache, no-store, must-revalidate"
}
}Configuration with SSL
esap.cc {
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
# Automatic HTTPS
tls {
protocols tls1.2 tls1.3
}
# Log configuration
log {
output file /var/log/caddy/shortlinker.log
format single_field common_log
}
}Nginx Configuration
Basic Configuration
# TCP port
server {
listen 80;
server_name esap.cc;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Disable cache
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
# Unix socket
server {
listen 80;
server_name esap.cc;
location / {
proxy_pass http://unix:/tmp/shortlinker.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}Complete HTTPS Configuration
server {
listen 80;
server_name esap.cc;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name esap.cc;
# SSL configuration
ssl_certificate /etc/ssl/certs/esap.cc.crt;
ssl_certificate_key /etc/ssl/private/esap.cc.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
location / {
proxy_pass http://127.0.0.1:8080;
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;
# Disable cache
add_header Cache-Control "no-cache, no-store, must-revalidate";
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
}
# Log configuration
access_log /var/log/nginx/shortlinker.access.log;
error_log /var/log/nginx/shortlinker.error.log;
}Apache Configuration
# TCP port
<VirtualHost *:80>
ServerName esap.cc
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# Forward client real IP (requires mod_headers)
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
# Disable cache
Header always set Cache-Control "no-cache, no-store, must-revalidate"
# Logs
CustomLog /var/log/apache2/shortlinker.access.log combined
ErrorLog /var/log/apache2/shortlinker.error.log
</VirtualHost>
# Unix socket
<VirtualHost *:80>
ServerName esap.cc
ProxyPreserveHost On
ProxyPass / unix:/tmp/shortlinker.sock|http://localhost/
ProxyPassReverse / unix:/tmp/shortlinker.sock|http://localhost/
# Forward client real IP (requires mod_headers)
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
Header always set Cache-Control "no-cache, no-store, must-revalidate"
CustomLog /var/log/apache2/shortlinker.access.log combined
ErrorLog /var/log/apache2/shortlinker.error.log
</VirtualHost>Load Balancing
Nginx Load Balancing
upstream shortlinker {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
listen 80;
server_name esap.cc;
location / {
proxy_pass http://shortlinker;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}Performance Optimization
Connection Pool Optimization
upstream shortlinker {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
location / {
proxy_pass http://shortlinker;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_send_timeout 5s;
proxy_read_timeout 5s;
}
}Cache Configuration
Although short links shouldn't be cached, static resources can be cached:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}Monitoring and Logging
Access Log Format
log_format shortlinker '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/shortlinker.log shortlinker;Health Check
Note:
/health/*endpoints require authentication by default. In production, it’s recommended to setHEALTH_TOKENand probe/health/liveor/health/readywithAuthorization: Bearer <token>.
If adding request headers is not convenient, probing/(default returns307) can be used as a simple liveness check.
location = /_healthz {
access_log off;
# Recommended: authenticated probe (requires HEALTH_TOKEN configured)
# proxy_set_header Authorization "Bearer your_health_token";
# proxy_pass http://127.0.0.1:8080/health/live;
# Fallback: simple liveness probe (probes `/`, 307 counts as alive)
proxy_pass http://127.0.0.1:8080/;
proxy_connect_timeout 1s;
proxy_send_timeout 1s;
proxy_read_timeout 1s;
}