Click Tracking - BillionMail shows internal port 5679 in email links despite reverse proxy configuration
Problem Description
When using BillionMail behind a reverse proxy (nginx Proxy Manager), the generated email links incorrectly include the internal port 5679 instead of using the external domain without port.
Current Behavior
- Domain Settings configured as:
https://example.com - Generated email links show:
https://example.com:5679/tracking-link - Expected behavior:
https://example.com/tracking-link
Environment
- BillionMail version: 4.2.1
- Setup: Docker with nginx Proxy Manager reverse proxy
- External access: Port 443 (HTTPS)
- Internal container port: 5679
Issues Identified
1. Port appears in email links
Even when Domain Settings are configured with the external domain, BillionMail appends the internal port to all tracking links in emails.
2. Reverse proxy headers ignored
Standard reverse proxy headers are being ignored:
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Proto $scheme;
3. No option to disable click tracking
There doesn't appear to be any configuration option to completely disable click tracking for users who don't need this feature.
Proposed Solutions
Option 1: Respect reverse proxy headers
BillionMail should read X-Forwarded-Port and X-Forwarded-Proto headers to determine the correct external URL format.
Option 2: Enhanced Domain Settings
Allow Domain Settings to override port completely:
- Current: Domain + internal port
- Proposed: Use Domain Settings as-is without port modification
Option 3: Disable tracking option
Add configuration options to disable click tracking entirely:
- Environment variable:
DISABLE_CLICK_TRACKING=true - Admin UI toggle: "Enable Click Tracking" checkbox
- Per-campaign option: Disable tracking for specific emails
Use Cases
- Corporate environments where tracking links look suspicious
- Privacy-focused setups where tracking is unwanted
- Reverse proxy deployments where internal ports shouldn't be exposed
- Mobile app deep links where tracking breaks Universal Links
Expected Behavior
- Email links should respect external domain configuration
- Option to completely disable click tracking
- Proper reverse proxy header support
- Clean URLs without internal port numbers
This issue affects professional email deployments and user experience. Would appreciate guidance on current workarounds or planned fixes.
I'm encountering the same error, email links appear as mail.domain.com:4343/pmta/ instead of maill.domain.com/pmta/
Hey, please let me know what your current BillionMail version is.
Hey @dreambladeflag - i am using version 4.2.1 - I just saw that you released 4.3 yesterday. Is problem fixed there?
Claude AI supposed following changes. Maybe they help.
Thanks
Summary
BillionMail currently has issues when deployed behind reverse proxies (nginx Proxy Manager, Apache, etc.) that result in malformed email tracking URLs containing internal ports. Additionally, there's no way to disable email tracking features. This proposal provides specific code changes to resolve these issues.
Problems
1. Internal Port Exposure in Email Links
Current behavior:
- Email tracking links include internal ports:
https://example.com:5679/pmta/tracking-code - This happens even when
BILLIONMAIL_HOSTNAMEis correctly set tohttps://example.com
Expected behavior:
- Clean external URLs:
https://example.com/pmta/tracking-code
2. Reverse Proxy Headers Ignored
Current behavior:
- nginx Proxy Manager headers (
X-Forwarded-Host,X-Forwarded-Proto,X-Forwarded-Port) are ignored - BillionMail always uses internal server configuration for URL generation
Expected behavior:
- Automatic detection and use of reverse proxy headers
- Proper scheme and host resolution from proxy headers
3. No Way to Disable Email Tracking
Current behavior:
- Click tracking and open tracking (pixels) are always enabled
- No configuration option to disable these features
Expected behavior:
- Administrative controls to enable/disable click tracking and open tracking independently
- API endpoints for programmatic control
Proposed Solution
Code Changes Required
1. Enhanced URL Generation (core/internal/service/domains/baseurl.go)
Add reverse proxy detection:
// Check if we're behind a reverse proxy - if so, don't append port
var reverseProxyDomain string
err := public.OptionsMgrInstance.GetOption(context.Background(), "reverse_proxy_domain", &reverseProxyDomain)
if err == nil && reverseProxyDomain != "" {
withPort = false
}
Add header-based URL building:
// GetBaseURLFromRequest builds URL from reverse proxy headers if available
func GetBaseURLFromRequest(r *ghttp.Request) string {
if r == nil {
return GetBaseURL()
}
// Check for reverse proxy headers
forwardedProto := r.Header.Get("X-Forwarded-Proto")
forwardedHost := r.Header.Get("X-Forwarded-Host")
forwardedPort := r.Header.Get("X-Forwarded-Port")
if forwardedHost != "" {
scheme := "https"
if forwardedProto != "" {
scheme = forwardedProto
}
// Build URL with proper port handling
if forwardedPort != "" && forwardedPort != "80" && forwardedPort != "443" {
if (scheme == "https" && forwardedPort != "443") || (scheme == "http" && forwardedPort != "80") {
return scheme + "://" + forwardedHost + ":" + forwardedPort
}
}
return scheme + "://" + forwardedHost
}
return GetBaseURL()
}
2. Email Tracking Configuration
API Structure (core/api/settings/v1/settings.go):
// Email tracking configuration
EmailTracking struct {
ClickTrackingEnabled bool `json:"click_tracking_enabled" dc:"click tracking enabled"`
OpenTrackingEnabled bool `json:"open_tracking_enabled" dc:"open tracking enabled"`
} `json:"email_tracking" dc:"email tracking configuration"`
type SetEmailTrackingConfigReq struct {
g.Meta `path:"/settings/set_email_tracking_config" tags:"Settings" method:"post"`
Authorization string `json:"authorization" in:"header" v:"required"`
ClickTrackingEnabled bool `json:"click_tracking_enabled"`
OpenTrackingEnabled bool `json:"open_tracking_enabled"`
}
Tracking Logic (core/internal/service/maillog_stat/tracker.go):
// TrackLinks - Check if click tracking is enabled
func (t *MailTracker) TrackLinks() {
var clickTrackingEnabled bool
err := public.OptionsMgrInstance.GetOption(context.Background(), "click_tracking_enabled", &clickTrackingEnabled)
if err != nil {
clickTrackingEnabled = true // Default to enabled
}
if !clickTrackingEnabled {
return // Skip tracking if disabled
}
// ... existing logic
}
// AppendTrackingPixel - Check if open tracking is enabled
func (t *MailTracker) AppendTrackingPixel() {
var openTrackingEnabled bool
err := public.OptionsMgrInstance.GetOption(context.Background(), "open_tracking_enabled", &openTrackingEnabled)
if err != nil {
openTrackingEnabled = true // Default to enabled
}
if !openTrackingEnabled {
return // Skip tracking if disabled
}
// ... existing logic
}
Files to Modify
core/internal/service/domains/baseurl.go- URL generation logiccore/api/settings/v1/settings.go- API definitionscore/internal/controller/settings/settings_v1_get_system_config.go- Load tracking configcore/internal/controller/settings/settings_v1_set_email_tracking_config.go- New API endpointcore/internal/service/maillog_stat/tracker.go- Respect tracking settings
Benefits
- Clean URLs: Email links will be properly formatted without internal ports
- Reverse Proxy Support: Full compatibility with nginx, Apache, Cloudflare, etc.
- Privacy Controls: Administrators can disable tracking for compliance (GDPR, etc.)
- Backward Compatibility: All existing functionality preserved
- API Integration: Programmatic control over tracking settings
Configuration Example
nginx Proxy Manager Headers:
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port 443;
API Usage:
# Set reverse proxy domain
POST /api/settings/set_reverse_proxy_domain
{"domain": "https://mail.example.com"}
# Disable click tracking, keep open tracking
POST /api/settings/set_email_tracking_config
{"click_tracking_enabled": false, "open_tracking_enabled": true}
Hey, please let me know what your current BillionMail version is.
Just upgraded to 4.3.1 and still encountering the same error. Clicking email links appear as https://mail.example.com:4343/pmta/rFNdgvLFt6yLP1BxL... removing the :4343 redirects to the proper url.
Port Info:
## MAIL Ports
SMTP_PORT=25
SMTPS_PORT=465
SUBMISSION_PORT=587
IMAP_PORT=143
IMAPS_PORT=993
POP_PORT=110
POPS_PORT=995
REDIS_PORT=127.0.0.1:26379
SQL_PORT=127.0.0.1:25432
## Manage Ports
HTTP_PORT=81
HTTPS_PORT=4343
nginx config:
server {
listen 80;
server_name mail.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name mail.example.com;
# add Strict-Transport-Security to prevent man in the middle attacks
add_header Strict-Transport-Security "max-age=31536000" always;
set $upstream 0.0.0.0:81;
location / {
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
proxy_pass_header Authorization;
proxy_pass http://$upstream;
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_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
client_max_body_size 0;
proxy_read_timeout 36000s;
proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
}
Thank you for your feedback. We have noticed this issue, and we'll fix it in the next release.
Hey, are you using BillionMail reverse proxy in the Settings? Like this
Hey, are you using BillionMail reverse proxy in the
Settings? Like this![]()
Odd I didn't even see that till now, can't remember setting it unless it was done automatically... I'll try removing the :4343
I tried updating it but get an xhr 500 internal error:
Hey, are you using BillionMail reverse proxy in the
Settings? Like this![]()
我也想停止追踪来着,主要目的是为了不显示实际域名。看到你这图有解了,十分感谢,已反代。