> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/minekube/gate/llms.txt
> Use this file to discover all available pages before exploring further.

# Forced Hosts Configuration

> Route players to specific servers based on connection hostname using Gate's ForcedHosts feature

# Forced Hosts Configuration

ForcedHosts allows you to route players to specific backend servers based on the **hostname** they use to connect to your Gate proxy. This enables multiple domains to point to the same proxy but direct players to different servers.

<Note>
  ForcedHosts is available in **classic mode** (when `lite.enabled: false`). For lightweight deployments, see [Gate Lite Mode](/guide/lite) which provides similar host-based routing with advanced load balancing.
</Note>

## How It Works

When a player connects to your Gate proxy, the client sends the hostname they used in the initial handshake packet. Gate examines this hostname and routes the player based on your ForcedHosts configuration.

```mermaid theme={null}
graph LR
    A[Player connects to<br/>creative.example.com] --> B[Gate Proxy]
    B --> C{Check ForcedHosts}
    C -->|Match found| D[Creative Server]
    C -->|No match| E[Try List Servers]
```

## Configuration

<ParamField path="config.forcedHosts" type="object">
  Map of hostnames to server name lists. Keys are hostnames (case-insensitive), values are arrays of server names for load balancing.

  ```yaml theme={null}
  forcedHosts:
    'creative.example.com': ['creative']
    'survival.example.com': ['survival']
    'lobby.example.com': ['lobby-1', 'lobby-2']
  ```
</ParamField>

## Key Features

<CardGroup cols={2}>
  <Card title="Hostname-Only Matching" icon="globe">
    Port numbers are ignored - `creative.example.com:25565` matches `creative.example.com`
  </Card>

  <Card title="Case-Insensitive" icon="text-size">
    `Creative.Example.COM` matches `creative.example.com`
  </Card>

  <Card title="Load Balancing" icon="scale-balanced">
    List multiple servers per hostname for distribution
  </Card>

  <Card title="Automatic Fallback" icon="arrow-rotate-left">
    Uses `try` list when no forced host matches
  </Card>
</CardGroup>

## Basic Configuration

### Simple Domain Routing

Route different domains to different game modes:

```yaml config.yml theme={null}
config:
  bind: 0.0.0.0:25565
  
  servers:
    lobby: localhost:25566
    creative: localhost:25567
    survival: localhost:25568
    minigames: localhost:25569
  
  try:
    - lobby  # Default for unmatched hostnames
  
  forcedHosts:
    'creative.example.com': ['creative']
    'survival.example.com': ['survival']
    'games.example.com': ['minigames']
    'lobby.example.com': ['lobby']
```

### Load Balancing Setup

Distribute players across multiple servers for the same hostname:

```yaml config.yml theme={null}
config:
  servers:
    lobby-1: localhost:25566
    lobby-2: localhost:25567
    creative-1: localhost:25568
    creative-2: localhost:25569
    survival: localhost:25570
  
  try:
    - lobby-1
    - lobby-2
  
  forcedHosts:
    # Load balance between multiple lobby servers
    'lobby.example.com': ['lobby-1', 'lobby-2']
    
    # Load balance between multiple creative servers  
    'creative.example.com': ['creative-1', 'creative-2']
    
    # Single survival server
    'survival.example.com': ['survival']
```

<Note>
  Gate tries servers in order from the list. For advanced load balancing strategies (random, round-robin, least-connections), use [Gate Lite mode](/guide/lite).
</Note>

## Advanced Examples

### Multi-Domain Network

Support multiple domains and aliases for a large server network:

```yaml config.yml theme={null}
config:
  servers:
    hub: localhost:25566
    skyblock: localhost:25567
    prison: localhost:25568
    creative: localhost:25569
    survival: localhost:25570
  
  try:
    - hub
  
  forcedHosts:
    # Main network domains
    'play.mynetwork.com': ['hub']
    'hub.mynetwork.com': ['hub']
    
    # Game mode specific domains
    'skyblock.mynetwork.com': ['skyblock']
    'prison.mynetwork.com': ['prison']
    'creative.mynetwork.com': ['creative']
    'survival.mynetwork.com': ['survival']
    
    # Short aliases
    'sb.mynetwork.com': ['skyblock']
    'build.mynetwork.com': ['creative']
```

