Skip to content

Web Customization

Xray Checker allows full customization of the web interface. You can replace the default template, add custom styles, change the logo, favicon, and add any other static files.

Enabling Custom Assets

Set the path to your custom assets directory:

Terminal window
# Environment variable
WEB_CUSTOM_ASSETS_PATH=/path/to/custom
# CLI flag
xray-checker --web-custom-assets-path=/path/to/custom

If the path is set and the directory exists, custom assets will be loaded at startup.

Directory Structure

Place your custom files in a flat directory (no subdirectories):

custom/
├── index.html # Full template replacement (optional)
├── logo.svg # Single logo for both themes (optional)
├── logo.png # Single logo PNG (optional)
├── logo-dark.svg # Logo for dark theme (optional)
├── logo-dark.png # Logo for dark theme PNG (optional)
├── logo-light.svg # Logo for light theme (optional)
├── logo-light.png # Logo for light theme PNG (optional)
├── favicon.ico # Favicon override (optional)
├── custom.css # Additional styles, auto-injected (optional)
└── any-file.ext # Available at /static/any-file.ext

Logo Files

You can customize the logo in two ways:

  1. Single logo — provide logo.svg or logo.png to use the same logo for both dark and light themes
  2. Theme-specific logos — provide logo-dark.svg/logo-dark.png and logo-light.svg/logo-light.png for different logos per theme

Priority order (first found is used):

  1. logo-dark.svg / logo-light.svg (theme-specific SVG)
  2. logo-dark.png / logo-light.png (theme-specific PNG)
  3. logo.svg (universal SVG)
  4. logo.png (universal PNG)

Custom Styles (custom.css)

The easiest way to customize the appearance. If custom.css exists, it will be automatically injected after the default styles.

CSS Variables

Override theme colors using CSS variables:

:root {
/* Background colors */
--bg-primary: #0a0a0f;
--bg-secondary: #12121a;
--bg-tertiary: #1a1a24;
/* Text colors */
--text-primary: #f4f4f5;
--text-secondary: #a1a1aa;
--text-muted: #71717a;
/* Accent colors */
--color-green: #22c55e;
--color-red: #ef4444;
--color-orange: #f97316;
/* Border */
--border: #27272a;
}

Example: Light Theme Override

:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #495057;
--text-muted: #6c757d;
--border: #dee2e6;
}

Example: Custom Logo Size

.header-logo img {
width: 48px;
height: 48px;
}

Full Template Replacement (index.html)

For complete control, provide your own index.html. This is a Go template with access to all page data.

Go Template Syntax

{{ .Variable }} <!-- Output variable -->
{{ if .Condition }}...{{ end }}
{{ range .Array }}...{{ end }}
{{ formatLatency .Latency }} <!-- Format duration as "123ms" or "n/a" -->

Available Variables

PageData (root object)

VariableTypeDescription
.VersionstringXray Checker version
.HoststringServer host
.PortstringServer port
.CheckIntervalintProxy check interval in seconds
.TimeoutintProxy check timeout in seconds
.CheckMethodstringCheck method: ip, status, or download
.IPCheckUrlstringURL for IP checking
.StatusCheckUrlstringURL for status checking
.DownloadUrlstringURL for download checking
.SimulateLatencyboolWhether latency simulation is enabled
.SubscriptionUpdateboolWhether subscription auto-update is enabled
.SubscriptionUpdateIntervalintSubscription update interval in seconds
.StartPortintFirst proxy port number
.InstancestringMetrics instance label
.PushUrlstringPrometheus pushgateway URL
.ShowServerDetailsboolWhether to show server IPs and ports
.IsPublicboolWhether public mode is enabled
.SubscriptionNamestringSubscription name for display
.Endpoints[]EndpointInfoArray of proxy endpoints

EndpointInfo (each item in .Endpoints)

VariableTypeAvailabilityDescription
.NamestringAlwaysProxy name
.StableIDstringAlwaysUnique proxy identifier
.IndexintAlwaysProxy index (0-based)
.StatusboolAlwaystrue if online
.Latencytime.DurationAlwaysResponse latency
.ServerInfostringWhen ShowServerDetails && !IsPublicServer address and port
.ProxyPortintWhen ShowServerDetails && !IsPublicLocal proxy port
.URLstringWhen !IsPublicConfig status endpoint URL

Template Functions

FunctionDescriptionExample
formatLatencyFormats duration as milliseconds{{ formatLatency .Latency }}"123ms" or "n/a"

Conditional Rendering

<!-- Show only in non-public mode -->
{{ if not .IsPublic }}
<a href="{{ .URL }}">Config</a>
{{ end }}
<!-- Show server details when enabled -->
{{ if .ShowServerDetails }}
<span>{{ .ServerInfo }}</span>
{{ end }}
<!-- Loop through proxies -->
{{ range .Endpoints }}
<div class="{{ if .Status }}online{{ else }}offline{{ end }}">
{{ .Name }} - {{ formatLatency .Latency }}
</div>
{{ end }}

Minimal Template Example

<!DOCTYPE html>
<html>
<head>
<title>{{ if .SubscriptionName }}{{ .SubscriptionName }}{{ else }}Status{{ end }}</title>
</head>
<body>
<h1>Proxy Status</h1>
{{ range .Endpoints }}
<div>
{{ if .Status }}✓{{ else }}✗{{ end }}
{{ .Name }}
({{ formatLatency .Latency }})
</div>
{{ end }}
</body>
</html>

Docker Example

services:
xray-checker:
image: kutovoys/xray-checker:latest
volumes:
- ./custom:/app/custom:ro
environment:
- WEB_CUSTOM_ASSETS_PATH=/app/custom
- SUBSCRIPTION_URL=https://...

Directory structure:

my-project/
├── docker-compose.yml
└── custom/
├── logo.svg # or logo-dark.svg + logo-light.svg
├── favicon.ico
└── custom.css

Startup Logs

When custom assets are loaded, you’ll see:

INFO Custom assets enabled: /app/custom
INFO Custom assets loaded:
INFO ✓ logo.svg
INFO ✓ custom.css
INFO Using default template

Or with custom template:

INFO Custom assets loaded:
INFO ✓ index.html
INFO ✓ custom.css
INFO Using custom template: index.html

Errors

ErrorCause
custom assets directory does not existPath set but directory not found
failed to parse custom templateInvalid Go template syntax in index.html

The application will not start if custom assets path is set but invalid.