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:
# Environment variableWEB_CUSTOM_ASSETS_PATH=/path/to/custom
# CLI flagxray-checker --web-custom-assets-path=/path/to/customIf 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.extLogo Files
You can customize the logo in two ways:
- Single logo — provide
logo.svgorlogo.pngto use the same logo for both dark and light themes - Theme-specific logos — provide
logo-dark.svg/logo-dark.pngandlogo-light.svg/logo-light.pngfor different logos per theme
Priority order (first found is used):
logo-dark.svg/logo-light.svg(theme-specific SVG)logo-dark.png/logo-light.png(theme-specific PNG)logo.svg(universal SVG)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)
| Variable | Type | Description |
|---|---|---|
.Version | string | Xray Checker version |
.Host | string | Server host |
.Port | string | Server port |
.CheckInterval | int | Proxy check interval in seconds |
.Timeout | int | Proxy check timeout in seconds |
.CheckMethod | string | Check method: ip, status, or download |
.IPCheckUrl | string | URL for IP checking |
.StatusCheckUrl | string | URL for status checking |
.DownloadUrl | string | URL for download checking |
.SimulateLatency | bool | Whether latency simulation is enabled |
.SubscriptionUpdate | bool | Whether subscription auto-update is enabled |
.SubscriptionUpdateInterval | int | Subscription update interval in seconds |
.StartPort | int | First proxy port number |
.Instance | string | Metrics instance label |
.PushUrl | string | Prometheus pushgateway URL |
.ShowServerDetails | bool | Whether to show server IPs and ports |
.IsPublic | bool | Whether public mode is enabled |
.SubscriptionName | string | Subscription name for display |
.Endpoints | []EndpointInfo | Array of proxy endpoints |
EndpointInfo (each item in .Endpoints)
| Variable | Type | Availability | Description |
|---|---|---|---|
.Name | string | Always | Proxy name |
.StableID | string | Always | Unique proxy identifier |
.Index | int | Always | Proxy index (0-based) |
.Status | bool | Always | true if online |
.Latency | time.Duration | Always | Response latency |
.ServerInfo | string | When ShowServerDetails && !IsPublic | Server address and port |
.ProxyPort | int | When ShowServerDetails && !IsPublic | Local proxy port |
.URL | string | When !IsPublic | Config status endpoint URL |
Template Functions
| Function | Description | Example |
|---|---|---|
formatLatency | Formats 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.cssStartup Logs
When custom assets are loaded, you’ll see:
INFO Custom assets enabled: /app/customINFO Custom assets loaded:INFO ✓ logo.svgINFO ✓ custom.cssINFO Using default templateOr with custom template:
INFO Custom assets loaded:INFO ✓ index.htmlINFO ✓ custom.cssINFO Using custom template: index.htmlErrors
| Error | Cause |
|---|---|
custom assets directory does not exist | Path set but directory not found |
failed to parse custom template | Invalid Go template syntax in index.html |
The application will not start if custom assets path is set but invalid.