diff --git a/client/client.go b/client/client.go
index dca9a73f40458d481e3286e5ba434508cfa09855..f736bdf567ad53e6e396e93a729a4410f82eb894 100644
--- a/client/client.go
+++ b/client/client.go
@@ -78,6 +78,8 @@ type HoppingConfig struct {
 	Obfs4Certs    []string `json:"obfs4_certs"`
 	PortSeed      int64    `json:"port_seed"`
 	PortCount     uint     `json:"port_count"`
+	MinHopPort    uint     `json:"min_hop_port"`
+	MaxHopPort    uint     `json:"max_hop_port"`
 	MinHopSeconds uint     `json:"min_hop_seconds"`
 	HopJitter     uint     `json:"hop_jitter"`
 }
@@ -139,13 +141,14 @@ func generateObfs4Config(config Config) []*Obfs4Config {
 	obfsEndpoints := []*Obfs4Config{}
 
 	if config.HoppingConfig.Enabled {
+		portHopRange := int(config.HoppingConfig.MaxHopPort - config.HoppingConfig.MinHopPort)
 		for i, obfs4Remote := range config.HoppingConfig.Remotes {
 			// We want a non-crypto RNG so that we can share a seed
 			// #nosec G404
 			r := rand.New(rand.NewSource(config.HoppingConfig.PortSeed))
 			for pi := 0; pi < int(config.HoppingConfig.PortCount); pi++ {
-				portOffset := r.Intn(obfsvpn.PortHopRange)
-				addr := net.JoinHostPort(obfs4Remote, fmt.Sprint(portOffset+obfsvpn.MinHopPort))
+				portOffset := r.Intn(portHopRange)
+				addr := net.JoinHostPort(obfs4Remote, fmt.Sprint(portOffset+int(obfsvpn.MinHopPort)))
 				obfsEndpoints = append(obfsEndpoints, &Obfs4Config{
 					Cert:   config.HoppingConfig.Obfs4Certs[i],
 					Remote: addr,
diff --git a/cmd/client/main.go b/cmd/client/main.go
index bbec76debc614f3dd4f073f76de53a5287c689ec..f54b2266548a0b8323369282039b606985a6d950 100644
--- a/cmd/client/main.go
+++ b/cmd/client/main.go
@@ -37,6 +37,8 @@ func main() {
 		minHopSeconds         uint  = 5
 		portSeed              int64 = 1
 		portCount             uint  = 100
+		minHopPort            uint  = obfsvpn.MinHopPort
+		maxHopPort            uint  = obfsvpn.MaxHopPort
 		proxyPort             string
 		proxyHost             string
 		obfs4Certs            string
@@ -66,11 +68,13 @@ func main() {
 	flags.StringVar(&obfs4Remotes, "r", obfs4Remotes, "The remote obfs4 endpoint ips (no port) separated by commas. If hopping is not enabled only the first cert will be used")
 	flags.StringVar(&obfs4RemotePort, "rp", obfs4RemotePort, "The remote obfs4 endpoint port to use. Only applicable to NON-hopping")
 	flags.UintVar(&portCount, "pc", portCount, "The number of ports to try for each remote. Only applicable to hopping")
+	flags.UintVar(&minHopPort, "min-port", minHopPort, "The lower limit of the port range used for port hopping")
+	flags.UintVar(&maxHopPort, "max-port", maxHopPort, "The upper limit of the port range used for port hopping")
 	flags.Int64Var(&portSeed, "ps", portSeed, "The random seed to generate ports from. Only applicable to hopping")
 	flags.UintVar(&minHopSeconds, "m", minHopSeconds, "The minimun number of seconds to wait before hopping. Only applicable to hopping")
 	flags.UintVar(&hopJitter, "j", hopJitter, "A random range to wait (on top of the minimum) seconds before hopping. Only applicable to hopping")
-	flags.StringVar(&proxyPort, "p", proxyPort, "The port for the local proxy (default: 8080)")
-	flags.StringVar(&proxyHost, "i", proxyHost, "The host for the local proxy (default: localhost)")
+	flags.StringVar(&proxyPort, "p", proxyPort, "The port for the local proxy")
+	flags.StringVar(&proxyHost, "i", proxyHost, "The host for the local proxy")
 	err := flags.Parse(os.Args[1:])
 	if err != nil {
 		logger.Fatalf("error parsing flags: %v", err)
@@ -87,6 +91,10 @@ func main() {
 		logger.Fatalf("number of obfs4 remotes must match number of obfs4 certs")
 	}
 
+	if (maxHopPort - minHopPort) < portCount {
+		logger.Fatalf("configured port hopping boundaries (range of %d ports) cannot be smaller than port count %d.", (maxHopPort - minHopPort), portCount)
+	}
+
 	proxyAddr := net.JoinHostPort(proxyHost, proxyPort)
 
 	logger.Printf("proxyAddr: %+v", proxyAddr)
@@ -124,6 +132,8 @@ func main() {
 				Obfs4Certs:    obfs4CertsList,
 				PortSeed:      portSeed,
 				PortCount:     portCount,
+				MinHopPort:    minHopPort,
+				MaxHopPort:    maxHopPort,
 				MinHopSeconds: minHopSeconds,
 				HopJitter:     hopJitter,
 			},
diff --git a/cmd/server/main.go b/cmd/server/main.go
index da196e2ecaaeaf04d18b15b66d5d7c38b3cbdd95..00f562b8ea6c23ef1a152e910f69c228d5fdde31 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -47,6 +47,8 @@ var (
 	port                  = "port"
 	portSeed              = "port-seed"
 	portCount             = "port-count"
+	minHopPort            = "min-hop-port"
+	maxHopPort            = "max-hop-port"
 	stateDir              = "state"
 	remote                = "remote"
 	verbose               = "v"
@@ -81,6 +83,8 @@ type config struct {
 	kcpMTU                int
 	portSeed              int64
 	portCount             uint
+	minHopPort            uint
+	maxHopPort            uint
 	stateDir              string
 	remote                string
 	verbose               bool
@@ -127,11 +131,13 @@ func newConfigFromViper(logger *log.Logger) *config {
 		kcpDisableFlowControl: viper.GetBool(kcpDisableFlowControl),
 		kcpMTU:                viper.GetInt(kcpMTU),
 
-		portSeed:  viper.GetInt64(portSeed),
-		portCount: viper.GetUint(portCount),
-		stateDir:  getStateDir(),
-		remote:    viper.GetString(remote),
-		verbose:   viper.GetBool(verbose),
+		portSeed:   viper.GetInt64(portSeed),
+		portCount:  viper.GetUint(portCount),
+		minHopPort: viper.GetUint(minHopPort),
+		maxHopPort: viper.GetUint(maxHopPort),
+		stateDir:   getStateDir(),
+		remote:     viper.GetString(remote),
+		verbose:    viper.GetBool(verbose),
 	}
 
 	// Sanity check on the configuration
@@ -234,8 +240,10 @@ func main() {
 	flag.Int(kcpResend, obfsvpn.DefaultResend, "KCP Resend")
 	flag.Bool(kcpDisableFlowControl, obfsvpn.DefaultDisableFlowControl, "KCP DisableFlowControl")
 	flag.Int(kcpMTU, obfsvpn.DefaultMTU, "KCP MTU")
-	flag.Int64(portSeed, 1, "The seed to use for generating random ports")
-	flag.Uint(portCount, 100, "The number of random ports to listen on")
+	flag.Int64(portSeed, 1, "The seed to use for generating random ports for port hopping")
+	flag.Uint(portCount, 100, "The number of random ports to listen on for port hopping")
+	flag.Uint(minHopPort, obfsvpn.MinHopPort, "The lower limit of the available port range for port hopping")
+	flag.Uint(maxHopPort, obfsvpn.MaxHopPort, "The upper limit of the available port range for port hopping")
 	flag.String(stateDir, "", "A directory in which to store bridge state")
 
 	flag.Bool(verbose, false, "Enable verbose logging")
@@ -263,6 +271,8 @@ func main() {
 		// AutomaticEnv doesn't do well with dashes
 		err = viper.BindEnv(portSeed, "OBFSVPN_PORT_SEED")
 		err = viper.BindEnv(portCount, "OBFSVPN_PORT_COUNT")
+		err = viper.BindEnv(minHopPort, "OBFSVPN_MIN_HOP_PORT")
+		err = viper.BindEnv(maxHopPort, "OBFSVPN_MAX_HOP_PORT")
 	}
 	if err != nil {
 		log.Fatalf("Error calling BindEnv: %v", err)
@@ -291,6 +301,10 @@ func main() {
 		debug.SetOutput(os.Stderr)
 	}
 
+	if (cfg.maxHopPort - cfg.minHopPort) < cfg.portCount {
+		logger.Fatalf("configured port hopping boundaries (range of %d ports) cannot be smaller than port count %d.", (cfg.maxHopPort - cfg.minHopPort), cfg.portCount)
+	}
+
 	if cfg.cfgFile == "" {
 		stateDir := getStateDir()
 		if stateDir != "" {
@@ -336,6 +350,8 @@ func main() {
 		StateDir:    cfg.stateDir,
 		PortSeed:    cfg.portSeed,
 		PortCount:   cfg.portCount,
+		MinHopPort:  cfg.minHopPort,
+		MaxHopPort:  cfg.maxHopPort,
 		OpenvpnAddr: cfg.remote,
 		KCPConfig:   kcpConfig,
 		QUICConfig:  quicConfig,
diff --git a/obfsvpn/hop.go b/obfsvpn/hop.go
index 2404e2d7c7e260461446b1038c2e319c81f61455..6a9179e521d6a6eb5fab77d1e16d20ad17f5efe5 100644
--- a/obfsvpn/hop.go
+++ b/obfsvpn/hop.go
@@ -1,8 +1,6 @@
 package obfsvpn
 
-const MinHopPort = 49152
-const MaxHopPort = 65535
-
-var PortHopRange = MaxHopPort - MinHopPort
+const MinHopPort uint = 49152
+const MaxHopPort uint = 65535
 
 const MaxUDPLen uint = 65507
diff --git a/server/tcpserver.go b/server/tcpserver.go
index 50b448870563840d20a765db09a38e5cf213d717..7ed5b9e77398243c3e9cd55f719c808626480b1a 100644
--- a/server/tcpserver.go
+++ b/server/tcpserver.go
@@ -36,6 +36,8 @@ type ServerConfig struct {
 	HoppingEnabled  bool
 	PortSeed        int64
 	PortCount       uint
+	MinHopPort      uint
+	MaxHopPort      uint
 	KCPConfig       obfsvpn.KCPConfig
 	QUICConfig      obfsvpn.QUICConfig
 }
diff --git a/server/udpserver.go b/server/udpserver.go
index 561ad26daa3e91b71a7e4828400e4593978c577c..70de25b3c243b326dc50e237f1b5bb9927835c82 100644
--- a/server/udpserver.go
+++ b/server/udpserver.go
@@ -54,9 +54,10 @@ func (s *UDPServer) Start() error {
 
 	if s.cfg.HoppingEnabled {
 		listeners = make([]net.Listener, s.cfg.PortCount)
+		portHopRange := int(s.cfg.MaxHopPort - s.cfg.MinHopPort)
 		for i := 0; i < int(s.cfg.PortCount); i++ {
-			portOffset := r.Intn(obfsvpn.PortHopRange)
-			addr := net.JoinHostPort(s.cfg.Obfs4ListenIP, fmt.Sprint(portOffset+obfsvpn.MinHopPort))
+			portOffset := r.Intn(portHopRange)
+			addr := net.JoinHostPort(s.cfg.Obfs4ListenIP, fmt.Sprint(portOffset+int(s.cfg.MinHopPort)))
 			listeners[i], err = listenConfig.Listen(s.ctx, addr)
 
 			if err != nil {