# How to Serve Croc's Web Client with a Custom Upstream Relay

> Learn how to serve Croc's web client with a custom upstream relay. Configure RelayHost, RelayPassword, and AllowedPorts for a secure and personalized experience.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: how-to-guide
- Published: 2026-07-26

---

**You can serve the croc web client against a custom upstream relay by configuring the `RelayHost`, `RelayPassword`, and `AllowedPorts` fields in the `webrelay.Config` struct before passing it to `webrelay.Run` or `webrelay.Handler`.**

The croc secure file transfer tool provides a browser-based web client that normally connects to the default public relay at `croc.schollz.com`. According to the schollz/croc source code, the `webrelay` package implements this functionality as a static-file server combined with a WebSocket-to-TCP bridge, allowing you to redirect traffic to any private relay infrastructure by modifying the runtime configuration.

## Architecture of the WebRelay Package

The `webrelay` package ([[`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go)](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go)) serves the embedded web UI and proxies connections to an upstream TCP relay. It never participates in the croc protocol itself; it merely forwards opaque byte streams between the browser's WebSocket and the relay's TCP ports.

Key components include:

- **`Config` struct** ([`webrelay/webrelay.go:32-42`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go#L32-L42)) – Holds runtime options including `RelayHost`, `RelayPassword`, `AllowedPorts`, `OriginPatterns`, and the embedded static file system.
- **`Handler` function** ([`webrelay/webrelay.go:59-93`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go#L59-L93)) – Normalizes configuration, builds the static file handler, and wires routes for `/healthz`, [`/config.js`](https://github.com/schollz/croc/blob/main//config.js), `/ws`, and the root path into an `http.ServeMux`.
- **`websocket` method** ([`webrelay/webrelay.go:140-220`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go#L140-L220)) – Validates the requested port against `AllowedPorts`, dials the upstream relay, upgrades the HTTP connection to a WebSocket, and proxies bidirectional traffic.
- **`newStaticHandler`** ([`webrelay/webrelay.go:300-318`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go#L300-L318)) – Serves the embedded UI assets from the `webassets` virtual file system.

## Configuring a Custom Upstream Relay

To use a custom upstream relay, you must override the default values in a `webrelay.Config` instance. The defaults are:

- `RelayHost`: `"croc.schollz.com"`
- `RelayPassword`: `"pass123"`
- `AllowedPorts`: `[]string{"9009","9010","9011","9012","9013","9014","9015","9016","9017"}`

Changing these fields directs the WebSocket bridge to forward traffic to your specified relay. The static UI, health checks, and configuration endpoints remain functional regardless of the upstream target.

### Required Configuration Fields

When preparing your configuration, set these fields:

- **`RelayHost`** – The hostname of your custom relay (e.g., `"myrelay.example.com"`). This must be a hostname only, not a URL.
- **`RelayPassword`** – The authentication password your relay expects. Omit or leave empty if the relay does not require authentication.
- **`AllowedPorts`** – A slice of strings representing the TCP ports your relay has open for croc connections (e.g., `[]string{"9009", "9010"}`).

## Implementation Steps

Follow these steps to serve the web client with a custom relay:

1. **Create a `Config` struct** with your relay's hostname and the specific ports you wish to expose.
2. **Set `RelayPassword`** if your relay requires authentication.
3. **Choose an entry point** – Use `webrelay.Run` for a standalone server or `webrelay.Handler` to embed the UI in an existing HTTP mux.
4. **Start the server** – The UI will be available at the configured `ListenAddress`, and the JavaScript client will fetch runtime configuration from [`/config.js`](https://github.com/schollz/croc/blob/main//config.js), which automatically contains your custom relay details.

## Code Examples

### Standalone Server

Use `webrelay.Run` when you want a dedicated process serving the croc web client:

```go
package main

import (
	"context"
	"log"

	"github.com/schollz/croc/v10/src/webrelay"
)

func main() {
	// Define custom configuration for your upstream relay.
	cfg := webrelay.Config{
		ListenAddress: "0.0.0.0:8080",
		RelayHost:     "myrelay.example.com",
		RelayPassword: "my-secret-pass",
		AllowedPorts:  []string{"9009", "9010"},
		OriginPatterns: []string{"https://mydomain.com"},
	}

	// Run blocks until the context is cancelled.
	if err := webrelay.Run(context.Background(), cfg); err != nil {
		log.Fatalf("croc web server failed: %v", err)
	}
}

```

### Embedded in Existing Server

Use `webrelay.Handler` when integrating the croc UI into a larger application:

```go
mux := http.NewServeMux()

// Create the croc web handler with custom relay configuration.
handler, err := webrelay.Handler(cfg)
if err != nil {
	log.Fatalf("failed to create croc web handler: %v", err)
}

// Mount the UI under a sub-path.
mux.Handle("/croc/", http.StripPrefix("/croc", handler))

log.Fatal(http.ListenAndServe(":8080", mux))

```

## Key Source Files

| File | Purpose |
|------|---------|
| [[`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go)](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go) | Core implementation containing `Config`, `Handler`, `Run`, and the WebSocket bridging logic. |
| [[`src/webassets/assets.go`](https://github.com/schollz/croc/blob/main/src/webassets/assets.go)](https://github.com/schollz/croc/blob/main/src/webassets/assets.go) | Embeds the static HTML, JavaScript, and assets served to browsers. |
| [[`main.go`](https://github.com/schollz/croc/blob/main/main.go)](https://github.com/schollz/croc/blob/main/main.go) | CLI entry point that parses flags and invokes `webrelay.Run` when the `--web` flag is present. |
| [[`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go)](https://github.com/schollz/croc/blob/main/src/croc/croc.go) | High-level transfer logic for the standard CLI client, useful for understanding relay protocol requirements. |

## Summary

- The `webrelay` package combines a static-file server with a WebSocket-to-TCP bridge to enable browser-based croc transfers.
- Set the **`RelayHost`**, **`RelayPassword`**, and **`AllowedPorts`** fields in `webrelay.Config` to redirect traffic to a custom upstream relay.
- Use **`webrelay.Run`** for standalone deployments or **`webrelay.Handler`** to embed the UI in existing Go HTTP servers.
- The web client automatically discovers relay configuration via the [`/config.js`](https://github.com/schollz/croc/blob/main//config.js) endpoint generated by the server.

## Frequently Asked Questions

### What is the webrelay package in croc?

The `webrelay` package is a Go library within schollz/croc that serves the browser-based web client. It hosts the static UI files from an embedded filesystem and proxies WebSocket connections to a TCP relay. It handles the `/ws` endpoint for WebSocket upgrades, serves [`/config.js`](https://github.com/schollz/croc/blob/main//config.js) with runtime relay settings, and provides a `/healthz` health check endpoint.

### How do I configure authentication for a custom relay?

Set the **`RelayPassword`** field in your `webrelay.Config` struct to match the password configured on your upstream relay. The default value is `"pass123"`, which matches the public croc relay. If your custom relay requires a different password, you must explicitly set this field before starting the server.

### Can I embed the croc web client in an existing HTTP server?

Yes. Instead of calling `webrelay.Run`, use the **`webrelay.Handler`** function to create an `http.Handler` that you can mount within any existing `http.ServeMux`. This allows you to serve the croc UI under a sub-path or integrate it into a larger web application while still using your custom upstream relay configuration.

### What ports need to be allowlisted for the web client?

The **`AllowedPorts`** slice must contain the string representations of all TCP ports your upstream relay has open for croc connections. The default configuration includes ports 9009 through 9017. If your private relay only listens on specific ports (for example, 9009 and 9010), include only those in your configuration to restrict WebSocket connections to valid relay endpoints.