diff --git a/cmd/root.go b/cmd/root.go
index b2cd594dc3d4c858811fd0930cc65c061abb41cd..7c1785ef03a4630f0f63b3b3714e02b99cf34470 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -352,7 +352,12 @@ func overrideCluster(envName string, clientConfig clientcmd.ClientConfig, overri
 
 	var servers = make(map[string]string)
 	for name, cluster := range rawConfig.Clusters {
-		servers[cluster.Server] = name
+		server, err := utils.NormalizeURL(cluster.Server)
+		if err != nil {
+			return err
+		}
+
+		servers[server] = name
 	}
 
 	//
@@ -366,8 +371,13 @@ func overrideCluster(envName string, clientConfig clientcmd.ClientConfig, overri
 		return err
 	}
 
-	if _, ok := servers[env.Server]; ok {
-		clusterName := servers[env.Server]
+	server, err := utils.NormalizeURL(env.Server)
+	if err != nil {
+		return err
+	}
+
+	if _, ok := servers[server]; ok {
+		clusterName := servers[server]
 		log.Debugf("Overwriting --cluster flag with '%s'", clusterName)
 		overrides.Context.Cluster = clusterName
 		log.Debugf("Overwriting --namespace flag with '%s'", env.Namespace)
diff --git a/metadata/environment.go b/metadata/environment.go
index 18b1b0193b3bfbbed1aab5ee9d87c673ecfe4898..efcb9035193dee15cfe7e41c1b73287ab7294556 100644
--- a/metadata/environment.go
+++ b/metadata/environment.go
@@ -30,6 +30,7 @@ import (
 	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet"
 	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec"
 	param "github.com/ksonnet/ksonnet/metadata/params"
+	"github.com/ksonnet/ksonnet/utils"
 )
 
 const (
@@ -492,6 +493,11 @@ params + {
 }
 
 func generateSpecData(server, namespace string) ([]byte, error) {
+	server, err := utils.NormalizeURL(server)
+	if err != nil {
+		return nil, err
+	}
+
 	// Format the spec json and return; preface keys with 2 space idents.
 	return json.MarshalIndent(EnvironmentSpec{Server: server, Namespace: namespace}, "", "  ")
 }
diff --git a/utils/strings.go b/utils/strings.go
index c15dd077050e02ae252f07ce08ca47eefc52ef79..fdd28a96be9c14f7c45d973d7e51fb835c14b145 100644
--- a/utils/strings.go
+++ b/utils/strings.go
@@ -18,6 +18,8 @@ package utils
 import (
 	"bytes"
 	"strings"
+
+	"github.com/PuerkitoBio/purell"
 )
 
 // IsASCIIIdentifier takes a string and returns true if the string does not
@@ -32,6 +34,14 @@ func IsASCIIIdentifier(s string) bool {
 	return true
 }
 
+// NormalizeURL uses purell's "usually safe normalization" algorithm to
+// normalize URLs. This includes removing dot segments, removing trailing
+// slashes, removing unnecessary escapes, removing default ports, and setting
+// the URL to lowercase.
+func NormalizeURL(s string) (string, error) {
+	return purell.NormalizeURLString(s, purell.FlagsUsuallySafeGreedy)
+}
+
 func PadRows(rows [][]string) (string, error) {
 	maxRowLen := 0
 	for _, row := range rows {
diff --git a/utils/strings_test.go b/utils/strings_test.go
index 821608ab319f038da58f3e8861f25c3150b74f33..3886e28caf5387dfc1dca943d682c80b748a300b 100644
--- a/utils/strings_test.go
+++ b/utils/strings_test.go
@@ -53,6 +53,34 @@ func TestIsASCIIIdentifier(t *testing.T) {
 	}
 }
 
+func TestNormalizeURL(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected string
+	}{
+		{
+			input:    "host/path/./a/b/../c",
+			expected: "host/path/a/c",
+		},
+		{
+			input:    "HTTP://host",
+			expected: "http://host",
+		},
+		{
+			input:    "http://host:80",
+			expected: "http://host",
+		},
+	}
+	for _, test := range tests {
+		normalized, err := NormalizeURL(test.input)
+		if err != nil {
+			t.Error(err)
+		}
+
+		require.EqualValues(t, test.expected, normalized)
+	}
+}
+
 func TestPadRows(t *testing.T) {
 	tests := []struct {
 		input    [][]string