diff --git a/config/config.go b/config/config.go
index 296c9761..942ee876 100644
--- a/config/config.go
+++ b/config/config.go
@@ -293,10 +293,13 @@ func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) {
 	}
 
 	// parse proxy group
+	if err := proxyGroupsDagSort(groupsConfig); err != nil {
+		return nil, err
+	}
 	for idx, mapping := range groupsConfig {
 		groupType, existType := mapping["type"].(string)
 		groupName, existName := mapping["name"].(string)
-		if !existType && existName {
+		if !(existType && existName) {
 			return nil, fmt.Errorf("ProxyGroup %d: missing type or name", idx)
 		}
 
diff --git a/config/utils.go b/config/utils.go
index 54e205f8..7c6b69f3 100644
--- a/config/utils.go
+++ b/config/utils.go
@@ -34,3 +34,128 @@ func or(pointers ...*int) *int {
 	}
 	return pointers[len(pointers)-1]
 }
+
+// Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order.
+// Meanwhile, record the original index in the config file.
+// If loop is detected, return an error with location of loop.
+func proxyGroupsDagSort(groupsConfig []map[string]interface{}) error {
+
+	type Node struct {
+		indegree int
+		// topological order
+		topo int
+		// the origional data in `groupsConfig`
+		data map[string]interface{}
+		// `outdegree` and `from` are used in loop locating
+		outdegree int
+		from      []string
+	}
+
+	graph := make(map[string]*Node)
+
+	// Step 1.1 build dependency graph
+	for idx, mapping := range groupsConfig {
+		// record original order in config file.
+		// this field can be used determinate the display order in FrontEnd.
+		mapping["configIdx"] = idx
+		groupName, existName := mapping["name"].(string)
+		if !existName {
+			return fmt.Errorf("ProxyGroup %d: missing name", idx)
+		}
+		if node, ok := graph[groupName]; ok {
+			if node.data != nil {
+				return fmt.Errorf("ProxyGroup %s: duplicate group name", groupName)
+			}
+			node.data = mapping
+		} else {
+			graph[groupName] = &Node{0, -1, mapping, 0, nil}
+		}
+		proxies, existProxies := mapping["proxies"]
+		if !existProxies {
+			return fmt.Errorf("ProxyGroup %s: the `proxies` field is requried", groupName)
+		}
+		for _, proxy := range proxies.([]interface{}) {
+			proxy := proxy.(string)
+			if node, ex := graph[proxy]; ex {
+				node.indegree++
+			} else {
+				graph[proxy] = &Node{1, -1, nil, 0, nil}
+			}
+		}
+	}
+	// Step 1.2 Topological Sort
+	// topological index of **ProxyGroup**
+	index := 0
+	queue := make([]string, 0)
+	for name, node := range graph {
+		// in the begning, put nodes that have `node.indegree == 0` into queue.
+		if node.indegree == 0 {
+			queue = append(queue, name)
+		}
+	}
+	// every element in queue have indegree == 0
+	for ; len(queue) > 0; queue = queue[1:] {
+		name := queue[0]
+		node := graph[name]
+		if node.data != nil {
+			index++
+			groupsConfig[len(groupsConfig)-index] = node.data
+			for _, proxy := range node.data["proxies"].([]interface{}) {
+				child := graph[proxy.(string)]
+				child.indegree--
+				if child.indegree == 0 {
+					queue = append(queue, proxy.(string))
+				}
+			}
+		}
+		delete(graph, name)
+	}
+
+	// no loop is detected, return sorted ProxyGroup
+	if len(graph) == 0 {
+		return nil
+	}
+
+	// if loop is detected, locate the loop and throw an error
+	// Step 2.1 rebuild the graph, fill `outdegree` and `from` filed
+	for name, node := range graph {
+		if node.data == nil {
+			continue
+		}
+		for _, proxy := range node.data["proxies"].([]interface{}) {
+			node.outdegree++
+			child := graph[proxy.(string)]
+			if child.from == nil {
+				child.from = make([]string, 0, child.indegree)
+			}
+			child.from = append(child.from, name)
+		}
+	}
+	// Step 2.2 remove nodes outside the loop. so that we have only the loops remain in `graph`
+	queue = make([]string, 0)
+	// initialize queue with node have outdegree == 0
+	for name, node := range graph {
+		if node.outdegree == 0 {
+			queue = append(queue, name)
+		}
+	}
+	// every element in queue have outdegree == 0
+	for ; len(queue) > 0; queue = queue[1:] {
+		name := queue[0]
+		node := graph[name]
+		for _, f := range node.from {
+			graph[f].outdegree--
+			if graph[f].outdegree == 0 {
+				queue = append(queue, f)
+			}
+		}
+		delete(graph, name)
+	}
+	// Step 2.3 report the elements in loop
+	loopElements := make([]string, 0, len(graph))
+	for name := range graph {
+		loopElements = append(loopElements, name)
+		delete(graph, name)
+	}
+	return fmt.Errorf("Loop is detected in ProxyGroup, please check following ProxyGroups: %v", loopElements)
+}