### Development and Production

Separate development and production environments:

```yaml config.yml theme={null}
config:
  servers:
    prod-lobby: production-lobby.internal:25565
    prod-survival: production-survival.internal:25565
    dev-lobby: dev-lobby.internal:25565
    dev-survival: dev-survival.internal:25565
    test-lobby: test-lobby.internal:25565
  
  try:
    - prod-lobby
  
  forcedHosts:
    # Production domains (public)
    'play.example.com': ['prod-lobby']
    'survival.example.com': ['prod-survival']
    
    # Development domains (internal)
    'dev.example.com': ['dev-lobby']
    'dev-survival.example.com': ['dev-survival']
    
    # Testing domains
    'test.example.com': ['test-lobby']
    'staging.example.com': ['dev-lobby']
```

### Mixed Load Balancing Strategy

Combine load balancing with single servers:

```yaml config.yml theme={null}
config:
  servers:
    lobby-1: 192.168.1.10:25565
    lobby-2: 192.168.1.11:25565
    creative-main: 192.168.1.20:25565
    creative-backup: 192.168.1.21:25565
    survival: 192.168.1.30:25565
    minigames-1: 192.168.1.40:25565
    minigames-2: 192.168.1.41:25565
  
  try:
    - lobby-1
    - lobby-2
  
  forcedHosts:
    # Load balanced lobbies (tries in order)
    'lobby.example.com': ['lobby-1', 'lobby-2']
    
    # Primary with backup fallback
    'creative.example.com': ['creative-main', 'creative-backup']
    
    # Single dedicated server
    'survival.example.com': ['survival']
    
    # Multiple game servers
    'games.example.com': ['minigames-1', 'minigames-2']
```

## DNS Configuration

For ForcedHosts to work, configure DNS records to point all domains to your Gate proxy IP:

<CodeGroup>
  ```dns Individual A Records theme={null}
  # Point each domain to Gate proxy
  creative.example.com    A    203.0.113.10
  survival.example.com    A    203.0.113.10
  lobby.example.com       A    203.0.113.10
  play.example.com        A    203.0.113.10
  ```

  ```dns Wildcard Record theme={null}
  # Use wildcard if DNS provider supports it
  *.example.com           A    203.0.113.10

  # Still need root domain
  example.com             A    203.0.113.10
  ```

  ```dns SRV Records (Optional) theme={null}
  # Optional SRV records for custom ports
  _minecraft._tcp.creative.example.com  SRV  0 5 25565 gate.example.com.
  _minecraft._tcp.survival.example.com  SRV  0 5 25565 gate.example.com.
  ```
</CodeGroup>

## Behavior and Matching

Understanding how Gate matches hostnames:

### Matching Rules

1. **Port Ignored** - `example.com:25565` matches `example.com`
2. **Case Insensitive** - `Example.COM` matches `example.com`
3. **Exact Match** - No wildcard or regex support
4. **Virtual Host Cleaning** - Handles Forge FML separators automatically
5. **IP Addresses** - Raw IP connections don't match, use try list

### Connection Examples

Given this configuration:

```yaml theme={null}
forcedHosts:
  'creative.example.com': ['creative-server']
  'survival.example.com': ['survival-server']

try:
  - lobby-server
```

How different connections are routed:

| Player Connects To           | Routes To         | Reason                         |
| ---------------------------- | ----------------- | ------------------------------ |
| `creative.example.com:25565` | `creative-server` | Matches forced host            |
| `CREATIVE.EXAMPLE.COM:25565` | `creative-server` | Case-insensitive match         |
| `survival.example.com:12345` | `survival-server` | Port ignored, hostname matches |
| `other.example.com:25565`    | `lobby-server`    | No match, uses try list        |
| `192.168.1.100:25565`        | `lobby-server`    | IP address, uses try list      |
| `example.com:25565`          | `lobby-server`    | No match, uses try list        |

### Forge/FML Handling

Forge clients append `\x00FML\x00` to virtual host. Gate automatically strips this before matching:

```yaml theme={null}
# Forge client connects with: creative.example.com�FML�
# Gate normalizes to: creative.example.com
# Matches: 'creative.example.com'
```

## Load Balancing Behavior

When multiple servers are listed for a hostname:

```yaml theme={null}
forcedHosts:
  'lobby.example.com': ['lobby-1', 'lobby-2', 'lobby-3']
```

**Gate tries servers in order:**

1. Attempts connection to `lobby-1`
2. If `lobby-1` is offline or fails, tries `lobby-2`
3. If `lobby-2` is offline or fails, tries `lobby-3`
4. If all fail, uses try list or disconnects player

<Warning>
  This is **sequential failover**, not random load balancing. All players go to first available server. For true load balancing (random, round-robin, least-connections), use [Gate Lite mode](/guide/lite).
</Warning>

## Complete Example

A full configuration for a multi-server network with forced hosts:

```yaml config.yml theme={null}
config:
  bind: 0.0.0.0:25565
  onlineMode: true
  
  # Backend servers
  servers:
    # Main lobby servers (load balanced)
    lobby-1: 10.0.1.10:25565
    lobby-2: 10.0.1.11:25565
    
    # Game mode servers
    survival: 10.0.2.10:25565
    creative: 10.0.3.10:25565
    skyblock: 10.0.4.10:25565
    
    # Minigame servers
    bedwars: 10.0.5.10:25565
    skywars: 10.0.5.11:25565
    duels: 10.0.5.12:25565
    
    # Development
    dev: 10.0.9.10:25565
  
  # Default connection order
  try:
    - lobby-1
    - lobby-2
  
  # Hostname-based routing
  forcedHosts:
    # Main domain - load balanced lobbies
    'play.example.com': ['lobby-1', 'lobby-2']
    'lobby.example.com': ['lobby-1', 'lobby-2']
    
    # Game modes
    'survival.example.com': ['survival']
    'creative.example.com': ['creative']
    'skyblock.example.com': ['skyblock']
    
    # Minigames
    'bedwars.example.com': ['bedwars']
    'skywars.example.com': ['skywars']
    'duels.example.com': ['duels']
    
    # Development (internal only)
    'dev.example.internal': ['dev']
  
  # Player info forwarding
  forwarding:
    mode: velocity
    velocitySecret: your-secret-here
  
  # Enable failover
  failoverOnUnexpectedServerDisconnect: true
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Players not routing correctly">
    **Problem**: Players connecting to `creative.example.com` end up on wrong server

    **Diagnosis**:

    1. Enable debug logging: `debug: true`
    2. Check logs for hostname Gate receives
    3. Verify DNS points to Gate proxy
    4. Test with `/connect creative.example.com` (if different result, it's DNS)

    **Common Causes**:

    * DNS not pointing to Gate
    * Hostname mismatch (check case, spacing)
    * Server name in forcedHosts doesn't exist in servers section
    * Player connecting via IP address instead of domain
  </Accordion>

  <Accordion title="Load balancing not working">
    **Problem**: All players go to same server despite multiple listed

    **Explanation**: This is expected - Gate uses sequential failover, not random distribution

    **Solution**: For true load balancing, use [Gate Lite mode](/guide/lite) with load balancing strategies:

    ```yaml theme={null}
    lite:
      enabled: true
      routes:
        - host: 'lobby.example.com'
          backend: ['lobby-1:25565', 'lobby-2:25565']
          strategy: random  # or round-robin, least-connections
    ```
  </Accordion>

  <Accordion title="IP connections bypass forced hosts">
    **Problem**: Players connecting via IP address don't get routed

    **Explanation**: This is expected - ForcedHosts only work with domain names

    **Solutions**:

    * Encourage players to use domain names
    * Configure try list as your "default" routing
    * Block IP connections: set `server-ip=127.0.0.1` on Gate bind
  </Accordion>

  <Accordion title="Forced host server not found">
    **Error**: `Forced host "creative.example.com" server "creative" must be registered under servers`

    **Solution**: Add server to servers section:

    ```yaml theme={null}
    servers:
      creative: localhost:25567  # Add this

    forcedHosts:
      'creative.example.com': ['creative']
    ```
  </Accordion>

  <Accordion title="Fallback not working">
    **Problem**: Players get disconnected when hostname doesn't match

    **Diagnosis**:

    * Check try list is configured with available servers
    * Verify servers in try list exist in servers section
    * Check if servers in try list are actually online

    **Solution**:

    ```yaml theme={null}
    servers:
      lobby: localhost:25566

    try:
      - lobby  # Must exist in servers
    ```
  </Accordion>
</AccordionGroup>

## Migration from Broken Configs

<Info>
  If you configured ForcedHosts before it was fixed (PR #560), your configuration should now work automatically! The fix included proper hostname extraction, case normalization, and fallback behavior.
</Info>

**No changes needed** - existing `forcedHosts` configurations will work after updating Gate.

## Comparison: ForcedHosts vs Gate Lite

| Feature                | ForcedHosts (Classic) | Gate Lite                 |
| ---------------------- | --------------------- | ------------------------- |
| Hostname-based routing | ✅                     | ✅                         |
| Sequential failover    | ✅                     | ✅                         |
| Random load balancing  | ❌                     | ✅                         |
| Round-robin            | ❌                     | ✅                         |
| Least-connections      | ❌                     | ✅                         |
| Lowest-latency         | ❌                     | ✅                         |
| Wildcard hostnames     | ❌                     | ✅                         |
| Hostname regex         | ❌                     | ✅ (patterns)              |
| Server switching       | ✅ (/server command)   | ❌ (thin proxy)            |
| Proxy commands         | ✅                     | ❌                         |
| Extensions/plugins     | ✅ (full access)       | ⚠️ (limited)              |
| Performance            | Good                  | Excellent (lower latency) |

For advanced routing and load balancing, consider [Gate Lite mode](/guide/lite).

## Best Practices

<AccordionGroup>
  <Accordion title="Use descriptive hostnames">
    Choose clear, memorable hostnames:

    * ✅ `creative.example.com`, `survival.example.com`
    * ❌ `s1.example.com`, `server2.example.com`
  </Accordion>

  <Accordion title="Configure a solid try list">
    Always have reliable fallback servers in try list for unmatched hostnames or IP connections:

    ```yaml theme={null}
    try:
      - lobby-1
      - lobby-2
      - hub
    ```
  </Accordion>

  <Accordion title="Document your routing">
    Keep documentation of which domains route where. Add comments:

    ```yaml theme={null}
    forcedHosts:
      # Public domains
      'play.example.com': ['lobby-1', 'lobby-2']
      
      # Internal domains
      'dev.internal': ['dev-server']
    ```
  </Accordion>

  <Accordion title="Test all hostnames">
    After configuration changes, test each hostname:

    ```bash theme={null}
    # Test from Minecraft client
    # Connect to: creative.example.com
    # Verify lands on creative server
    ```
  </Accordion>

  <Accordion title="Use wildcard DNS carefully">
    If using wildcard DNS (`*.example.com`), remember all subdomains will resolve. Consider:

    * Adding explicit forcedHosts for intended subdomains
    * Having good default try list for unmatched subdomains
  </Accordion>
</AccordionGroup>

## Related Configuration

<CardGroup cols={2}>
  <Card title="Servers" icon="server" href="/configuration/servers">
    Configure backend servers and try list
  </Card>

  <Card title="Gate Lite Mode" icon="bolt" href="/guide/lite">
    Advanced host-based routing with load balancing
  </Card>

  <Card title="Forwarding" icon="share-nodes" href="/configuration/forwarding">
    Forward player info to backend servers
  </Card>

  <Card title="Status Ping" icon="signal" href="/configuration/status-ping">
    Customize server list appearance per hostname
  </Card>
</CardGroup>
