You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ra...@apache.org on 2021/02/19 20:12:49 UTC

[trafficcontrol] branch master updated: Remove cacheurl from Delivery Services (#4644)

This is an automated email from the ASF dual-hosted git repository.

rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 56ef357  Remove cacheurl from Delivery Services (#4644)
56ef357 is described below

commit 56ef357d47da9918cb6d0b903f06391c18fd9b6a
Author: Steve Hamrick <sh...@users.noreply.github.com>
AuthorDate: Fri Feb 19 13:12:36 2021 -0700

    Remove cacheurl from Delivery Services (#4644)
    
    * Remove from types and fix compile errors
    
    * Remove remaining traces
    
    * Missed some files
    
    * Remove from TP
    
    * Dont remove from v11 DS
    
    * Remove reference to cacheurl
    
    * Fully remove cache_url, fix tests
    
    * Always return
    
    * Fix merge issue
    
    * Fix build error
    
    * Remove route
    
    * Remove field from api v3
    
    * Fix v1 issues
    
    * Dont remove from v11 DS
    
    * Fix build error
    
    * Remove route
    
    * Fix merge
    
    * Fix Merge issues
    
    * Fix merge issues
    
    * Fix more merge issues
    
    * Fix build
    
    * Deprecate CacheURL, revert some refactors
    
    * Remove un-needed result
    
    * Handle old versions without issue.
    
    * encoding/json handles this for us.
    
    * Remove from docs
    
    * Remove from v4 fixtures
    
    * Readd userdeliveryservice test
    
    * Unremove Cacheurl, add DS V40
    
    * Point to v4
    
    * Readd docs
    
    * Add V40 DS
    
    * Dont need compat struct
    
    * Dont need this link
    
    * Upgrade atscfg to use latest DS
    
    * Use correct DS version in v4 tests
    
    * Readd symlink for now
    
    * Upgrade atscfg server version
    
    * Fix test
    
    * Fix build
    
    * Fix Integration tests
    
    * Missed a spot
    
    * v4 tests use v4
    
    * Update headers and dont use new v4 route
    
    * Use proper struct and readd more cacheurl functionality
    
    * Remove from types and fix compile errors
    
    * Remove remaining traces
    
    * Missed some files
    
    * Dont remove from v11 DS
    
    * Fully remove cache_url, fix tests
    
    * Fix merge issue
    
    * Fix build error
    
    * Remove route
    
    * Fix v1 issues
    
    * Fix build error
    
    * Remove route
    
    * Fix merge
    
    * Fix Merge issues
    
    * Fix merge issues
    
    * Fix more merge issues
    
    * Handle old versions without issue.
    
    * Remove from docs
    
    * Readd userdeliveryservice test
    
    * Unremove Cacheurl, add DS V40
    
    * Readd docs
    
    * Add V40 DS
    
    * Dont need this link
    
    * Readd symlink for now
    
    * Upgrade atscfg server version
    
    * v4 tests use v4
    
    * Update headers and dont use new v4 route
    
    * Revert "Merge branch 'cacheurl' of github.com:shamrickus/trafficcontrol into cacheurl"
    
    This reverts commit 2a92ba59e59bc01b0b8b16c63c2d59b500fcaea8, reversing
    changes made to 1f4ec086db7b491cb351f8ac82c9a5040fc562ea.
    
    * Fix merge issues, use v4 methods in v4 tests, rename some v4 methods
    
    * More merge fixes
    
    * Missed some conversions.
    
    * Fix tests
    
    * Move ds functions
    
    * Add comments & clean up
    
    * Handle old versions of cachegroup post
    
    * Remove symlink
    
    * Fix Merge
    
    * Rename ds
    
    * Correctly cast ds based on version
---
 docs/source/api/v4/deliveryservice_requests.rst    |  25 -
 .../api/v4/deliveryservice_requests_id_assign.rst  |   5 -
 .../api/v4/deliveryservice_requests_id_status.rst  |   5 -
 docs/source/api/v4/deliveryservices.rst            |  15 -
 docs/source/api/v4/deliveryservices_id.rst         |   5 -
 docs/source/api/v4/deliveryservices_id_safe.rst    |   5 -
 docs/source/api/v4/servers_id_deliveryservices.rst |   5 -
 docs/source/overview/delivery_services.rst         |   2 +-
 infrastructure/cdn-in-a-box/enroller/enroller.go   |   2 +-
 infrastructure/docker/traffic_router/run.sh        |   6 +-
 lib/go-atscfg/atscfg.go                            |  11 +-
 lib/go-atscfg/atscfg_test.go                       |   8 +-
 lib/go-atscfg/cacheurldotconfig.go                 | 175 ------
 lib/go-atscfg/cacheurldotconfig_test.go            | 195 ------
 lib/go-atscfg/meta.go                              |   6 -
 lib/go-atscfg/regexremapdotconfig.go               |   4 -
 lib/go-atscfg/regexremapdotconfig_test.go          |   4 -
 lib/go-atscfg/remapdotconfig.go                    |  50 +-
 lib/go-atscfg/remapdotconfig_test.go               | 585 +-----------------
 lib/go-tc/deliveryservice_requests.go              |  26 +-
 lib/go-tc/deliveryservices.go                      | 425 +++-----------
 traffic_ops/client                                 |   1 -
 .../testing/api/v1/federation_users_test.go        |   2 +-
 traffic_ops/testing/api/v1/federations_test.go     |   2 +-
 .../api/v4/cachegroupsdeliveryservices_test.go     |   4 +-
 traffic_ops/testing/api/v4/cookie_test.go          |   2 +-
 .../testing/api/v4/deliveryservices_test.go        | 128 ++--
 .../testing/api/v4/deliveryserviceservers_test.go  |  16 +-
 traffic_ops/testing/api/v4/servers_test.go         |  10 +-
 .../testing/api/v4/serverservercapability_test.go  |   4 +-
 traffic_ops/testing/api/v4/tc-fixtures.json        |  16 -
 .../testing/api/v4/topologies_queue_update_test.go |   2 +-
 traffic_ops/testing/api/v4/topologies_test.go      |   4 +-
 traffic_ops/testing/api/v4/traffic_control_test.go |   2 +-
 .../traffic_ops_golang/cachegroup/dspost.go        |  94 ++-
 .../traffic_ops_golang/dbhelpers/db_helpers.go     |   2 +-
 .../deliveryservice/deliveryservices.go            | 653 +++++++++++++++------
 .../deliveryservice/request/requests_test.go       |   2 +-
 .../deliveryservice/request/validate.go            |   3 +-
 .../traffic_ops_golang/deliveryservice/safe.go     |   8 +-
 .../deliveryservice/servers/servers.go             |  12 +-
 traffic_ops/traffic_ops_golang/routing/routes.go   |  12 +-
 traffic_ops/v4-client/deliveryservice.go           |  83 ++-
 traffic_ops_ort/atstccfg/cfgfile/cfgfile_test.go   |  61 +-
 traffic_ops_ort/atstccfg/cfgfile/routing.go        |   2 -
 traffic_ops_ort/atstccfg/cfgfile/wrappers.go       |   8 -
 traffic_ops_ort/atstccfg/toreq/toreq.go            |   2 +-
 traffic_ops_ort/atstccfg/toreqnew/toreqnew.go      |   6 +-
 .../testing/ort-tests/tcdata/steeringtargets.go    |   2 +-
 .../form.deliveryService.DNS.tpl.html              |  28 -
 .../form.deliveryService.HTTP.tpl.html             |  28 -
 .../form.deliveryService.anyMap.tpl.html           |  28 -
 52 files changed, 916 insertions(+), 1875 deletions(-)

diff --git a/docs/source/api/v4/deliveryservice_requests.rst b/docs/source/api/v4/deliveryservice_requests.rst
index c94266c..91d5a21 100644
--- a/docs/source/api/v4/deliveryservice_requests.rst
+++ b/docs/source/api/v4/deliveryservice_requests.rst
@@ -83,11 +83,6 @@ Response Structure
 
 	:active:                                A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:              A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                              A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                             The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                                 The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                               Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
@@ -304,11 +299,6 @@ Request Structure
 
 	:active:                                        A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:      A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                                      A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                     The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                         The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                       Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
@@ -493,11 +483,6 @@ Response Structure
 
 	:active:                                A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:              A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                              A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                             The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                                 The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                               Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
@@ -716,11 +701,6 @@ Request Structure
 
 	:active:                        A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:      A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                      A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                     The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                         The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                       Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
@@ -925,11 +905,6 @@ Response Structure
 
 	:active:                                        A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:      A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                                      A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                             The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                                 The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                               Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/api/v4/deliveryservice_requests_id_assign.rst b/docs/source/api/v4/deliveryservice_requests_id_assign.rst
index fa8786e..14bcf41 100644
--- a/docs/source/api/v4/deliveryservice_requests_id_assign.rst
+++ b/docs/source/api/v4/deliveryservice_requests_id_assign.rst
@@ -58,11 +58,6 @@ Response Structure
 
 	:active:                        A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:      A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                      A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                     The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                         The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                       Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/api/v4/deliveryservice_requests_id_status.rst b/docs/source/api/v4/deliveryservice_requests_id_status.rst
index a905317..3fe18b2 100644
--- a/docs/source/api/v4/deliveryservice_requests_id_status.rst
+++ b/docs/source/api/v4/deliveryservice_requests_id_status.rst
@@ -59,11 +59,6 @@ Response Structure
 
 	:active:                        A boolean that defines :ref:`ds-active`.
 	:anonymousBlockingEnabled:      A boolean that defines :ref:`ds-anonymous-blocking`
-	:cacheurl:                      A :ref:`ds-cacheurl`
-
-		.. deprecated:: ATCv3.0
-			This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 	:ccrDnsTtl:                     The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 	:cdnId:                         The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 	:cdnName:                       Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/api/v4/deliveryservices.rst b/docs/source/api/v4/deliveryservices.rst
index a2bcaf7..9703093 100644
--- a/docs/source/api/v4/deliveryservices.rst
+++ b/docs/source/api/v4/deliveryservices.rst
@@ -73,11 +73,6 @@ Response Structure
 ------------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 :cdnName:                   Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
@@ -268,11 +263,6 @@ Request Structure
 -----------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 :checkPath:                 A :ref:`ds-check-path`
@@ -383,11 +373,6 @@ Response Structure
 ------------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 :cdnName:                   Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/api/v4/deliveryservices_id.rst b/docs/source/api/v4/deliveryservices_id.rst
index 562cd2a..e5cc669 100644
--- a/docs/source/api/v4/deliveryservices_id.rst
+++ b/docs/source/api/v4/deliveryservices_id.rst
@@ -31,11 +31,6 @@ Request Structure
 -----------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 
diff --git a/docs/source/api/v4/deliveryservices_id_safe.rst b/docs/source/api/v4/deliveryservices_id_safe.rst
index 48d6a5d..62ee48e 100644
--- a/docs/source/api/v4/deliveryservices_id_safe.rst
+++ b/docs/source/api/v4/deliveryservices_id_safe.rst
@@ -63,11 +63,6 @@ Response Structure
 ------------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 :cdnName:                   Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/api/v4/servers_id_deliveryservices.rst b/docs/source/api/v4/servers_id_deliveryservices.rst
index 4f6b73a..b1c8ec6 100644
--- a/docs/source/api/v4/servers_id_deliveryservices.rst
+++ b/docs/source/api/v4/servers_id_deliveryservices.rst
@@ -69,11 +69,6 @@ Response Structure
 ------------------
 :active:                   A boolean that defines :ref:`ds-active`.
 :anonymousBlockingEnabled: A boolean that defines :ref:`ds-anonymous-blocking`
-:cacheurl:                 A :ref:`ds-cacheurl`
-
-	.. deprecated:: ATCv3.0
-		This field has been deprecated in Traffic Control 3.x and is subject to removal in Traffic Control 4.x or later
-
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for legacy reasons
 :cdnId:                     The integral, unique identifier of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
 :cdnName:                   Name of the :ref:`ds-cdn` to which the :term:`Delivery Service` belongs
diff --git a/docs/source/overview/delivery_services.rst b/docs/source/overview/delivery_services.rst
index f16bf1d..6b2917c 100644
--- a/docs/source/overview/delivery_services.rst
+++ b/docs/source/overview/delivery_services.rst
@@ -54,7 +54,7 @@ Enables/Disables blocking of anonymized IP address - proxies, :abbr:`TOR (The On
 Cache URL Expression
 --------------------
 .. deprecated:: 3.0
-	This feature is no longer supported by :abbr:`ATS (Apache Traffic Server)` and consequently it will be removed from Traffic Control in the future.
+	This feature is no longer supported by :abbr:`ATS (Apache Traffic Server)` and consequently it will be removed from Traffic Control in the future. Current plans are to remove after ATC 5.X is no longer supported.
 
 Manipulates the cache key of the incoming requests. Normally, the cache key is the :term:`Origin` domain. This can be changed so that multiple services can share a cache key, can also be used to preserve cached content if service origin is changed.
 
diff --git a/infrastructure/cdn-in-a-box/enroller/enroller.go b/infrastructure/cdn-in-a-box/enroller/enroller.go
index 5a17050..acee786 100644
--- a/infrastructure/cdn-in-a-box/enroller/enroller.go
+++ b/infrastructure/cdn-in-a-box/enroller/enroller.go
@@ -270,7 +270,7 @@ func enrollDeliveryServiceServer(toSession *session, r io.Reader) error {
 	}
 
 	params := url.Values{"xmlId": []string{dss.XmlId}}
-	dses, _, err := toSession.GetDeliveryServicesV30WithHdr(nil, params)
+	dses, _, err := toSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		return err
 	}
diff --git a/infrastructure/docker/traffic_router/run.sh b/infrastructure/docker/traffic_router/run.sh
index 3c03f6c..e610450 100755
--- a/infrastructure/docker/traffic_router/run.sh
+++ b/infrastructure/docker/traffic_router/run.sh
@@ -69,12 +69,12 @@ init() {
 	TMP_DELIVERY_SERVICE_TYPE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TRAFFIC_OPS_URI/api/1.2/types.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="DNS"]; print match[0]')"
 	echo "Got delivery service type ID: $TMP_DELIVERY_SERVICE_TYPE_ID"
 
-	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=c2-service" --data-urlencode "ds.display_name=C2 Service" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispersion=1 [...]
+	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=c2-service" --data-urlencode "ds.display_name=C2 Service" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispersion=1 [...]
 
 	TMP_DELIVERY_SERVICE_LIVE_TYPE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TRAFFIC_OPS_URI/api/1.2/types.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="DNS_LIVE_NATNL"]; print match[0]')"
 	echo "Got delivery service live type ID: $TMP_DELIVERY_SERVICE_LIVE_TYPE_ID"
 
-	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=ds2-live" --data-urlencode "ds.display_name=DS2 Live" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_LIVE_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispersion= [...]
+	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=ds2-live" --data-urlencode "ds.display_name=DS2 Live" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_LIVE_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispersion= [...]
 
 	TMP_DELIVERY_SERVICE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TRAFFIC_OPS_URI/api/1.2/deliveryservices.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["xmlId"]=="c2-service"]; print match[0]')"
 	echo "Got delivery ID: $TMP_DELIVERY_SERVICE_ID"
@@ -82,7 +82,7 @@ init() {
 	TMP_DELIVERY_SERVICE_HTTP_TYPE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TRAFFIC_OPS_URI/api/1.2/types.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="HTTP"]; print match[0]')"
 	echo "Got delivery service http type ID: $TMP_DELIVERY_SERVICE_HTTP_TYPE_ID"
 
-	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=c3-service" --data-urlencode "ds.display_name=C3 Service" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_HTTP_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispers [...]
+	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "ds.xml_id=c3-service" --data-urlencode "ds.display_name=C3 Service" --data-urlencode "ds.cdn_id=$TMP_CDN_ID" --data-urlencode "ds.type=$TMP_DELIVERY_SERVICE_HTTP_TYPE_ID"  --data-urlencode "ds.protocol=0" --data-urlencode "ds.dscp=0" --data-urlencode "ds.signed=0" --data-urlencode "ds.qstring_ignore=0" --data-urlencode "ds.geo_limit=0" --data-urlencode "ds.http_bypass_fqdn=" --data-urlencode "ds.initial_dispers [...]
 
 	TMP_DELIVERY_SERVICE_HTTP_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TRAFFIC_OPS_URI/api/1.2/deliveryservices.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["xmlId"]=="c3-service"]; print match[0]')"
 	echo "Got delivery http ID: $TMP_DELIVERY_SERVICE_HTTP_ID"
diff --git a/lib/go-atscfg/atscfg.go b/lib/go-atscfg/atscfg.go
index c1c9e42..360b44e 100644
--- a/lib/go-atscfg/atscfg.go
+++ b/lib/go-atscfg/atscfg.go
@@ -50,15 +50,15 @@ type ServerCapability string
 // Server is a tc.Server for the latest lib/go-tc and traffic_ops/vx-client type.
 // This allows atscfg to not have to change the type everywhere it's used, every time ATC changes the base type,
 // but to only have to change it here, and the places where breaking symbol changes were made.
-type Server tc.ServerV30
+type Server tc.ServerV40
 
 // DeliveryService is a tc.DeliveryService for the latest lib/go-tc and traffic_ops/vx-client type.
 // This allows atscfg to not have to change the type everywhere it's used, every time ATC changes the base type,
 // but to only have to change it here, and the places where breaking symbol changes were made.
-type DeliveryService tc.DeliveryServiceNullableV30
+type DeliveryService tc.DeliveryServiceV4
 
 // ToDeliveryServices converts a slice of the latest lib/go-tc and traffic_ops/vx-client type to the local alias.
-func ToDeliveryServices(dses []tc.DeliveryServiceNullableV30) []DeliveryService {
+func ToDeliveryServices(dses []tc.DeliveryServiceV4) []DeliveryService {
 	ad := []DeliveryService{}
 	for _, ds := range dses {
 		ad = append(ad, DeliveryService(ds))
@@ -75,13 +75,14 @@ func OldToDeliveryServices(dses []tc.DeliveryServiceNullable) []DeliveryService
 				DeliveryServiceNullableV15: tc.DeliveryServiceNullableV15(ds),
 			},
 		}
-		ad = append(ad, DeliveryService(upgradedDS))
+
+		ad = append(ad, DeliveryService(upgradedDS.UpgradeToV4()))
 	}
 	return ad
 }
 
 // ToServers converts a slice of the latest lib/go-tc and traffic_ops/vx-client type to the local alias.
-func ToServers(servers []tc.ServerV30) []Server {
+func ToServers(servers []tc.ServerV40) []Server {
 	as := []Server{}
 	for _, sv := range servers {
 		as = append(as, Server(sv))
diff --git a/lib/go-atscfg/atscfg_test.go b/lib/go-atscfg/atscfg_test.go
index 806bd33..513c7ec 100644
--- a/lib/go-atscfg/atscfg_test.go
+++ b/lib/go-atscfg/atscfg_test.go
@@ -119,9 +119,11 @@ func setIP6(sv *Server, ip6Address string) {
 }
 
 func setIPInfo(sv *Server, interfaceName string, ipAddress string, ip6Address string) {
-	sv.Interfaces = []tc.ServerInterfaceInfo{
-		tc.ServerInterfaceInfo{
-			Name: interfaceName,
+	sv.Interfaces = []tc.ServerInterfaceInfoV40{
+		tc.ServerInterfaceInfoV40{
+			ServerInterfaceInfo: tc.ServerInterfaceInfo{
+				Name: interfaceName,
+			},
 		},
 	}
 	if ipAddress != "" {
diff --git a/lib/go-atscfg/cacheurldotconfig.go b/lib/go-atscfg/cacheurldotconfig.go
deleted file mode 100644
index 557a5ce..0000000
--- a/lib/go-atscfg/cacheurldotconfig.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package atscfg
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/apache/trafficcontrol/lib/go-tc"
-)
-
-const ContentTypeCacheURLDotConfig = ContentTypeTextASCII
-const LineCommentCacheURLDotConfig = LineCommentHash
-
-func MakeCacheURLDotConfig(
-	fileName string,
-	server *Server,
-	deliveryServices []DeliveryService,
-	deliveryServiceServers []tc.DeliveryServiceServer,
-	hdrComment string,
-) (Cfg, error) {
-	warnings := []string{}
-
-	if server.CDNName == nil {
-		return Cfg{}, makeErr(warnings, "server missing CDNName")
-	}
-
-	dsIDs := map[int]struct{}{}
-	for _, ds := range deliveryServices {
-		if ds.ID != nil {
-			dsIDs[*ds.ID] = struct{}{} // TODO warn?
-		}
-	}
-
-	dss := filterDSS(deliveryServiceServers, dsIDs, nil)
-
-	dssMap := map[int][]int{} // map[dsID]serverID
-	for _, dss := range dss {
-		if dss.Server == nil || dss.DeliveryService == nil {
-			warnings = append(warnings, "Delivery Service Servers had nil entries, skipping!")
-			continue
-		}
-		dssMap[*dss.DeliveryService] = append(dssMap[*dss.DeliveryService], *dss.Server)
-	}
-
-	dsesWithServers := []DeliveryService{}
-	for _, ds := range deliveryServices {
-		if ds.ID == nil {
-			warnings = append(warnings, "Delivery Service had nil id, skipping!")
-			continue
-		}
-		// ANY_MAP and STEERING DSes don't have origins, and thus can't be put into the cacheurl config.
-		if ds.Type != nil && (*ds.Type == tc.DSTypeAnyMap || *ds.Type == tc.DSTypeSteering) {
-			continue
-		}
-		if len(dssMap[*ds.ID]) == 0 && ds.Topology == nil {
-			continue
-		}
-		dsesWithServers = append(dsesWithServers, ds)
-	}
-
-	dses, dsWarns := deliveryServicesToCacheURLDSes(dsesWithServers)
-	warnings = append(warnings, dsWarns...)
-
-	text := makeHdrComment(hdrComment)
-
-	if fileName == "cacheurl_qstring.config" { // This is the per remap drop qstring w cacheurl use case, the file is the same for all remaps
-		text += `http://([^?]+)(?:\?|$)  http://$1` + "\n"
-		text += `https://([^?]+)(?:\?|$)  https://$1` + "\n"
-
-		return Cfg{
-			Text:        text,
-			ContentType: ContentTypeCacheURLDotConfig,
-			LineComment: LineCommentCacheURLDotConfig,
-			Warnings:    warnings,
-		}, nil
-	}
-
-	if fileName == "cacheurl.config" { // this is the global drop qstring w cacheurl use case
-		seenOrigins := map[string]struct{}{}
-		for dsName, ds := range dses {
-			if ds.QStringIgnore != 1 {
-				continue
-			}
-			if _, ok := seenOrigins[ds.OrgServerFQDN]; ok {
-				continue
-			}
-			org := ds.OrgServerFQDN
-
-			scheme := "https://"
-			if !strings.HasPrefix(org, scheme) {
-				scheme = "http://"
-			}
-
-			if !strings.HasPrefix(org, scheme) {
-				// TODO determine if we should return an empty config here. A bad DS should not break config gen, and MUST NOT for self-service
-				warnings = append(warnings, "ds '"+string(dsName)+"' origin '"+org+"' with no scheme! cacheurl.config will likely be malformed!")
-			}
-
-			fqdnPath := strings.TrimPrefix(org, scheme)
-
-			text += scheme + `(` + fqdnPath + `/[^?]+)(?:\?|$)  ` + scheme + `$1` + "\n"
-
-			seenOrigins[ds.OrgServerFQDN] = struct{}{}
-		}
-		text = strings.Replace(text, `__RETURN__`, "\n", -1)
-		return Cfg{
-			Text:        text,
-			ContentType: ContentTypeCacheURLDotConfig,
-			LineComment: LineCommentCacheURLDotConfig,
-			Warnings:    warnings,
-		}, nil
-	}
-
-	// TODO verify prefix and suffix exist, and warn if they don't? Perl doesn't
-	dsName := tc.DeliveryServiceName(strings.TrimSuffix(strings.TrimPrefix(fileName, "cacheurl_"), ".config"))
-
-	ds, ok := dses[dsName]
-	if !ok {
-		warnings = append(warnings, "ds '"+string(dsName)+"' not found, not creating in cacheurl config!")
-	} else {
-		text += ds.CacheURL + "\n"
-		text = strings.Replace(text, `__RETURN__`, "\n", -1)
-	}
-	return Cfg{
-		Text:        text,
-		ContentType: ContentTypeCacheURLDotConfig,
-		LineComment: LineCommentCacheURLDotConfig,
-		Warnings:    warnings,
-	}, nil
-}
-
-type cacheURLDS struct {
-	OrgServerFQDN string
-	QStringIgnore int
-	CacheURL      string
-}
-
-// DeliveryServicesToCacheURLDSes returns the "CacheURLDS" map, and any warnings.
-func deliveryServicesToCacheURLDSes(dses []DeliveryService) (map[tc.DeliveryServiceName]cacheURLDS, []string) {
-	warnings := []string{}
-	sDSes := map[tc.DeliveryServiceName]cacheURLDS{}
-	for _, ds := range dses {
-		if ds.OrgServerFQDN == nil || ds.QStringIgnore == nil || ds.XMLID == nil || ds.Active == nil {
-			warnings = append(warnings, fmt.Sprintf("atscfg.DeliveryServicesToCacheURLDSes got DS %+v with nil values! Skipping!", ds))
-			continue
-		}
-		if !*ds.Active {
-			continue
-		}
-		sds := cacheURLDS{OrgServerFQDN: *ds.OrgServerFQDN, QStringIgnore: *ds.QStringIgnore}
-		if ds.CacheURL != nil {
-			sds.CacheURL = *ds.CacheURL
-		}
-		sDSes[tc.DeliveryServiceName(*ds.XMLID)] = sds
-	}
-	return sDSes, warnings
-}
diff --git a/lib/go-atscfg/cacheurldotconfig_test.go b/lib/go-atscfg/cacheurldotconfig_test.go
deleted file mode 100644
index 20aab11..0000000
--- a/lib/go-atscfg/cacheurldotconfig_test.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package atscfg
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/apache/trafficcontrol/lib/go-util"
-)
-
-func TestMakeCacheURLDotConfigWithDS(t *testing.T) {
-	server := makeGenericServer()
-	cdnName := "mycdn"
-	server.CDNName = &cdnName
-
-	hdr := "myHeaderComment"
-
-	fileName := "cacheurl_myds.config"
-
-	ds0 := makeGenericDS()
-	ds0.ID = util.IntPtr(420)
-	ds0.XMLID = util.StrPtr("myds")
-	ds0.OrgServerFQDN = util.StrPtr("http://myorigin.example.net")
-	ds0.QStringIgnore = util.IntPtr(0)
-	ds0.CacheURL = util.StrPtr("http://mycacheurl.net")
-
-	servers := []Server{*server}
-	dses := []DeliveryService{*ds0}
-	dss := makeDSS(servers, dses)
-
-	cfg, err := MakeCacheURLDotConfig(fileName, server, dses, dss, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	if !strings.Contains(txt, hdr) {
-		t.Errorf("expected: header comment text '"+hdr+"', actual: %v", txt)
-	}
-
-	if !strings.HasPrefix(strings.TrimSpace(txt), "#") {
-		t.Errorf("expected: header comment, actual: %v", txt)
-	}
-
-	if !strings.Contains(txt, "mycacheurl") {
-		t.Errorf("expected: contains cacheurl, actual: %v", txt)
-	}
-}
-
-func TestMakeCacheURLDotConfigGlobalFile(t *testing.T) {
-	server := makeGenericServer()
-	cdnName := "mycdn"
-	server.CDNName = &cdnName
-
-	hdr := "myHeaderComment"
-
-	fileName := "cacheurl.config"
-
-	ds0 := makeGenericDS()
-	ds0.ID = util.IntPtr(420)
-	ds0.XMLID = util.StrPtr("ds0")
-	ds0.OrgServerFQDN = util.StrPtr("http://myorigin.example.net")
-	ds0.QStringIgnore = util.IntPtr(1)
-	ds0.CacheURL = util.StrPtr("http://mycacheurl.net")
-
-	servers := []Server{*server}
-	dses := []DeliveryService{*ds0}
-	dss := makeDSS(servers, dses)
-
-	cfg, err := MakeCacheURLDotConfig(fileName, server, dses, dss, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	if !strings.Contains(txt, hdr) {
-		t.Errorf("expected: header comment text '"+hdr+"', actual: %v", txt)
-	}
-
-	if !strings.HasPrefix(strings.TrimSpace(txt), "#") {
-		t.Errorf("expected: header comment, actual: %v", txt)
-	}
-
-	if !strings.Contains(txt, "myorigin") {
-		t.Errorf("expected: contains origin, actual: %v", txt)
-	}
-
-	if strings.Contains(txt, "mycacheurl") {
-		t.Errorf("expected: global file to NOT contain cacheurl, actual: contains cacheurl")
-	}
-}
-
-func TestMakeCacheURLDotConfigGlobalFileNoQStringIgnore(t *testing.T) {
-	server := makeGenericServer()
-	cdnName := "mycdn"
-	server.CDNName = &cdnName
-
-	hdr := "myHeaderComment"
-
-	fileName := "cacheurl.config"
-
-	ds0 := makeGenericDS()
-	ds0.ID = util.IntPtr(420)
-	ds0.XMLID = util.StrPtr("ds0")
-	ds0.OrgServerFQDN = util.StrPtr("http://myorigin.example.net")
-	ds0.QStringIgnore = util.IntPtr(0)
-	ds0.CacheURL = util.StrPtr("http://mycacheurl.net")
-
-	servers := []Server{*server}
-	dses := []DeliveryService{*ds0}
-	dss := makeDSS(servers, dses)
-
-	cfg, err := MakeCacheURLDotConfig(fileName, server, dses, dss, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	if !strings.Contains(txt, hdr) {
-		t.Errorf("expected: header comment text '" + hdr + "', actual: missing")
-	}
-
-	if !strings.HasPrefix(strings.TrimSpace(txt), "#") {
-		t.Errorf("expected: header comment, actual: missing")
-	}
-
-	if strings.Contains(txt, "myorigin") {
-		t.Errorf("expected: qstring ignore 0 to omit DS, actual: '%v'", txt)
-	}
-
-	if strings.Contains(txt, "mycacheurl") {
-		t.Errorf("expected: global file to NOT contain cacheurl, actual: contains cacheurl")
-	}
-}
-
-func TestMakeCacheURLDotConfigQStringFile(t *testing.T) {
-	server := makeGenericServer()
-	cdnName := "mycdn"
-	server.CDNName = &cdnName
-
-	hdr := "myHeaderComment"
-
-	fileName := "cacheurl_qstring.config"
-
-	ds0 := makeGenericDS()
-	ds0.ID = util.IntPtr(420)
-	ds0.XMLID = util.StrPtr("ds0")
-	ds0.OrgServerFQDN = util.StrPtr("http://myorigin.example.net")
-	ds0.QStringIgnore = util.IntPtr(0)
-	ds0.CacheURL = util.StrPtr("http://mycacheurl.net")
-
-	servers := []Server{*server}
-	dses := []DeliveryService{*ds0}
-	dss := makeDSS(servers, dses)
-
-	cfg, err := MakeCacheURLDotConfig(fileName, server, dses, dss, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	if !strings.Contains(txt, hdr) {
-		t.Errorf("expected: header comment '" + hdr + "', actual: missing")
-	}
-
-	if !strings.HasPrefix(strings.TrimSpace(txt), "#") {
-		t.Errorf("expected: header comment, actual: missing")
-	}
-
-	if strings.Contains(txt, "myorigin") {
-		t.Errorf("expected: qstring file to NOT contain origin, actual: '%v'", txt)
-	}
-
-	if strings.Contains(txt, "mycacheurl") {
-		t.Errorf("expected: qstring file to NOT contain cacheurl, actual: '%v'", txt)
-	}
-}
diff --git a/lib/go-atscfg/meta.go b/lib/go-atscfg/meta.go
index 88a20d8..da88b5f 100644
--- a/lib/go-atscfg/meta.go
+++ b/lib/go-atscfg/meta.go
@@ -262,12 +262,6 @@ func addMetaObjConfigDir(
 				warnings = append(warnings, "ensuring config file '"+configFile+"': "+err.Error())
 			}
 		}
-		if ds.CacheURL != nil {
-			configFile := "cacheurl_" + *ds.XMLID + ".config"
-			if configFilesM, err = ensureConfigFile(configFilesM, configFile, configDir); err != nil {
-				warnings = append(warnings, "ensuring config file '"+configFile+"': "+err.Error())
-			}
-		}
 		if ds.SigningAlgorithm != nil && *ds.SigningAlgorithm == tc.SigningAlgorithmURLSig {
 			configFile := "url_sig_" + *ds.XMLID + ".config"
 			if configFilesM, err = ensureConfigFile(configFilesM, configFile, configDir); err != nil {
diff --git a/lib/go-atscfg/regexremapdotconfig.go b/lib/go-atscfg/regexremapdotconfig.go
index 2d7a0bd..6e27e7f 100644
--- a/lib/go-atscfg/regexremapdotconfig.go
+++ b/lib/go-atscfg/regexremapdotconfig.go
@@ -89,7 +89,6 @@ func MakeRegexRemapDotConfig(
 type cdnDS struct {
 	OrgServerFQDN string
 	QStringIgnore int
-	CacheURL      string
 	RegexRemap    string
 }
 
@@ -110,9 +109,6 @@ func deliveryServicesToCDNDSes(dses []DeliveryService) (map[tc.DeliveryServiceNa
 		if ds.RegexRemap != nil {
 			sds.RegexRemap = *ds.RegexRemap
 		}
-		if ds.CacheURL != nil {
-			sds.CacheURL = *ds.CacheURL
-		}
 		sDSes[tc.DeliveryServiceName(*ds.XMLID)] = sds
 	}
 	return sDSes, warnings
diff --git a/lib/go-atscfg/regexremapdotconfig_test.go b/lib/go-atscfg/regexremapdotconfig_test.go
index 39fc59e..1390a9f 100644
--- a/lib/go-atscfg/regexremapdotconfig_test.go
+++ b/lib/go-atscfg/regexremapdotconfig_test.go
@@ -40,7 +40,6 @@ func TestMakeRegexRemapDotConfig(t *testing.T) {
 	ds := makeGenericDS()
 	ds.XMLID = &dsName
 	ds.OrgServerFQDN = util.StrPtr("https://myorigin.example.net") // DS "origin_server_fqdn" is actually a URL including the scheme, the name is wrong.
-	ds.CacheURL = util.StrPtr("https://mycacheurl.net")
 	ds.RegexRemap = util.StrPtr("myregexremap")
 
 	dses := []DeliveryService{*ds}
@@ -84,14 +83,12 @@ func TestMakeRegexRemapDotConfigUnusedDS(t *testing.T) {
 	ds.XMLID = &dsName
 	ds.OrgServerFQDN = util.StrPtr("https://myorigin.example.net") // DS "origin_server_fqdn" is actually a URL including the scheme, the name is wrong.
 	ds.QStringIgnore = util.IntPtr(0)
-	ds.CacheURL = util.StrPtr("https://mycacheurl.net")
 	ds.RegexRemap = util.StrPtr("myregexremap")
 
 	ds1 := makeGenericDS()
 	ds1.XMLID = util.StrPtr("otherds")
 	ds1.OrgServerFQDN = util.StrPtr("https://otherorigin.example.net") // DS "origin_server_fqdn" is actually a URL including the scheme, the name is wrong.
 	ds1.QStringIgnore = util.IntPtr(0)
-	ds1.CacheURL = util.StrPtr("https://othercacheurl.net")
 	ds1.RegexRemap = util.StrPtr("otherregexremap")
 
 	dses := []DeliveryService{*ds, *ds1}
@@ -145,7 +142,6 @@ func TestMakeRegexRemapDotConfigReplaceReturns(t *testing.T) {
 	ds.XMLID = &dsName
 	ds.OrgServerFQDN = util.StrPtr("https://myorigin.example.net") // DS "origin_server_fqdn" is actually a URL including the scheme, the name is wrong.
 	ds.QStringIgnore = util.IntPtr(0)
-	ds.CacheURL = util.StrPtr("https://mycacheurl.net")
 	ds.RegexRemap = util.StrPtr("myregexremap__RETURN__mypostnewline")
 
 	dses := []DeliveryService{*ds}
diff --git a/lib/go-atscfg/remapdotconfig.go b/lib/go-atscfg/remapdotconfig.go
index 2ad3538..3e7a054 100644
--- a/lib/go-atscfg/remapdotconfig.go
+++ b/lib/go-atscfg/remapdotconfig.go
@@ -21,6 +21,7 @@ package atscfg
 
 import (
 	"errors"
+	"github.com/apache/trafficcontrol/lib/go-log"
 	"sort"
 	"strconv"
 	"strings"
@@ -150,9 +151,8 @@ func getServerConfigRemapDotConfigForMid(
 			continue // skip remap rules from extra HOST_REGEXP entries
 		}
 
-		// multiple uses of cacheurl and cachekey plugins don't work right in ATS, but Perl has always done it.
+		// multiple uses of cachekey plugins don't work right in ATS, but Perl has always done it.
 		// So for now, keep track of it, so we can log an error when it happens.
-		hasCacheURL := false
 		hasCacheKey := false
 
 		midRemap := ""
@@ -168,21 +168,12 @@ func getServerConfigRemapDotConfigForMid(
 		}
 
 		if ds.QStringIgnore != nil && *ds.QStringIgnore == tc.QueryStringIgnoreIgnoreInCacheKeyAndPassUp {
-			qstr, addedCacheURL, addedCacheKey := getQStringIgnoreRemap(atsMajorVersion)
-			if addedCacheURL {
-				hasCacheURL = true
-			}
+			qstr, addedCacheKey := getQStringIgnoreRemap(atsMajorVersion)
 			if addedCacheKey {
 				hasCacheKey = true
 			}
 			midRemap += qstr
 		}
-		if ds.CacheURL != nil && *ds.CacheURL != "" {
-			if hasCacheURL {
-				warnings = append(warnings, "Delivery Service '"+*ds.XMLID+"': qstring_ignore and cacheurl both add cacheurl, but ATS cacheurl doesn't work correctly with multiple entries! Adding anyway!")
-			}
-			midRemap += ` @plugin=cacheurl.so @pparam=` + cacheURLConfigFileName(*ds.XMLID)
-		}
 
 		if ds.ProfileID != nil && len(profilesCacheKeyConfigParams[*ds.ProfileID]) > 0 {
 			if hasCacheKey {
@@ -344,9 +335,8 @@ func buildEdgeRemapLine(
 		}
 	}
 
-	// multiple uses of cacheurl and cachekey plugins don't work right in ATS, but Perl has always done it.
+	// multiple uses of cachekey plugins don't work right in ATS, but Perl has always done it.
 	// So for now, keep track of it, so we can log an error when it happens.
-	hasCacheURL := false
 	hasCacheKey := false
 
 	if ds.QStringIgnore != nil {
@@ -357,10 +347,7 @@ func buildEdgeRemapLine(
 			if _, globalExists := cacheURLConfigParams["location"]; globalExists {
 				warnings = append(warnings, "Delivery Service '"+*ds.XMLID+"': qstring_ignore == 1, but global cacheurl.config param exists, so skipping remap rename config_file=cacheurl.config parameter")
 			} else {
-				qstr, addedCacheURL, addedCacheKey := getQStringIgnoreRemap(atsMajorVersion)
-				if addedCacheURL {
-					hasCacheURL = true
-				}
+				qstr, addedCacheKey := getQStringIgnoreRemap(atsMajorVersion)
 				if addedCacheKey {
 					hasCacheKey = true
 				}
@@ -369,13 +356,6 @@ func buildEdgeRemapLine(
 		}
 	}
 
-	if ds.CacheURL != nil && *ds.CacheURL != "" {
-		if hasCacheURL {
-			warnings = append(warnings, "Delivery Service '"+*ds.XMLID+"': qstring_ignore and cacheurl both add cacheurl, but ATS cacheurl doesn't work correctly with multiple entries! Adding anyway!")
-		}
-		text += ` @plugin=cacheurl.so @pparam=` + cacheURLConfigFileName(*ds.XMLID)
-	}
-
 	if len(cacheKeyConfigParams) > 0 {
 		if hasCacheKey {
 			warnings = append(warnings, "Delivery Service '"+*ds.XMLID+"': qstring_ignore and params both add cachekey, but ATS cachekey doesn't work correctly with multiple entries! Adding anyway!")
@@ -528,21 +508,13 @@ func midHeaderRewriteConfigFileName(dsName string) string {
 	return "hdr_rw_mid_" + dsName + ".config"
 }
 
-func cacheURLConfigFileName(dsName string) string {
-	return "cacheurl_" + dsName + ".config"
-}
-
-// getQStringIgnoreRemap returns the remap, whether cacheurl was added, and whether cachekey was added.
-func getQStringIgnoreRemap(atsMajorVersion int) (string, bool, bool) {
-	if atsMajorVersion >= 6 {
-		addingCacheURL := false
-		addingCacheKey := true
-		return ` @plugin=cachekey.so @pparam=--separator= @pparam=--remove-all-params=true @pparam=--remove-path=true @pparam=--capture-prefix-uri=/^([^?]*)/$1/`, addingCacheURL, addingCacheKey
-	} else {
-		addingCacheURL := true
-		addingCacheKey := false
-		return ` @plugin=cacheurl.so @pparam=cacheurl_qstring.config`, addingCacheURL, addingCacheKey
+// getQStringIgnoreRemap returns the remap, whether cachekey was added.
+func getQStringIgnoreRemap(atsMajorVersion int) (string, bool) {
+	if atsMajorVersion < 7 {
+		log.Errorf("Unsupport version of ats found %v", atsMajorVersion)
+		return "", false
 	}
+	return ` @plugin=cachekey.so @pparam=--separator= @pparam=--remove-all-params=true @pparam=--remove-path=true @pparam=--capture-prefix-uri=/^([^?]*)/$1/`, true
 }
 
 // makeServerPackageParamData returns a map[paramName]paramVal for this server, config file 'package'.
diff --git a/lib/go-atscfg/remapdotconfig_test.go b/lib/go-atscfg/remapdotconfig_test.go
index 8b1c60c..6ef38fa 100644
--- a/lib/go-atscfg/remapdotconfig_test.go
+++ b/lib/go-atscfg/remapdotconfig_test.go
@@ -27,6 +27,24 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-util"
 )
 
+func makeTestRemapServer() *Server {
+	server := &Server{}
+	server.ProfileID = util.IntPtr(42)
+	server.CDNName = util.StrPtr("mycdn")
+	server.Cachegroup = util.StrPtr("cg0")
+	server.DomainName = util.StrPtr("mydomain")
+	server.CDNID = util.IntPtr(43)
+	server.HostName = util.StrPtr("server0")
+	server.HTTPSPort = util.IntPtr(12443)
+	server.ID = util.IntPtr(44)
+	setIP(server, "192.168.2.4")
+	server.ProfileID = util.IntPtr(46)
+	server.Profile = util.StrPtr("MyProfile")
+	server.TCPPort = util.IntPtr(12080)
+	server.Type = "MID"
+	return server
+}
+
 func TestMakeRemapDotConfig(t *testing.T) {
 	hdr := "myHeaderComment"
 
@@ -39,7 +57,6 @@ func TestMakeRemapDotConfig(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -161,7 +178,6 @@ func TestMakeRemapDotConfigMidLiveLocalExcluded(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -274,7 +290,6 @@ func TestMakeRemapDotConfigMid(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -401,7 +416,6 @@ func TestMakeRemapDotConfigNilOrigin(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = nil
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -514,7 +528,6 @@ func TestMakeRemapDotConfigEmptyOrigin(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("")
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -627,7 +640,6 @@ func TestMakeRemapDotConfigDuplicateOrigins(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -651,7 +663,6 @@ func TestMakeRemapDotConfigDuplicateOrigins(t *testing.T) {
 	ds2.Type = &dsType2
 	ds2.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds2.MidHeaderRewrite = util.StrPtr("mymidrewrite2")
-	ds2.CacheURL = util.StrPtr("mycacheurl2")
 	ds2.RangeRequestHandling = util.IntPtr(0)
 	ds2.RemapText = util.StrPtr("myremaptext")
 	ds2.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -770,16 +781,13 @@ func TestMakeRemapDotConfigDuplicateOrigins(t *testing.T) {
 
 func TestMakeRemapDotConfigNilMidRewrite(t *testing.T) {
 	hdr := "myHeaderComment"
-
 	server := makeTestRemapServer()
-
 	ds := DeliveryService{}
 	ds.ID = util.IntPtr(48)
 	dsType := tc.DSType("HTTP_LIVE_NATNL")
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = nil
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -877,32 +885,9 @@ func TestMakeRemapDotConfigNilMidRewrite(t *testing.T) {
 
 	txtLines := strings.Split(txt, "\n")
 
-	if len(txtLines) != 2 {
-		t.Errorf("expected one line for each remap plus a comment, actual: '%v' count %v", txt, len(txtLines))
-	}
-
-	remapLine := txtLines[1]
-
-	if !strings.HasPrefix(remapLine, "map") {
-		t.Errorf("expected to start with 'map', actual '%v'", txt)
-	}
-
-	if strings.Count(remapLine, "origin.example.test") != 2 {
-		t.Errorf("expected to contain origin FQDN twice (Mids remap origins to themselves, as a forward proxy), actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "hdr_rw_mid_") {
-		t.Errorf("expected no 'hdr_rw_mid_' for nil mid header rewrite on DS, actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "myedgeheaderrewrite") {
-		t.Errorf("expected no edge header rewrite text for mid server, actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "hdr_rw_") {
-		t.Errorf("expected no edge header rewrite for mid server, actual '%v'", txt)
+	if len(txtLines) != 1 {
+		t.Fatalf("expected one line, actual: '%v' count %v", txt, len(txtLines))
 	}
-
 }
 
 func TestMakeRemapDotConfigMidHasNoEdgeRewrite(t *testing.T) {
@@ -916,7 +901,6 @@ func TestMakeRemapDotConfigMidHasNoEdgeRewrite(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = nil
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -1014,292 +998,8 @@ func TestMakeRemapDotConfigMidHasNoEdgeRewrite(t *testing.T) {
 
 	txtLines := strings.Split(txt, "\n")
 
-	if len(txtLines) != 2 {
-		t.Errorf("expected one line for each remap plus a comment, actual: '%v' count %v", txt, len(txtLines))
-	}
-
-	remapLine := txtLines[1]
-
-	if !strings.HasPrefix(remapLine, "map") {
-		t.Errorf("expected to start with 'map', actual '%v'", txt)
-	}
-
-	if strings.Count(remapLine, "origin.example.test") != 2 {
-		t.Errorf("expected to contain origin FQDN twice (Mids remap origins to themselves, as a forward proxy), actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "hdr_rw_mid_") {
-		t.Errorf("expected no 'hdr_rw_mid_' for nil mid header rewrite on DS, actual '%v'", txt)
-	}
-}
-
-func TestMakeRemapDotConfigMidQStringPassUpATS7CacheKey(t *testing.T) {
-	hdr := "myHeaderComment"
-
-	server := makeTestRemapServer()
-
-	ds := DeliveryService{}
-	ds.ID = util.IntPtr(48)
-	dsType := tc.DSType("HTTP_LIVE_NATNL")
-	ds.Type = &dsType
-	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
-	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
-	ds.RangeRequestHandling = util.IntPtr(0)
-	ds.RemapText = util.StrPtr("myremaptext")
-	ds.EdgeHeaderRewrite = nil
-	ds.SigningAlgorithm = util.StrPtr("url_sig")
-	ds.XMLID = util.StrPtr("mydsname")
-	ds.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreIgnoreInCacheKeyAndPassUp))
-	ds.RegexRemap = util.StrPtr("myregexremap")
-	ds.FQPacingRate = util.IntPtr(0)
-	ds.DSCP = util.IntPtr(0)
-	ds.RoutingName = util.StrPtr("myroutingname")
-	ds.MultiSiteOrigin = util.BoolPtr(false)
-	ds.OriginShield = util.StrPtr("myoriginshield")
-	ds.ProfileID = util.IntPtr(49)
-	ds.Protocol = util.IntPtr(0)
-	ds.AnonymousBlockingEnabled = util.BoolPtr(false)
-	ds.Active = util.BoolPtr(true)
-
-	dses := []DeliveryService{ds}
-
-	dss := []tc.DeliveryServiceServer{
-		tc.DeliveryServiceServer{
-			Server:          util.IntPtr(*server.ID),
-			DeliveryService: util.IntPtr(*ds.ID),
-		},
-	}
-
-	dsRegexes := []tc.DeliveryServiceRegexes{
-		tc.DeliveryServiceRegexes{
-			DSName: *ds.XMLID,
-			Regexes: []tc.DeliveryServiceRegex{
-				tc.DeliveryServiceRegex{
-					Type:      string(tc.DSMatchTypeHostRegex),
-					SetNumber: 0,
-					Pattern:   "myregexpattern",
-				},
-			},
-		},
-	}
-
-	serverParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "trafficserver",
-			ConfigFile: "package",
-			Value:      "6",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "serverpkgval",
-			ConfigFile: "package",
-			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
-		},
-	}
-
-	cacheKeyParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "cachekeyparamname",
-			ConfigFile: "cacheurl.config",
-			Value:      "cachekeyparamval",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cacheurl.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cachekey.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-	}
-
-	cdn := &tc.CDN{
-		DomainName: "cdndomain.example",
-		Name:       "my-cdn-name",
-	}
-
-	topologies := []tc.Topology{}
-	cgs := []tc.CacheGroupNullable{}
-	serverCapabilities := map[int]map[ServerCapability]struct{}{}
-	dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
-
-	cfg, err := MakeRemapDotConfig(server, dses, dss, dsRegexes, serverParams, cdn, cacheKeyParams, topologies, cgs, serverCapabilities, dsRequiredCapabilities, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	txt = strings.TrimSpace(txt)
-
-	testComment(t, txt, hdr)
-
-	txtLines := strings.Split(txt, "\n")
-
-	if len(txtLines) != 2 {
-		t.Errorf("expected one line for each remap plus a comment, actual: '%v' count %v", txt, len(txtLines))
-	}
-
-	remapLine := txtLines[1]
-
-	if !strings.HasPrefix(remapLine, "map") {
-		t.Errorf("expected to start with 'map', actual '%v'", txt)
-	}
-
-	if strings.Count(remapLine, "origin.example.test") != 2 {
-		t.Errorf("expected to contain origin FQDN twice (Mids remap origins to themselves, as a forward proxy), actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "hdr_rw_mid_") {
-		t.Errorf("expected no 'hdr_rw_mid_' for nil mid header rewrite on DS, actual '%v'", txt)
-	}
-
-	if !strings.Contains(remapLine, "cachekey") {
-		t.Errorf("expected 'cachekey' for qstring pass up and ATS 6+, actual '%v'", txt)
-	}
-	if strings.Contains(remapLine, "cacheurl") {
-		t.Errorf("expected no 'cacheurl' for ATS 6+, actual '%v'", txt)
-	}
-}
-
-func TestMakeRemapDotConfigMidQStringPassUpATS5CacheURL(t *testing.T) {
-	hdr := "myHeaderComment"
-
-	server := makeTestRemapServer()
-
-	ds := DeliveryService{}
-	ds.ID = util.IntPtr(48)
-	dsType := tc.DSType("HTTP_LIVE_NATNL")
-	ds.Type = &dsType
-	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
-	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
-	ds.RangeRequestHandling = util.IntPtr(0)
-	ds.RemapText = util.StrPtr("myremaptext")
-	ds.EdgeHeaderRewrite = nil
-	ds.SigningAlgorithm = util.StrPtr("url_sig")
-	ds.XMLID = util.StrPtr("mydsname")
-	ds.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreIgnoreInCacheKeyAndPassUp))
-	ds.RegexRemap = util.StrPtr("myregexremap")
-	ds.FQPacingRate = util.IntPtr(0)
-	ds.DSCP = util.IntPtr(0)
-	ds.RoutingName = util.StrPtr("myroutingname")
-	ds.MultiSiteOrigin = util.BoolPtr(false)
-	ds.OriginShield = util.StrPtr("myoriginshield")
-	ds.ProfileID = util.IntPtr(49)
-	ds.Protocol = util.IntPtr(0)
-	ds.AnonymousBlockingEnabled = util.BoolPtr(false)
-	ds.Active = util.BoolPtr(true)
-
-	dses := []DeliveryService{ds}
-
-	dss := []tc.DeliveryServiceServer{
-		tc.DeliveryServiceServer{
-			Server:          util.IntPtr(*server.ID),
-			DeliveryService: util.IntPtr(*ds.ID),
-		},
-	}
-
-	dsRegexes := []tc.DeliveryServiceRegexes{
-		tc.DeliveryServiceRegexes{
-			DSName: *ds.XMLID,
-			Regexes: []tc.DeliveryServiceRegex{
-				tc.DeliveryServiceRegex{
-					Type:      string(tc.DSMatchTypeHostRegex),
-					SetNumber: 0,
-					Pattern:   "myregexpattern",
-				},
-			},
-		},
-	}
-
-	serverParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "trafficserver",
-			ConfigFile: "package",
-			Value:      "5",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "serverpkgval",
-			ConfigFile: "package",
-			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
-		},
-	}
-
-	cacheKeyParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "cachekeyparamname",
-			ConfigFile: "cacheurl.config",
-			Value:      "cachekeyparamval",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cacheurl.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cachekey.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-	}
-
-	cdn := &tc.CDN{
-		DomainName: "cdndomain.example",
-		Name:       "my-cdn-name",
-	}
-
-	topologies := []tc.Topology{}
-	cgs := []tc.CacheGroupNullable{}
-	serverCapabilities := map[int]map[ServerCapability]struct{}{}
-	dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
-
-	cfg, err := MakeRemapDotConfig(server, dses, dss, dsRegexes, serverParams, cdn, cacheKeyParams, topologies, cgs, serverCapabilities, dsRequiredCapabilities, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	txt = strings.TrimSpace(txt)
-
-	testComment(t, txt, hdr)
-
-	txtLines := strings.Split(txt, "\n")
-
-	if len(txtLines) != 2 {
-		t.Errorf("expected one line for each remap plus a comment, actual: '%v' count %v", txt, len(txtLines))
-	}
-
-	remapLine := txtLines[1]
-
-	if !strings.HasPrefix(remapLine, "map") {
-		t.Errorf("expected to start with 'map', actual '%v'", txt)
-	}
-
-	if strings.Count(remapLine, "origin.example.test") != 2 {
-		t.Errorf("expected to contain origin FQDN twice (Mids remap origins to themselves, as a forward proxy), actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "hdr_rw_mid_") {
-		t.Errorf("expected no 'hdr_rw_mid_' for nil mid header rewrite on DS, actual '%v'", txt)
-	}
-
-	if !strings.Contains(remapLine, "cacheurl") {
-		t.Errorf("expected 'cacheurl' for qstring pass up and ATS <=6, actual '%v'", txt)
-	}
-	if strings.Contains(remapLine, "cachekey") {
-		t.Errorf("expected no 'cachekey' for ATS <=6, actual '%v'", txt)
+	if len(txtLines) != 1 {
+		t.Fatalf("expected one line, actual: '%v' count %v", txt, len(txtLines))
 	}
 }
 
@@ -1314,7 +1014,6 @@ func TestMakeRemapDotConfigMidProfileCacheKey(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(0)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -1463,7 +1162,6 @@ func TestMakeRemapDotConfigMidRangeRequestHandling(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -1604,152 +1302,6 @@ func TestMakeRemapDotConfigMidSlicePluginRangeRequestHandling(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
-	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
-	ds.RemapText = util.StrPtr("myremaptext")
-	ds.EdgeHeaderRewrite = nil
-	ds.SigningAlgorithm = util.StrPtr("url_sig")
-	ds.XMLID = util.StrPtr("mydsname")
-	ds.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreIgnoreInCacheKeyAndPassUp))
-	ds.RegexRemap = util.StrPtr("myregexremap")
-	ds.FQPacingRate = util.IntPtr(0)
-	ds.DSCP = util.IntPtr(0)
-	ds.RoutingName = util.StrPtr("myroutingname")
-	ds.MultiSiteOrigin = util.BoolPtr(false)
-	ds.OriginShield = util.StrPtr("myoriginshield")
-	ds.ProfileID = util.IntPtr(49)
-	ds.ProfileName = util.StrPtr("dsprofile")
-	ds.Protocol = util.IntPtr(0)
-	ds.AnonymousBlockingEnabled = util.BoolPtr(false)
-	ds.Active = util.BoolPtr(true)
-
-	dses := []DeliveryService{ds}
-
-	dss := []tc.DeliveryServiceServer{
-		tc.DeliveryServiceServer{
-			Server:          util.IntPtr(*server.ID),
-			DeliveryService: util.IntPtr(*ds.ID),
-		},
-	}
-
-	dsRegexes := []tc.DeliveryServiceRegexes{
-		tc.DeliveryServiceRegexes{
-			DSName: *ds.XMLID,
-			Regexes: []tc.DeliveryServiceRegex{
-				tc.DeliveryServiceRegex{
-					Type:      string(tc.DSMatchTypeHostRegex),
-					SetNumber: 0,
-					Pattern:   "myregexpattern",
-				},
-			},
-		},
-	}
-
-	serverParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "trafficserver",
-			ConfigFile: "package",
-			Value:      "7",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "serverpkgval",
-			ConfigFile: "package",
-			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
-		},
-	}
-
-	cacheKeyParams := []tc.Parameter{
-		tc.Parameter{
-			Name:       "cachekeykey",
-			ConfigFile: "cacheurl.config",
-			Value:      "cachekeyval",
-			Profiles:   []byte(`["dsprofile"]`),
-		},
-		tc.Parameter{
-			Name:       "shouldnotexist",
-			ConfigFile: "cacheurl.config",
-			Value:      "shouldnotexisteither",
-			Profiles:   []byte(`["not-dsprofile"]`),
-		},
-		tc.Parameter{
-			Name:       "cachekeykey",
-			ConfigFile: "cacheurl.config",
-			Value:      "cachekeyval",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cacheurl.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-		tc.Parameter{
-			Name:       "not_location",
-			ConfigFile: "cachekey.config",
-			Value:      "notinconfig",
-			Profiles:   []byte(`["global"]`),
-		},
-	}
-
-	cdn := &tc.CDN{
-		DomainName: "cdndomain.example",
-		Name:       "my-cdn-name",
-	}
-
-	topologies := []tc.Topology{}
-	cgs := []tc.CacheGroupNullable{}
-	serverCapabilities := map[int]map[ServerCapability]struct{}{}
-	dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
-
-	cfg, err := MakeRemapDotConfig(server, dses, dss, dsRegexes, serverParams, cdn, cacheKeyParams, topologies, cgs, serverCapabilities, dsRequiredCapabilities, hdr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txt := cfg.Text
-
-	txt = strings.TrimSpace(txt)
-
-	testComment(t, txt, hdr)
-
-	txtLines := strings.Split(txt, "\n")
-
-	if len(txtLines) != 2 {
-		t.Fatalf("expected one line for each remap plus a comment, actual: '%v' count %v", txt, len(txtLines))
-	}
-
-	remapLine := txtLines[1]
-
-	if !strings.HasPrefix(remapLine, "map") {
-		t.Errorf("expected to start with 'map', actual '%v'", txt)
-	}
-
-	if strings.Count(remapLine, "origin.example.test") != 2 {
-		t.Errorf("expected to contain origin FQDN twice (Mids remap origins to themselves, as a forward proxy), actual '%v'", txt)
-	}
-
-	if !strings.Contains(remapLine, "cache_range_requests.so") {
-		t.Errorf("expected to contain range request handling plugin, actual '%v'", txt)
-	}
-
-	if strings.Contains(remapLine, "slice.so") {
-		t.Errorf("expected to not contain range request handling slice plugin, actual '%v'", txt)
-	}
-}
-
-func TestMakeRemapDotConfigFirstExcludedSecondIncluded(t *testing.T) {
-	hdr := "myHeaderComment"
-
-	server := makeTestRemapServer()
-
-	ds := DeliveryService{}
-	ds.ID = util.IntPtr(48)
-	dsType := tc.DSType("HTTP_LIVE_NATNL")
-	ds.Type = &dsType
-	ds.OrgServerFQDN = util.StrPtr("") // should be excluded
-	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -1774,7 +1326,6 @@ func TestMakeRemapDotConfigFirstExcludedSecondIncluded(t *testing.T) {
 	ds2.Type = &dsType2
 	ds2.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds2.MidHeaderRewrite = util.StrPtr("")
-	ds2.CacheURL = util.StrPtr("")
 	ds2.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds2.RemapText = util.StrPtr("myremaptext")
 	ds2.EdgeHeaderRewrite = nil
@@ -1902,7 +1453,6 @@ func TestMakeRemapDotConfigAnyMap(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds.RemapText = util.StrPtr("") // should not be included, any map requires remap text
 	ds.EdgeHeaderRewrite = nil
@@ -1927,7 +1477,6 @@ func TestMakeRemapDotConfigAnyMap(t *testing.T) {
 	ds2.Type = &dsType2
 	ds2.OrgServerFQDN = util.StrPtr("myorigin")
 	ds2.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-	ds2.CacheURL = util.StrPtr("mycacheurl")
 	ds2.RangeRequestHandling = util.IntPtr(0)
 	ds2.RemapText = util.StrPtr("myremaptext")
 	ds2.EdgeHeaderRewrite = util.StrPtr("myedgerewrite")
@@ -2083,7 +1632,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("myorigin")
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myreamptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2110,7 +1658,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("myorigin")
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2137,7 +1684,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("myorigin")
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2164,7 +1710,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("myorigin")
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2191,7 +1736,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("myorigin")
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2218,7 +1762,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = nil // nil origin should not be included
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2245,7 +1788,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("") // empty origin should not be included
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2272,7 +1814,6 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 		ds.Type = &dsType
 		ds.OrgServerFQDN = util.StrPtr("") // empty origin should not be included
 		ds.MidHeaderRewrite = util.StrPtr("mymidrewrite")
-		ds.CacheURL = util.StrPtr("mycacheurl")
 		ds.RangeRequestHandling = util.IntPtr(0)
 		ds.RemapText = util.StrPtr("myremaptext")
 		ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -2499,7 +2040,6 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacement(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -2645,7 +2185,6 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTP(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -2791,7 +2330,6 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTPS(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -2937,7 +2475,6 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTPToHTTPS(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -3083,7 +2620,6 @@ func TestMakeRemapDotConfigEdgeRemapUnderscoreHTTPReplace(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -3225,7 +2761,6 @@ func TestMakeRemapDotConfigEdgeDSCPRemap(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -3373,7 +2908,6 @@ func TestMakeRemapDotConfigEdgeNoDSCPRemap(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -3521,7 +3055,6 @@ func TestMakeRemapDotConfigEdgeHeaderRewrite(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("myedgeheaderrewrite")
@@ -3673,7 +3206,6 @@ func TestMakeRemapDotConfigEdgeHeaderRewriteEmpty(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = util.StrPtr("")
@@ -3825,7 +3357,6 @@ func TestMakeRemapDotConfigEdgeHeaderRewriteNil(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -3977,7 +3508,6 @@ func TestMakeRemapDotConfigEdgeSigningURLSig(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4124,7 +3654,6 @@ func TestMakeRemapDotConfigEdgeSigningURISigning(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4271,7 +3800,6 @@ func TestMakeRemapDotConfigEdgeSigningNone(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4418,7 +3946,6 @@ func TestMakeRemapDotConfigEdgeSigningEmpty(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4565,7 +4092,6 @@ func TestMakeRemapDotConfigEdgeSigningWrong(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4712,7 +4238,6 @@ func TestMakeRemapDotConfigEdgeQStringDropAtEdge(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -4857,7 +4382,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUp(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5005,7 +4529,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpWithCacheKeyParameter(t *testi
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5153,7 +4676,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParam(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5273,7 +4795,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParamCacheURL(t *testi
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5373,8 +4894,8 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParamCacheURL(t *testi
 		t.Errorf("expected remap on edge server with ats<5 to not contain cachekey plugin, actual '%v'", txt)
 	}
 
-	if !strings.Contains(remapLine, "cacheurl.so") {
-		t.Errorf("expected remap on edge server with ats<5 to contain cacheurl  plugin, actual '%v'", txt)
+	if strings.Contains(remapLine, "cacheurl.so") {
+		t.Errorf("expected remap on edge server with ats<5 to not contain cacheurl plugin, actual '%v'", txt)
 	}
 }
 
@@ -5392,7 +4913,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParamCacheURLAndDSCach
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5491,14 +5011,6 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParamCacheURLAndDSCach
 	if strings.Contains(remapLine, "cachekey.so") {
 		t.Errorf("expected remap on edge server with ats<5 to not contain cachekey plugin, actual '%v'", txt)
 	}
-
-	if !strings.Contains(remapLine, "cacheurl.so") {
-		t.Errorf("expected remap on edge server with ats<5 to contain cacheurl  plugin, actual '%v'", txt)
-	}
-
-	if !strings.Contains(remapLine, "cacheurl_") {
-		t.Errorf("expected remap on edge server with ds qstring cacheurl and ds cacheurl to generate both, actual '%v'", txt)
-	}
 }
 
 func TestMakeRemapDotConfigMidQStringIgnorePassUpCacheURLParamCacheURLAndDSCacheURL(t *testing.T) {
@@ -5516,7 +5028,6 @@ func TestMakeRemapDotConfigMidQStringIgnorePassUpCacheURLParamCacheURLAndDSCache
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5615,14 +5126,6 @@ func TestMakeRemapDotConfigMidQStringIgnorePassUpCacheURLParamCacheURLAndDSCache
 	if strings.Contains(remapLine, "cachekey.so") {
 		t.Errorf("expected remap on edge server with ats<5 to not contain cachekey plugin, actual '%v'", txt)
 	}
-
-	if !strings.Contains(remapLine, "cacheurl.so") {
-		t.Errorf("expected remap on edge server with ats<5 to contain cacheurl  plugin, actual '%v'", txt)
-	}
-
-	if !strings.Contains(remapLine, "cacheurl_") {
-		t.Errorf("expected remap on edge server with ds qstring cacheurl and ds cacheurl to generate both, actual '%v'", txt)
-	}
 }
 
 func TestMakeRemapDotConfigEdgeCacheURL(t *testing.T) {
@@ -5637,7 +5140,6 @@ func TestMakeRemapDotConfigEdgeCacheURL(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5732,10 +5234,6 @@ func TestMakeRemapDotConfigEdgeCacheURL(t *testing.T) {
 	if !strings.HasPrefix(remapLine, "map") {
 		t.Errorf("expected to start with 'map', actual '%v'", txt)
 	}
-
-	if !strings.Contains(remapLine, "cacheurl_") {
-		t.Errorf("expected remap on edge server with ds cacheurl to contain cacheurl plugin, actual '%v'", txt)
-	}
 }
 
 func TestMakeRemapDotConfigEdgeCacheKeyParams(t *testing.T) {
@@ -5750,7 +5248,6 @@ func TestMakeRemapDotConfigEdgeCacheKeyParams(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -5906,7 +5403,6 @@ func TestMakeRemapDotConfigEdgeRegexRemap(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6054,7 +5550,6 @@ func TestMakeRemapDotConfigEdgeRegexRemapEmpty(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(int(tc.RangeRequestHandlingCacheRangeRequest))
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6198,7 +5693,6 @@ func TestMakeRemapDotConfigEdgeRangeRequestNil(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = nil
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6346,7 +5840,6 @@ func TestMakeRemapDotConfigEdgeRangeRequestDontCache(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingDontCache)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6494,7 +5987,6 @@ func TestMakeRemapDotConfigEdgeRangeRequestBGFetch(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingBackgroundFetch)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6642,7 +6134,6 @@ func TestMakeRemapDotConfigEdgeRangeRequestSlice(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds.RemapText = util.StrPtr("myremaptext")
 	ds.EdgeHeaderRewrite = nil
@@ -6795,7 +6286,6 @@ func TestMakeRemapDotConfigRawRemapRangeDirective(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua __RANGE_DIRECTIVE__")
 	ds.EdgeHeaderRewrite = nil
@@ -6961,7 +6451,6 @@ func TestMakeRemapDotConfigRawRemapWithoutRangeDirective(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingSlice)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7121,7 +6610,6 @@ func TestMakeRemapDotConfigEdgeRangeRequestCache(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7269,7 +6757,6 @@ func TestMakeRemapDotConfigEdgeFQPacingNil(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7413,7 +6900,6 @@ func TestMakeRemapDotConfigEdgeFQPacingNegative(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7557,7 +7043,6 @@ func TestMakeRemapDotConfigEdgeFQPacingZero(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7701,7 +7186,6 @@ func TestMakeRemapDotConfigEdgeFQPacingPositive(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7849,7 +7333,6 @@ func TestMakeRemapDotConfigEdgeDNS(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -7993,7 +7476,6 @@ func TestMakeRemapDotConfigEdgeDNSNoRoutingName(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -8127,7 +7609,6 @@ func TestMakeRemapDotConfigEdgeRegexTypeNil(t *testing.T) {
 	ds.Type = &dsType
 	ds.OrgServerFQDN = util.StrPtr("origin.example.test")
 	ds.MidHeaderRewrite = util.StrPtr("")
-	ds.CacheURL = util.StrPtr("mycacheurl")
 	ds.RangeRequestHandling = util.IntPtr(tc.RangeRequestHandlingCacheRangeRequest)
 	ds.RemapText = util.StrPtr("@plugin=tslua.so @pparam=my-range-manipulator.lua")
 	ds.EdgeHeaderRewrite = nil
@@ -8249,21 +7730,3 @@ func TestMakeRemapDotConfigEdgeRegexTypeNil(t *testing.T) {
 	}
 
 }
-
-func makeTestRemapServer() *Server {
-	server := &Server{}
-	server.ProfileID = util.IntPtr(42)
-	server.CDNName = util.StrPtr("mycdn")
-	server.Cachegroup = util.StrPtr("cg0")
-	server.DomainName = util.StrPtr("mydomain")
-	server.CDNID = util.IntPtr(43)
-	server.HostName = util.StrPtr("server0")
-	server.HTTPSPort = util.IntPtr(12443)
-	server.ID = util.IntPtr(44)
-	setIP(server, "192.168.2.4")
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("MyProfile")
-	server.TCPPort = util.IntPtr(12080)
-	server.Type = "MID"
-	return server
-}
diff --git a/lib/go-tc/deliveryservice_requests.go b/lib/go-tc/deliveryservice_requests.go
index b156564..75a480a 100644
--- a/lib/go-tc/deliveryservice_requests.go
+++ b/lib/go-tc/deliveryservice_requests.go
@@ -406,19 +406,19 @@ type DeliveryServiceRequest struct {
 // DeliveryServiceRequestNullable is used as part of the workflow to create,
 // modify, or delete a delivery service.
 type DeliveryServiceRequestNullable struct {
-	AssigneeID      *int                        `json:"assigneeId,omitempty" db:"assignee_id"`
-	Assignee        *string                     `json:"assignee,omitempty"`
-	AuthorID        *IDNoMod                    `json:"authorId" db:"author_id"`
-	Author          *string                     `json:"author"`
-	ChangeType      *string                     `json:"changeType" db:"change_type"`
-	CreatedAt       *TimeNoMod                  `json:"createdAt" db:"created_at"`
-	ID              *int                        `json:"id" db:"id"`
-	LastEditedBy    *string                     `json:"lastEditedBy"`
-	LastEditedByID  *IDNoMod                    `json:"lastEditedById" db:"last_edited_by_id"`
-	LastUpdated     *TimeNoMod                  `json:"lastUpdated" db:"last_updated"`
-	DeliveryService *DeliveryServiceNullableV30 `json:"deliveryService" db:"deliveryservice"`
-	Status          *RequestStatus              `json:"status" db:"status"`
-	XMLID           *string                     `json:"-" db:"xml_id"`
+	AssigneeID      *int               `json:"assigneeId,omitempty" db:"assignee_id"`
+	Assignee        *string            `json:"assignee,omitempty"`
+	AuthorID        *IDNoMod           `json:"authorId" db:"author_id"`
+	Author          *string            `json:"author"`
+	ChangeType      *string            `json:"changeType" db:"change_type"`
+	CreatedAt       *TimeNoMod         `json:"createdAt" db:"created_at"`
+	ID              *int               `json:"id" db:"id"`
+	LastEditedBy    *string            `json:"lastEditedBy"`
+	LastEditedByID  *IDNoMod           `json:"lastEditedById" db:"last_edited_by_id"`
+	LastUpdated     *TimeNoMod         `json:"lastUpdated" db:"last_updated"`
+	DeliveryService *DeliveryServiceV4 `json:"deliveryService" db:"deliveryservice"`
+	Status          *RequestStatus     `json:"status" db:"status"`
+	XMLID           *string            `json:"-" db:"xml_id"`
 }
 
 // UnmarshalJSON implements the json.Unmarshaller interface to suppress unmarshalling for IDNoMod
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index 2c05b68..921baae 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -6,14 +6,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"regexp"
-	"strings"
-
-	"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
 	"github.com/apache/trafficcontrol/lib/go-util"
-
-	"github.com/asaskevich/govalidator"
-	validation "github.com/go-ozzo/ozzo-validation"
 )
 
 /*
@@ -56,6 +49,13 @@ type DeliveryServicesResponseV30 struct {
 	Alerts
 }
 
+// DeliveryServicesResponseV4 is the type of a response from the
+// /api/4.0/deliveryservices Traffic Ops endpoint.
+type DeliveryServicesResponseV4 struct {
+	Response []DeliveryServiceV4 `json:"response"`
+	Alerts
+}
+
 // DeliveryServicesNullableResponse ...
 // Deprecated: Please only use the versioned structures.
 type DeliveryServicesNullableResponse struct {
@@ -173,8 +173,32 @@ type DeliveryServiceV11 struct {
 	XMLID                    string                 `json:"xmlId"`
 }
 
+type DeliveryServiceV40 struct {
+	DeliveryServiceFieldsV31
+	DeliveryServiceFieldsV30
+	DeliveryServiceFieldsV15
+	DeliveryServiceFieldsV14
+	DeliveryServiceFieldsV13
+	DeliveryServiceNullableFieldsV11
+}
+
+type DeliveryServiceV31 struct {
+	DeliveryServiceV30
+	DeliveryServiceFieldsV31
+}
+
+// DeliveryServiceFieldsV31 contains additions to delivery services in api v3.1
+type DeliveryServiceFieldsV31 struct {
+	MaxRequestHeaderBytes *int `json:"maxRequestHeaderBytes" db:"max_request_header_bytes"`
+}
+
 type DeliveryServiceV30 struct {
 	DeliveryServiceNullableV15
+	DeliveryServiceFieldsV30
+}
+
+// DeliveryServiceFieldsV30 contains additions to delivery services in api v3.0
+type DeliveryServiceFieldsV30 struct {
 	Topology           *string `json:"topology" db:"topology"`
 	FirstHeaderRewrite *string `json:"firstHeaderRewrite" db:"first_header_rewrite"`
 	InnerHeaderRewrite *string `json:"innerHeaderRewrite" db:"inner_header_rewrite"`
@@ -182,27 +206,35 @@ type DeliveryServiceV30 struct {
 	ServiceCategory    *string `json:"serviceCategory" db:"service_category"`
 }
 
-type DeliveryServiceV31 struct {
-	DeliveryServiceV30
-	MaxRequestHeaderBytes *int `json:"maxRequestHeaderBytes" db:"max_request_header_bytes"`
-}
-
 // DeliveryServiceNullableV30 is the aliased structure that we should be using for all api 3.x delivery structure operations
 // This type should always alias the latest 3.x minor version struct. For ex, if you wanted to create a DeliveryServiceV32 struct, you would do the following:
 // type DeliveryServiceNullableV30 DeliveryServiceV32
 // DeliveryServiceV32 = DeliveryServiceV31 + the new fields
 type DeliveryServiceNullableV30 DeliveryServiceV31
 
+// DeliveryServiceV4 is the aliased structure that we should be using for all api 4.x delivery structure operations
+type DeliveryServiceV4 DeliveryServiceV40
+
 // Deprecated: Use versioned structures only from now on.
 type DeliveryServiceNullable DeliveryServiceNullableV15
 type DeliveryServiceNullableV15 struct {
 	DeliveryServiceNullableV14
+	DeliveryServiceFieldsV15
+}
+
+// DeliveryServiceFieldsV15 contains additions to delivery services in api v1.5
+type DeliveryServiceFieldsV15 struct {
 	EcsEnabled          bool `json:"ecsEnabled" db:"ecs_enabled"`
 	RangeSliceBlockSize *int `json:"rangeSliceBlockSize" db:"range_slice_block_size"`
 }
 
 type DeliveryServiceNullableV14 struct {
 	DeliveryServiceNullableV13
+	DeliveryServiceFieldsV14
+}
+
+// DeliveryServiceFieldsV14 contains additions to delivery services in api v1.4
+type DeliveryServiceFieldsV14 struct {
 	ConsistentHashRegex       *string  `json:"consistentHashRegex"`
 	ConsistentHashQueryParams []string `json:"consistentHashQueryParams"`
 	MaxOriginConnections      *int     `json:"maxOriginConnections" db:"max_origin_connections"`
@@ -210,6 +242,11 @@ type DeliveryServiceNullableV14 struct {
 
 type DeliveryServiceNullableV13 struct {
 	DeliveryServiceNullableV12
+	DeliveryServiceFieldsV13
+}
+
+// DeliveryServiceFieldsV13 contains additions to delivery services in api v1.3
+type DeliveryServiceFieldsV13 struct {
 	DeepCachingType   *DeepCachingType `json:"deepCachingType" db:"deep_caching_type"`
 	FQPacingRate      *int             `json:"fqPacingRate" db:"fq_pacing_rate"`
 	SigningAlgorithm  *string          `json:"signingAlgorithm" db:"signing_algorithm"`
@@ -226,11 +263,13 @@ type DeliveryServiceNullableV12 struct {
 // for all fields to be null.
 // TODO move contents to DeliveryServiceNullableV12, fix references, and remove
 type DeliveryServiceNullableV11 struct {
-	// NOTE: the db: struct tags are used for testing to map to their equivalent database column (if there is one)
-	//
+	DeliveryServiceNullableFieldsV11
+	DeliveryServiceRemovedFieldsV11
+}
+
+type DeliveryServiceNullableFieldsV11 struct {
 	Active                   *bool                   `json:"active" db:"active"`
 	AnonymousBlockingEnabled *bool                   `json:"anonymousBlockingEnabled" db:"anonymous_blocking_enabled"`
-	CacheURL                 *string                 `json:"cacheurl" db:"cacheurl"`
 	CCRDNSTTL                *int                    `json:"ccrDnsTtl" db:"ccr_dns_ttl"`
 	CDNID                    *int                    `json:"cdnId" db:"cdn_id"`
 	CDNName                  *string                 `json:"cdnName"`
@@ -285,324 +324,46 @@ type DeliveryServiceNullableV11 struct {
 	ExampleURLs              []string                `json:"exampleURLs"`
 }
 
-func requiredIfMatchesTypeName(patterns []string, typeName string) func(interface{}) error {
-	return func(value interface{}) error {
-		switch v := value.(type) {
-		case *int:
-			if v != nil {
-				return nil
-			}
-		case *bool:
-			if v != nil {
-				return nil
-			}
-		case *string:
-			if v != nil {
-				return nil
-			}
-		case *float64:
-			if v != nil {
-				return nil
-			}
-		default:
-			return fmt.Errorf("validation failure: unknown type %T", value)
-		}
-		pattern := strings.Join(patterns, "|")
-		err := error(nil)
-		match := false
-		if typeName != "" {
-			match, err = regexp.MatchString(pattern, typeName)
-			if match {
-				return fmt.Errorf("is required if type is '%s'", typeName)
-			}
-		}
-		return err
-	}
-}
-
-func validateOrgServerFQDN(orgServerFQDN string) bool {
-	_, fqdn, port, err := ParseOrgServerFQDN(orgServerFQDN)
-	if err != nil || !govalidator.IsHost(*fqdn) || (port != nil && !govalidator.IsPort(*port)) {
-		return false
-	}
-	return true
-}
-
-func ParseOrgServerFQDN(orgServerFQDN string) (*string, *string, *string, error) {
-	originRegex := regexp.MustCompile(`^(https?)://([^:]+)(:(\d+))?$`)
-	matches := originRegex.FindStringSubmatch(orgServerFQDN)
-	if len(matches) == 0 {
-		return nil, nil, nil, fmt.Errorf("unable to parse invalid orgServerFqdn: '%s'", orgServerFQDN)
-	}
-
-	protocol := strings.ToLower(matches[1])
-	FQDN := matches[2]
-
-	if len(protocol) == 0 || len(FQDN) == 0 {
-		return nil, nil, nil, fmt.Errorf("empty Origin protocol or FQDN parsed from '%s'", orgServerFQDN)
-	}
-
-	var port *string
-	if len(matches[4]) != 0 {
-		port = &matches[4]
-	}
-	return &protocol, &FQDN, port, nil
-}
-
-func (ds *DeliveryServiceNullableV30) Sanitize() {
-	if ds.GeoLimitCountries != nil {
-		*ds.GeoLimitCountries = strings.ToUpper(strings.Replace(*ds.GeoLimitCountries, " ", "", -1))
-	}
-	if ds.ProfileID != nil && *ds.ProfileID == -1 {
-		ds.ProfileID = nil
-	}
-	setNilIfEmpty(
-		&ds.EdgeHeaderRewrite,
-		&ds.MidHeaderRewrite,
-		&ds.FirstHeaderRewrite,
-		&ds.InnerHeaderRewrite,
-		&ds.LastHeaderRewrite,
-	)
-	if ds.RoutingName == nil || *ds.RoutingName == "" {
-		ds.RoutingName = util.StrPtr(DefaultRoutingName)
-	}
-	if ds.AnonymousBlockingEnabled == nil {
-		ds.AnonymousBlockingEnabled = util.BoolPtr(false)
-	}
-	signedAlgorithm := SigningAlgorithmURLSig
-	if ds.Signed && (ds.SigningAlgorithm == nil || *ds.SigningAlgorithm == "") {
-		ds.SigningAlgorithm = &signedAlgorithm
-	}
-	if !ds.Signed && ds.SigningAlgorithm != nil && *ds.SigningAlgorithm == signedAlgorithm {
-		ds.Signed = true
-	}
-	if ds.MaxOriginConnections == nil || *ds.MaxOriginConnections < 0 {
-		ds.MaxOriginConnections = util.IntPtr(0)
-	}
-	if ds.DeepCachingType == nil {
-		s := DeepCachingType("")
-		ds.DeepCachingType = &s
-	}
-	*ds.DeepCachingType = DeepCachingTypeFromString(string(*ds.DeepCachingType))
-	if ds.MaxRequestHeaderBytes == nil {
-		ds.MaxRequestHeaderBytes = util.IntPtr(DefaultMaxRequestHeaderBytes)
-	}
-}
-
-func setNilIfEmpty(ptrs ...**string) {
-	for _, s := range ptrs {
-		if *s != nil && strings.TrimSpace(**s) == "" {
-			*s = nil
-		}
+// Deprecated: used for backwards compatibility  with ATC <v5.1
+// DeliveryServiceRemovedFieldsV11 contains additions to delivery services in api v1.1 that were later removed
+type DeliveryServiceRemovedFieldsV11 struct {
+	CacheURL *string `json:"cacheurl" db:"cacheurl"`
+}
+
+// DowngradeToV3 converts the 4.x DS to a 3.x DS
+func (ds *DeliveryServiceV4) DowngradeToV3() DeliveryServiceNullableV30 {
+	return DeliveryServiceNullableV30{
+		DeliveryServiceV30: DeliveryServiceV30{
+			DeliveryServiceNullableV15: DeliveryServiceNullableV15{
+				DeliveryServiceNullableV14: DeliveryServiceNullableV14{
+					DeliveryServiceNullableV13: DeliveryServiceNullableV13{
+						DeliveryServiceNullableV12: DeliveryServiceNullableV12{
+							DeliveryServiceNullableV11: DeliveryServiceNullableV11{
+								DeliveryServiceNullableFieldsV11: ds.DeliveryServiceNullableFieldsV11,
+							},
+						},
+						DeliveryServiceFieldsV13: ds.DeliveryServiceFieldsV13,
+					},
+					DeliveryServiceFieldsV14: ds.DeliveryServiceFieldsV14,
+				},
+				DeliveryServiceFieldsV15: ds.DeliveryServiceFieldsV15,
+			},
+			DeliveryServiceFieldsV30: ds.DeliveryServiceFieldsV30,
+		},
+		DeliveryServiceFieldsV31: ds.DeliveryServiceFieldsV31,
 	}
 }
 
-func (ds *DeliveryServiceNullable) validateTypeFields(tx *sql.Tx) error {
-	// Validate the TypeName related fields below
-	err := error(nil)
-	DNSRegexType := "^DNS.*$"
-	HTTPRegexType := "^HTTP.*$"
-	SteeringRegexType := "^STEERING.*$"
-	latitudeErr := "Must be a floating point number within the range +-90"
-	longitudeErr := "Must be a floating point number within the range +-180"
-
-	typeName, err := ValidateTypeID(tx, ds.TypeID, "deliveryservice")
-	if err != nil {
-		return err
-	}
-
-	errs := validation.Errors{
-		"consistentHashQueryParams": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullable)
-				if len(ds.ConsistentHashQueryParams) == 0 || DSType(typeName).IsHTTP() {
-					return nil
-				}
-				return fmt.Errorf("consistentHashQueryParams not allowed for '%s' deliveryservice type", typeName)
-			})),
-		"initialDispersion": validation.Validate(ds.InitialDispersion,
-			validation.By(requiredIfMatchesTypeName([]string{HTTPRegexType}, typeName)),
-			validation.By(tovalidate.IsGreaterThanZero)),
-		"ipv6RoutingEnabled": validation.Validate(ds.IPV6RoutingEnabled,
-			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
-		"missLat": validation.Validate(ds.MissLat,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.Min(-90.0).Error(latitudeErr),
-			validation.Max(90.0).Error(latitudeErr)),
-		"missLong": validation.Validate(ds.MissLong,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.Min(-180.0).Error(longitudeErr),
-			validation.Max(180.0).Error(longitudeErr)),
-		"multiSiteOrigin": validation.Validate(ds.MultiSiteOrigin,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-		"orgServerFqdn": validation.Validate(ds.OrgServerFQDN,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.NewStringRule(validateOrgServerFQDN, "must start with http:// or https:// and be followed by a valid hostname with an optional port (no trailing slash)")),
-		"rangeSliceBlockSize": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullable)
-				if ds.RangeRequestHandling != nil {
-					if *ds.RangeRequestHandling == 3 {
-						return validation.Validate(ds.RangeSliceBlockSize, validation.Required,
-							// Per Slice Plugin implementation
-							validation.Min(MinRangeSliceBlockSize), // 256KiB
-							validation.Max(MaxRangeSliceBlockSize), // 32MiB
-						)
-					}
-					if ds.RangeSliceBlockSize != nil {
-						return errors.New("rangeSliceBlockSize can only be set if the rangeRequestHandling is set to 3 (Use the Slice Plugin)")
-					}
-				}
-				return nil
-			})),
-		"protocol": validation.Validate(ds.Protocol,
-			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
-		"qstringIgnore": validation.Validate(ds.QStringIgnore,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-		"rangeRequestHandling": validation.Validate(ds.RangeRequestHandling,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-	}
-	toErrs := tovalidate.ToErrors(errs)
-	if len(toErrs) > 0 {
-		return errors.New(util.JoinErrsStr(toErrs))
+// UpgradeToV4 converts the 3.x DS to a 4.x DS
+func (ds *DeliveryServiceNullableV30) UpgradeToV4() DeliveryServiceV4 {
+	return DeliveryServiceV4{
+		DeliveryServiceFieldsV31:         ds.DeliveryServiceFieldsV31,
+		DeliveryServiceFieldsV30:         ds.DeliveryServiceFieldsV30,
+		DeliveryServiceFieldsV15:         ds.DeliveryServiceFieldsV15,
+		DeliveryServiceFieldsV14:         ds.DeliveryServiceFieldsV14,
+		DeliveryServiceFieldsV13:         ds.DeliveryServiceFieldsV13,
+		DeliveryServiceNullableFieldsV11: ds.DeliveryServiceNullableFieldsV11,
 	}
-	return nil
-}
-
-func (ds *DeliveryServiceNullableV30) validateTypeFields(tx *sql.Tx) error {
-	// Validate the TypeName related fields below
-	err := error(nil)
-	DNSRegexType := "^DNS.*$"
-	HTTPRegexType := "^HTTP.*$"
-	SteeringRegexType := "^STEERING.*$"
-	latitudeErr := "Must be a floating point number within the range +-90"
-	longitudeErr := "Must be a floating point number within the range +-180"
-
-	typeName, err := ValidateTypeID(tx, ds.TypeID, "deliveryservice")
-	if err != nil {
-		return err
-	}
-
-	errs := validation.Errors{
-		"consistentHashQueryParams": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullableV30)
-				if len(ds.ConsistentHashQueryParams) == 0 || DSType(typeName).IsHTTP() {
-					return nil
-				}
-				return fmt.Errorf("consistentHashQueryParams not allowed for '%s' deliveryservice type", typeName)
-			})),
-		"initialDispersion": validation.Validate(ds.InitialDispersion,
-			validation.By(requiredIfMatchesTypeName([]string{HTTPRegexType}, typeName)),
-			validation.By(tovalidate.IsGreaterThanZero)),
-		"ipv6RoutingEnabled": validation.Validate(ds.IPV6RoutingEnabled,
-			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
-		"missLat": validation.Validate(ds.MissLat,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.Min(-90.0).Error(latitudeErr),
-			validation.Max(90.0).Error(latitudeErr)),
-		"missLong": validation.Validate(ds.MissLong,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.Min(-180.0).Error(longitudeErr),
-			validation.Max(180.0).Error(longitudeErr)),
-		"multiSiteOrigin": validation.Validate(ds.MultiSiteOrigin,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-		"orgServerFqdn": validation.Validate(ds.OrgServerFQDN,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
-			validation.NewStringRule(validateOrgServerFQDN, "must start with http:// or https:// and be followed by a valid hostname with an optional port (no trailing slash)")),
-		"rangeSliceBlockSize": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullableV30)
-				if ds.RangeRequestHandling != nil {
-					if *ds.RangeRequestHandling == 3 {
-						return validation.Validate(ds.RangeSliceBlockSize, validation.Required,
-							// Per Slice Plugin implementation
-							validation.Min(MinRangeSliceBlockSize), // 256KiB
-							validation.Max(MaxRangeSliceBlockSize), // 32MiB
-						)
-					}
-					if ds.RangeSliceBlockSize != nil {
-						return errors.New("rangeSliceBlockSize can only be set if the rangeRequestHandling is set to 3 (Use the Slice Plugin)")
-					}
-				}
-				return nil
-			})),
-		"protocol": validation.Validate(ds.Protocol,
-			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
-		"qstringIgnore": validation.Validate(ds.QStringIgnore,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-		"rangeRequestHandling": validation.Validate(ds.RangeRequestHandling,
-			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
-		"topology": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullableV30)
-				if ds.Topology != nil && DSType(typeName).IsSteering() {
-					return fmt.Errorf("steering deliveryservice types cannot be assigned to a topology")
-				}
-				return nil
-			})),
-		"maxRequestHeaderBytes": validation.Validate(ds,
-			validation.By(func(dsi interface{}) error {
-				ds := dsi.(*DeliveryServiceNullableV30)
-				if ds.MaxRequestHeaderBytes == nil {
-					return errors.New("maxRequestHeaderBytes empty, must be a valid positive value")
-				}
-				if *ds.MaxRequestHeaderBytes < 0 || *ds.MaxRequestHeaderBytes > 2147483647 {
-					return errors.New("maxRequestHeaderBytes must be a valid non negative value between 0 and 2147483647")
-				}
-				return nil
-			})),
-	}
-	toErrs := tovalidate.ToErrors(errs)
-	if len(toErrs) > 0 {
-		return errors.New(util.JoinErrsStr(toErrs))
-	}
-	return nil
-}
-
-func (ds *DeliveryServiceNullableV30) Validate(tx *sql.Tx) error {
-	ds.Sanitize()
-	neverOrAlways := validation.NewStringRule(tovalidate.IsOneOfStringICase("NEVER", "ALWAYS"),
-		"must be one of 'NEVER' or 'ALWAYS'")
-	isDNSName := validation.NewStringRule(govalidator.IsDNSName, "must be a valid hostname")
-	noPeriods := validation.NewStringRule(tovalidate.NoPeriods, "cannot contain periods")
-	noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot contain spaces")
-	noLineBreaks := validation.NewStringRule(tovalidate.NoLineBreaks, "cannot contain line breaks")
-	errs := tovalidate.ToErrors(validation.Errors{
-		"active":              validation.Validate(ds.Active, validation.NotNil),
-		"cdnId":               validation.Validate(ds.CDNID, validation.Required),
-		"deepCachingType":     validation.Validate(ds.DeepCachingType, neverOrAlways),
-		"displayName":         validation.Validate(ds.DisplayName, validation.Required, validation.Length(1, 48)),
-		"dscp":                validation.Validate(ds.DSCP, validation.NotNil, validation.Min(0)),
-		"geoLimit":            validation.Validate(ds.GeoLimit, validation.NotNil),
-		"geoProvider":         validation.Validate(ds.GeoProvider, validation.NotNil),
-		"logsEnabled":         validation.Validate(ds.LogsEnabled, validation.NotNil),
-		"regionalGeoBlocking": validation.Validate(ds.RegionalGeoBlocking, validation.NotNil),
-		"remapText":           validation.Validate(ds.RemapText, noLineBreaks),
-		"routingName":         validation.Validate(ds.RoutingName, isDNSName, noPeriods, validation.Length(1, 48)),
-		"typeId":              validation.Validate(ds.TypeID, validation.Required, validation.Min(1)),
-		"xmlId":               validation.Validate(ds.XMLID, validation.Required, noSpaces, noPeriods, validation.Length(1, 48)),
-	})
-	if err := ds.validateTopologyFields(); err != nil {
-		errs = append(errs, err)
-	}
-	if err := ds.validateTypeFields(tx); err != nil {
-		errs = append(errs, errors.New("type fields: "+err.Error()))
-	}
-	if len(errs) == 0 {
-		return nil
-	}
-	return util.JoinErrs(errs)
-}
-
-func (ds *DeliveryServiceNullableV30) validateTopologyFields() error {
-	if ds.Topology != nil && (ds.EdgeHeaderRewrite != nil || ds.MidHeaderRewrite != nil) {
-		return errors.New("cannot set edgeHeaderRewrite or midHeaderRewrite while a Topology is assigned. Use firstHeaderRewrite, innerHeaderRewrite, and/or lastHeaderRewrite instead")
-	}
-	if ds.Topology == nil && (ds.FirstHeaderRewrite != nil || ds.InnerHeaderRewrite != nil || ds.LastHeaderRewrite != nil) {
-		return errors.New("cannot set firstHeaderRewrite, innerHeaderRewrite, or lastHeaderRewrite unless this delivery service is assigned to a Topology. Use edgeHeaderRewrite and/or midHeaderRewrite instead")
-	}
-	return nil
 }
 
 func jsonValue(v interface{}) (driver.Value, error) {
@@ -636,13 +397,13 @@ func (ds *DeliveryServiceNullable) Scan(src interface{}) error {
 
 // Value implements the driver.Valuer interface --
 // marshals struct to json to pass back as a json.RawMessage.
-func (ds *DeliveryServiceNullableV30) Value() (driver.Value, error) {
+func (ds *DeliveryServiceV4) Value() (driver.Value, error) {
 	return jsonValue(ds)
 }
 
 // Scan implements the sql.Scanner interface --
-// expects json.RawMessage and unmarshals to a DeliveryServiceNullableV30 struct.
-func (ds *DeliveryServiceNullableV30) Scan(src interface{}) error {
+// expects json.RawMessage and unmarshals to a DeliveryServiceV4 struct.
+func (ds *DeliveryServiceV4) Scan(src interface{}) error {
 	return jsonScan(src, ds)
 }
 
diff --git a/traffic_ops/client b/traffic_ops/client
deleted file mode 120000
index 4d55042..0000000
--- a/traffic_ops/client
+++ /dev/null
@@ -1 +0,0 @@
-v3-client
\ No newline at end of file
diff --git a/traffic_ops/testing/api/v1/federation_users_test.go b/traffic_ops/testing/api/v1/federation_users_test.go
index 2ec7417..bb2e66a 100644
--- a/traffic_ops/testing/api/v1/federation_users_test.go
+++ b/traffic_ops/testing/api/v1/federation_users_test.go
@@ -23,7 +23,7 @@ func TestFederationUsers(t *testing.T) {
 	if Config.NoPerl {
 		t.Skip("No Perl instance for proxying")
 	}
-	WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, DeliveryServices, UsersDeliveryServices, CDNFederations, FederationUsers}, func() {
+	WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, DeliveryServices, CDNFederations, FederationUsers}, func() {
 		CreateTestInvalidFederationUsers(t)
 		GetTestInvalidFederationIDUsers(t)
 	})
diff --git a/traffic_ops/testing/api/v1/federations_test.go b/traffic_ops/testing/api/v1/federations_test.go
index 28c608b..b30822e 100644
--- a/traffic_ops/testing/api/v1/federations_test.go
+++ b/traffic_ops/testing/api/v1/federations_test.go
@@ -27,7 +27,7 @@ func TestFederations(t *testing.T) {
 	if Config.NoPerl {
 		t.Skip("No Perl instance for proxying")
 	}
-	WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, DeliveryServices, UsersDeliveryServices, CDNFederations}, func() {
+	WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, DeliveryServices, CDNFederations}, func() {
 		PostDeleteTestFederationsDeliveryServices(t)
 		GetTestFederations(t)
 		AddFederationResolversForCurrentUserTest(t)
diff --git a/traffic_ops/testing/api/v4/cachegroupsdeliveryservices_test.go b/traffic_ops/testing/api/v4/cachegroupsdeliveryservices_test.go
index 6236f8b..096d6dd 100644
--- a/traffic_ops/testing/api/v4/cachegroupsdeliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/cachegroupsdeliveryservices_test.go
@@ -42,7 +42,7 @@ func CreateTestCachegroupsDeliveryServices(t *testing.T) {
 		t.Fatalf("cannot test cachegroups delivery services: expected no initial delivery service servers, actual %v", len(dss.Response))
 	}
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET DeliveryServices: %v - %v", err, dses)
 	}
@@ -156,7 +156,7 @@ func setInactive(t *testing.T, dsID int) {
 	}
 	if *ds.Active {
 		*ds.Active = false
-		_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(dsID, *ds, nil)
+		_, _, err = TOSession.UpdateDeliveryServiceV4(dsID, *ds, nil)
 		if err != nil {
 			t.Errorf("Failed to set Delivery Service #%d to inactive: %v", dsID, err)
 		}
diff --git a/traffic_ops/testing/api/v4/cookie_test.go b/traffic_ops/testing/api/v4/cookie_test.go
index 0446d71..19a11c4 100644
--- a/traffic_ops/testing/api/v4/cookie_test.go
+++ b/traffic_ops/testing/api/v4/cookie_test.go
@@ -32,7 +32,7 @@ func TestCookies(t *testing.T) {
 }
 
 func CookiesTest(t *testing.T) {
-	s, _, err := toclient.LoginWithAgent(Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, true, "to-api-v3-client-tests", false, toReqTimeout)
+	s, _, err := toclient.LoginWithAgent(Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
 	credentials := tc.UserCredentials{
 		Username: Config.TrafficOps.Users.Admin,
 		Password: Config.TrafficOps.UserPassword,
diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go b/traffic_ops/testing/api/v4/deliveryservices_test.go
index f7fd630..4420919 100644
--- a/traffic_ops/testing/api/v4/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/deliveryservices_test.go
@@ -29,7 +29,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/lib/go-util"
-	toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
+	toclient "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
 func TestDeliveryServices(t *testing.T) {
@@ -142,7 +142,7 @@ func createBlankCDN(cdnName string, t *testing.T) tc.CDN {
 	return cdns[0]
 }
 
-func cleanUp(t *testing.T, ds tc.DeliveryServiceNullableV30, oldCDNID int, newCDNID int) {
+func cleanUp(t *testing.T, ds tc.DeliveryServiceV4, oldCDNID int, newCDNID int) {
 	_, _, err := TOSession.DeleteDeliveryServiceSSLKeysByID(*ds.XMLID)
 	if err != nil {
 		t.Error(err)
@@ -175,7 +175,7 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 		t.Fatal("expected at least one type")
 	}
 
-	customDS := tc.DeliveryServiceNullableV30{}
+	customDS := tc.DeliveryServiceV4{}
 	customDS.Active = util.BoolPtr(true)
 	customDS.CDNID = util.IntPtr(oldCdn.ID)
 	customDS.DSCP = util.IntPtr(0)
@@ -199,7 +199,7 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 	customDS.XMLID = util.StrPtr("dsID")
 	customDS.MaxRequestHeaderBytes = nil
 
-	ds, _, err := TOSession.CreateDeliveryServiceV30(customDS)
+	ds, _, err := TOSession.CreateDeliveryServiceV4(customDS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -242,7 +242,7 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 	}
 
 	ds.RoutingName = util.StrPtr("anothername")
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err == nil {
 		t.Fatal("should not be able to update delivery service (routing name) as it has ssl keys")
 	}
@@ -250,7 +250,7 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 
 	ds.CDNID = &newCdn.ID
 	ds.CDNName = &newCdn.Name
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err == nil {
 		t.Fatal("should not be able to update delivery service (cdn) as it has ssl keys")
 	}
@@ -275,7 +275,7 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 }
 
 func GetTestDeliveryServicesIMSAfterChange(t *testing.T, header http.Header) {
-	_, reqInf, err := TOSession.GetDeliveryServicesV30WithHdr(header, nil)
+	_, reqInf, err := TOSession.GetDeliveryServicesV4(header, nil)
 	if err != nil {
 		t.Fatalf("could not GET Delivery Services: %v", err)
 	}
@@ -286,7 +286,7 @@ func GetTestDeliveryServicesIMSAfterChange(t *testing.T, header http.Header) {
 	currentTime = currentTime.Add(1 * time.Second)
 	timeStr := currentTime.Format(time.RFC1123)
 	header.Set(rfc.IfModifiedSince, timeStr)
-	_, reqInf, err = TOSession.GetDeliveryServicesV30WithHdr(header, nil)
+	_, reqInf, err = TOSession.GetDeliveryServicesV4(header, nil)
 	if err != nil {
 		t.Fatalf("could not GET Delivery Services: %v", err)
 	}
@@ -306,12 +306,12 @@ func PostDeliveryServiceTest(t *testing.T) {
 	xmlid := *ds.XMLID + "-topology-test"
 
 	ds.XMLID = util.StrPtr("")
-	_, _, err := TOSession.CreateDeliveryServiceV30(ds)
+	_, _, err := TOSession.CreateDeliveryServiceV4(ds)
 	if err == nil {
 		t.Error("Expected error with empty xmlid")
 	}
 	ds.XMLID = nil
-	_, _, err = TOSession.CreateDeliveryServiceV30(ds)
+	_, _, err = TOSession.CreateDeliveryServiceV4(ds)
 	if err == nil {
 		t.Error("Expected error with nil xmlid")
 	}
@@ -319,7 +319,7 @@ func PostDeliveryServiceTest(t *testing.T) {
 	ds.Topology = new(string)
 	ds.XMLID = &xmlid
 
-	_, reqInf, err := TOSession.CreateDeliveryServiceV30(ds)
+	_, reqInf, err := TOSession.CreateDeliveryServiceV4(ds)
 	if err == nil {
 		t.Error("Expected error with non-existent Topology")
 	}
@@ -339,7 +339,7 @@ func CreateTestDeliveryServices(t *testing.T) {
 		t.Errorf("cannot create parameter: %v", err)
 	}
 	for _, ds := range testData.DeliveryServices {
-		_, _, err = TOSession.CreateDeliveryServiceV30(ds)
+		_, _, err = TOSession.CreateDeliveryServiceV4(ds)
 		if err != nil {
 			t.Errorf("could not CREATE delivery service '%s': %v", *ds.XMLID, err)
 		}
@@ -352,7 +352,7 @@ func GetTestDeliveryServicesIMS(t *testing.T) {
 	futureTime := time.Now().AddDate(0, 0, 1)
 	time := futureTime.Format(time.RFC1123)
 	header.Set(rfc.IfModifiedSince, time)
-	_, reqInf, err := TOSession.GetDeliveryServicesV30WithHdr(header, nil)
+	_, reqInf, err := TOSession.GetDeliveryServicesV4(header, nil)
 	if err != nil {
 		t.Fatalf("could not GET Delivery Services: %v", err)
 	}
@@ -362,11 +362,11 @@ func GetTestDeliveryServicesIMS(t *testing.T) {
 }
 
 func GetTestDeliveryServices(t *testing.T) {
-	actualDSes, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	actualDSes, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET DeliveryServices: %v - %v", err, actualDSes)
 	}
-	actualDSMap := make(map[string]tc.DeliveryServiceNullableV30, len(actualDSes))
+	actualDSMap := make(map[string]tc.DeliveryServiceV4, len(actualDSes))
 	for _, ds := range actualDSes {
 		actualDSMap[*ds.XMLID] = ds
 	}
@@ -391,7 +391,7 @@ func GetTestDeliveryServices(t *testing.T) {
 func GetInactiveTestDeliveryServices(t *testing.T) {
 	params := url.Values{}
 	params.Set("active", strconv.FormatBool(false))
-	inactiveDSes, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	inactiveDSes, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Errorf("cannot GET DeliveryServices: %v - %v", err, inactiveDSes)
 	}
@@ -401,7 +401,7 @@ func GetInactiveTestDeliveryServices(t *testing.T) {
 		}
 	}
 	params.Set("active", strconv.FormatBool(true))
-	activeDSes, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	activeDSes, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Errorf("cannot GET DeliveryServices: %v - %v", err, activeDSes)
 	}
@@ -413,11 +413,11 @@ func GetInactiveTestDeliveryServices(t *testing.T) {
 }
 
 func GetTestDeliveryServicesCapacity(t *testing.T) {
-	actualDSes, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	actualDSes, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET DeliveryServices: %v - %v", err, actualDSes)
 	}
-	actualDSMap := map[string]tc.DeliveryServiceNullableV30{}
+	actualDSMap := map[string]tc.DeliveryServiceV4{}
 	for _, ds := range actualDSes {
 		actualDSMap[*ds.XMLID] = ds
 		capDS, _, err := TOSession.GetDeliveryServiceCapacityWithHdr(strconv.Itoa(*ds.ID), nil)
@@ -431,12 +431,12 @@ func GetTestDeliveryServicesCapacity(t *testing.T) {
 func UpdateTestDeliveryServices(t *testing.T) {
 	firstDS := testData.DeliveryServices[0]
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET Delivery Services: %v", err)
 	}
 
-	remoteDS := tc.DeliveryServiceNullableV30{}
+	remoteDS := tc.DeliveryServiceV4{}
 	found := false
 	for _, ds := range dses {
 		if *ds.XMLID == *firstDS.XMLID {
@@ -459,14 +459,14 @@ func UpdateTestDeliveryServices(t *testing.T) {
 	remoteDS.MatchList = nil // verify that this field is optional in a PUT request, doesn't cause nil dereference panic
 	remoteDS.MaxRequestHeaderBytes = &updatedMaxRequestHeaderSize
 
-	if updateResp, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS.ID, remoteDS, nil); err != nil {
+	if updateResp, _, err := TOSession.UpdateDeliveryServiceV4(*remoteDS.ID, remoteDS, nil); err != nil {
 		t.Errorf("cannot UPDATE DeliveryService by ID: %v - %v", err, updateResp)
 	}
 
 	// Retrieve the server to check rack and interfaceName values were updated
 	params := url.Values{}
 	params.Set("id", strconv.Itoa(*remoteDS.ID))
-	apiResp, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	apiResp, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Service by ID: %v - %v", remoteDS.XMLID, err)
 	}
@@ -486,12 +486,12 @@ func UpdateTestDeliveryServices(t *testing.T) {
 func UpdateNullableTestDeliveryServices(t *testing.T) {
 	firstDS := testData.DeliveryServices[0]
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Services: %v", err)
 	}
 
-	var remoteDS tc.DeliveryServiceNullableV30
+	var remoteDS tc.DeliveryServiceV4
 	found := false
 	for _, ds := range dses {
 		if ds.XMLID == nil || ds.ID == nil {
@@ -512,13 +512,13 @@ func UpdateNullableTestDeliveryServices(t *testing.T) {
 	remoteDS.LongDesc = &updatedLongDesc
 	remoteDS.MaxDNSAnswers = &updatedMaxDNSAnswers
 
-	if updateResp, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS.ID, remoteDS, nil); err != nil {
+	if updateResp, _, err := TOSession.UpdateDeliveryServiceV4(*remoteDS.ID, remoteDS, nil); err != nil {
 		t.Fatalf("cannot UPDATE DeliveryService by ID: %v - %v", err, updateResp)
 	}
 
 	params := url.Values{}
 	params.Set("id", strconv.Itoa(*remoteDS.ID))
-	apiResp, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	apiResp, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Service by ID: %v - %v", remoteDS.XMLID, err)
 	}
@@ -542,13 +542,13 @@ func UpdateNullableTestDeliveryServices(t *testing.T) {
 // - assigned to (CLIENT_)STEERING delivery services
 // - assigned to any delivery services which have required capabilities that the topology can't satisfy
 func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Services: %v", err)
 	}
 
 	found := false
-	var nonCSDS *tc.DeliveryServiceNullableV30
+	var nonCSDS *tc.DeliveryServiceV4
 	for _, ds := range dses {
 		if ds.Type == nil || ds.ID == nil {
 			continue
@@ -556,11 +556,11 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 		if *ds.Type == tc.DSTypeClientSteering {
 			found = true
 			ds.Topology = util.StrPtr("my-topology")
-			if _, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil); err == nil {
+			if _, _, err := TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil); err == nil {
 				t.Errorf("assigning topology to CLIENT_STEERING delivery service - expected: error, actual: no error")
 			}
 		} else if nonCSDS == nil {
-			nonCSDS = new(tc.DeliveryServiceNullableV30)
+			nonCSDS = new(tc.DeliveryServiceV4)
 			*nonCSDS = ds
 		}
 	}
@@ -572,7 +572,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 	}
 
 	nonCSDS.Topology = new(string)
-	_, inf, err := TOSession.UpdateDeliveryServiceV30WithHdr(*nonCSDS.ID, *nonCSDS, nil)
+	_, inf, err := TOSession.UpdateDeliveryServiceV4(*nonCSDS.ID, *nonCSDS, nil)
 	if err == nil {
 		t.Error("Expected an error assigning a non-existent topology")
 	}
@@ -582,7 +582,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 
 	params := url.Values{}
 	params.Add("xmlId", "ds-top-req-cap")
-	dses, _, err = TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	dses, _, err = TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery service: %v", err)
 	}
@@ -594,7 +594,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 	// can't satisfy, then attempt to reassign its topology
 	top := *ds.Topology
 	ds.Topology = nil
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Fatalf("updating DS to remove topology, expected: no error, actual: %v", err)
 	}
@@ -607,7 +607,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 		t.Fatalf("adding 'asdf' required capability to '%s', expected: no error, actual: %v", *ds.XMLID, err)
 	}
 	ds.Topology = &top
-	_, reqInf, err := TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, reqInf, err := TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err == nil {
 		t.Errorf("updating DS topology which doesn't meet the DS required capabilities - expected: error, actual: nil")
 	}
@@ -618,13 +618,13 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 	if err != nil {
 		t.Fatalf("removing 'asdf' required capability from '%s', expected: no error, actual: %v", *ds.XMLID, err)
 	}
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Errorf("updating DS topology - expected: no error, actual: %v", err)
 	}
 
 	const xmlId = "top-ds-in-cdn2"
-	dses, _, err = TOSession.GetDeliveryServicesV30WithHdr(nil, url.Values{"xmlId": {xmlId}})
+	dses, _, err = TOSession.GetDeliveryServicesV4(nil, url.Values{"xmlId": {xmlId}})
 	if err != nil {
 		t.Fatalf("getting Delivery Service %s: %s", xmlId, err.Error())
 	}
@@ -635,7 +635,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 	ds = dses[0]
 	dsTopology := ds.Topology
 	ds.Topology = nil
-	ds, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	ds, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Fatalf("updating Delivery Service %s: %s", xmlId, err.Error())
 	}
@@ -701,7 +701,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 		t.Fatalf("updating Server %s: %s", *server.HostName, err.Error())
 	}
 	ds.Topology = dsTopology
-	_, reqInf, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	_, reqInf, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err == nil {
 		t.Fatalf("expected 400-level error assigning Topology %s to Delivery Service %s because Cache Group %s has no Servers in it in CDN %d, no error received", *dsTopology, xmlId, cacheGroupName, *ds.CDNID)
 	}
@@ -722,7 +722,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 		t.Fatalf("deleting Profile %s: %s", profile.Name, err.Error())
 	}
 
-	ds, reqInf, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	ds, reqInf, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Fatalf("updating Delivery Service %s: %s", xmlId, err.Error())
 	}
@@ -731,7 +731,7 @@ func UpdateDeliveryServiceWithInvalidTopology(t *testing.T) {
 // UpdateDeliveryServiceTopologyHeaderRewriteFields ensures that a delivery service can only use firstHeaderRewrite,
 // innerHeaderRewrite, or lastHeadeRewrite if a topology is assigned.
 func UpdateDeliveryServiceTopologyHeaderRewriteFields(t *testing.T) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Services: %v", err)
 	}
@@ -743,7 +743,7 @@ func UpdateDeliveryServiceTopologyHeaderRewriteFields(t *testing.T) {
 		ds.FirstHeaderRewrite = util.StrPtr("foo")
 		ds.InnerHeaderRewrite = util.StrPtr("bar")
 		ds.LastHeaderRewrite = util.StrPtr("baz")
-		_, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+		_, _, err := TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 		if ds.Topology != nil && err != nil {
 			t.Errorf("expected: no error updating topology-based header rewrite fields for topology-based DS, actual: %v", err)
 		}
@@ -755,7 +755,7 @@ func UpdateDeliveryServiceTopologyHeaderRewriteFields(t *testing.T) {
 		ds.LastHeaderRewrite = nil
 		ds.EdgeHeaderRewrite = util.StrPtr("foo")
 		ds.MidHeaderRewrite = util.StrPtr("bar")
-		_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+		_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 		if ds.Topology != nil && err == nil {
 			t.Errorf("expected: error updating legacy header rewrite fields for topology-based DS, actual: nil")
 		}
@@ -772,12 +772,12 @@ func UpdateDeliveryServiceTopologyHeaderRewriteFields(t *testing.T) {
 func UpdateDeliveryServiceWithInvalidRemapText(t *testing.T) {
 	firstDS := testData.DeliveryServices[0]
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Services: %v", err)
 	}
 
-	var remoteDS tc.DeliveryServiceNullableV30
+	var remoteDS tc.DeliveryServiceV4
 	found := false
 	for _, ds := range dses {
 		if ds.XMLID == nil || ds.ID == nil {
@@ -796,7 +796,7 @@ func UpdateDeliveryServiceWithInvalidRemapText(t *testing.T) {
 	updatedRemapText := "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua\nline2"
 	remoteDS.RemapText = &updatedRemapText
 
-	if _, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS.ID, remoteDS, nil); err == nil {
+	if _, _, err := TOSession.UpdateDeliveryServiceV4(*remoteDS.ID, remoteDS, nil); err == nil {
 		t.Errorf("Delivery service updated with invalid remap text: %v", updatedRemapText)
 	}
 }
@@ -815,12 +815,12 @@ func UpdateDeliveryServiceWithInvalidSliceRangeRequest(t *testing.T) {
 		t.Fatal("no HTTP or DNS Delivery Services to test with")
 	}
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Services: %v", err)
 	}
 
-	var remoteDS tc.DeliveryServiceNullableV30
+	var remoteDS tc.DeliveryServiceV4
 	found := false
 	for _, ds := range dses {
 		if ds.XMLID == nil || ds.ID == nil {
@@ -866,7 +866,7 @@ func UpdateDeliveryServiceWithInvalidSliceRangeRequest(t *testing.T) {
 		t.Run(tc.description, func(t *testing.T) {
 			remoteDS.RangeSliceBlockSize = tc.slicePluginSize
 			remoteDS.RangeRequestHandling = tc.rangeRequestSetting
-			if _, _, err := TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS.ID, remoteDS, nil); err == nil {
+			if _, _, err := TOSession.UpdateDeliveryServiceV4(*remoteDS.ID, remoteDS, nil); err == nil {
 				t.Error("Delivery service updated with invalid slice plugin configuration")
 			}
 		})
@@ -880,7 +880,7 @@ func UpdateValidateORGServerCacheGroup(t *testing.T) {
 	params.Set("xmlId", "ds-top")
 
 	//Get the correct DS
-	remoteDS, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	remoteDS, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Errorf("cannot GET Delivery Services: %v", err)
 	}
@@ -895,7 +895,7 @@ func UpdateValidateORGServerCacheGroup(t *testing.T) {
 	//Update DS's Topology to a non-ORG server's cachegroup
 	origTopo := *remoteDS[0].Topology
 	remoteDS[0].Topology = util.StrPtr("another-topology")
-	ds, reqInf, err := TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS[0].ID, remoteDS[0], nil)
+	ds, reqInf, err := TOSession.UpdateDeliveryServiceV4(*remoteDS[0].ID, remoteDS[0], nil)
 	if err == nil {
 		t.Errorf("shouldnot UPDATE DeliveryService by ID: %v, but update was successful", ds.XMLID)
 	} else if !strings.Contains(err.Error(), "the following ORG server cachegroups are not in the delivery service's topology") {
@@ -907,7 +907,7 @@ func UpdateValidateORGServerCacheGroup(t *testing.T) {
 
 	// Retrieve the DS to check if topology was updated with missing ORG server
 	params.Set("id", strconv.Itoa(*remoteDS[0].ID))
-	apiResp, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	apiResp, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET Delivery Service by ID: %v - %v", *remoteDS[0].XMLID, err)
 	}
@@ -917,7 +917,7 @@ func UpdateValidateORGServerCacheGroup(t *testing.T) {
 
 	//Set topology back to as it was for further testing
 	remoteDS[0].Topology = &origTopo
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*remoteDS[0].ID, remoteDS[0], nil)
+	_, _, err = TOSession.UpdateDeliveryServiceV4(*remoteDS[0].ID, remoteDS[0], nil)
 	if err != nil {
 		t.Fatalf("couldn't update topology:%v, %v", *remoteDS[0].Topology, err)
 	}
@@ -973,7 +973,7 @@ func GetAccessibleToTest(t *testing.T) {
 func getByTenants(tenantID int, expectedCount int) error {
 	params := url.Values{}
 	params.Set("accessibleTo", strconv.Itoa(tenantID))
-	deliveryServices, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	deliveryServices, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		return err
 	}
@@ -984,7 +984,7 @@ func getByTenants(tenantID int, expectedCount int) error {
 }
 
 func DeleteTestDeliveryServices(t *testing.T) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET deliveryservices: %v", err)
 	}
@@ -993,7 +993,7 @@ func DeleteTestDeliveryServices(t *testing.T) {
 			t.Errorf("testing Delivery Service has no XMLID")
 			continue
 		}
-		var ds tc.DeliveryServiceNullableV30
+		var ds tc.DeliveryServiceV4
 		found := false
 		for _, realDS := range dses {
 			if realDS.XMLID != nil && *realDS.XMLID == *testDS.XMLID {
@@ -1016,7 +1016,7 @@ func DeleteTestDeliveryServices(t *testing.T) {
 		// Retrieve the Server to see if it got deleted
 		params := url.Values{}
 		params.Set("id", strconv.Itoa(*ds.ID))
-		foundDS, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+		foundDS, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 		if err != nil {
 			t.Errorf("Unexpected error deleting Delivery Service '%s': %v", *ds.XMLID, err)
 		}
@@ -1047,12 +1047,12 @@ func DeliveryServiceMinorVersionsTest(t *testing.T) {
 		t.Errorf("expected XMLID: ds-test-minor-versions, actual: %s", *testDS.XMLID)
 	}
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET DeliveryServices: %v - %v", err, dses)
 	}
 
-	var ds tc.DeliveryServiceNullableV30
+	var ds tc.DeliveryServiceV4
 	found := false
 	for _, d := range dses {
 		if d.XMLID != nil && *d.XMLID == *testDS.XMLID {
@@ -1120,11 +1120,11 @@ func DeliveryServiceMinorVersionsTest(t *testing.T) {
 }
 
 func DeliveryServiceTenancyTest(t *testing.T) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Errorf("cannot GET deliveryservices: %v", err)
 	}
-	var tenant3DS tc.DeliveryServiceNullableV30
+	var tenant3DS tc.DeliveryServiceV4
 	foundTenant3DS := false
 	for _, d := range dses {
 		if *d.XMLID == "ds3" {
@@ -1137,7 +1137,7 @@ func DeliveryServiceTenancyTest(t *testing.T) {
 	}
 
 	toReqTimeout := time.Second * time.Duration(Config.Default.Session.TimeoutInSecs)
-	tenant4TOClient, _, err := toclient.LoginWithAgent(TOSession.URL, "tenant4user", "pa$$word", true, "to-api-v3-client-tests/tenant4user", true, toReqTimeout)
+	tenant4TOClient, _, err := toclient.LoginWithAgent(TOSession.URL, "tenant4user", "pa$$word", true, "to-api-v4-client-tests/tenant4user", true, toReqTimeout)
 	if err != nil {
 		t.Fatalf("failed to log in with tenant4user: %v", err.Error())
 	}
@@ -1155,7 +1155,7 @@ func DeliveryServiceTenancyTest(t *testing.T) {
 	}
 
 	// assert that tenant4user cannot update tenant3user's deliveryservice
-	if _, _, err = tenant4TOClient.UpdateDeliveryServiceV30WithHdr(*tenant3DS.ID, tenant3DS, nil); err == nil {
+	if _, _, err = tenant4TOClient.UpdateDeliveryServiceV4(*tenant3DS.ID, tenant3DS, nil); err == nil {
 		t.Errorf("expected tenant4user to be unable to update tenant3's deliveryservice (%s)", *tenant3DS.XMLID)
 	}
 
@@ -1167,7 +1167,7 @@ func DeliveryServiceTenancyTest(t *testing.T) {
 	// assert that tenant4user cannot create a deliveryservice outside of its tenant
 	tenant3DS.XMLID = util.StrPtr("deliveryservicetenancytest")
 	tenant3DS.DisplayName = util.StrPtr("deliveryservicetenancytest")
-	if _, _, err = tenant4TOClient.CreateDeliveryServiceV30(tenant3DS); err == nil {
+	if _, _, err = tenant4TOClient.CreateDeliveryServiceV4(tenant3DS); err == nil {
 		t.Error("expected tenant4user to be unable to create a deliveryservice outside of its tenant")
 	}
 }
diff --git a/traffic_ops/testing/api/v4/deliveryserviceservers_test.go b/traffic_ops/testing/api/v4/deliveryserviceservers_test.go
index fa57e77..3372c99 100644
--- a/traffic_ops/testing/api/v4/deliveryserviceservers_test.go
+++ b/traffic_ops/testing/api/v4/deliveryserviceservers_test.go
@@ -206,7 +206,7 @@ func TryToRemoveLastServerInDeliveryService(t *testing.T) {
 func AssignServersToTopologyBasedDeliveryService(t *testing.T) {
 	params := url.Values{}
 	params.Set("xmlId", "ds-top")
-	ds, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	ds, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery service 'ds-top': %s", err.Error())
 	}
@@ -272,7 +272,7 @@ func AssignOriginsToTopologyBasedDeliveryServices(t *testing.T) {
 	}
 	params = url.Values{}
 	params.Set("xmlId", "ds-top-req-cap")
-	ds, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	ds, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery service 'ds-top-req-cap': %s", err.Error())
 	}
@@ -300,7 +300,7 @@ func AssignOriginsToTopologyBasedDeliveryServices(t *testing.T) {
 	}
 	params = url.Values{}
 	params.Set("xmlId", "ds-top")
-	ds, _, err = TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	ds, _, err = TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery service 'ds-top': %s", err.Error())
 	}
@@ -322,7 +322,7 @@ func AssignOriginsToTopologyBasedDeliveryServices(t *testing.T) {
 func AssignServersToNonTopologyBasedDeliveryServiceThatUsesMidTier(t *testing.T) {
 	params := url.Values{}
 	params.Set("xmlId", "ds1")
-	dsWithMid, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	dsWithMid, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery service 'ds1': %s", err.Error())
 	}
@@ -558,7 +558,7 @@ func DeleteTestDeliveryServiceServers(t *testing.T) {
 
 	if *ds.Active {
 		*ds.Active = false
-		_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+		_, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 		if err != nil {
 			t.Errorf("Setting Delivery Service #%d to inactive", *ds.ID)
 		}
@@ -585,8 +585,8 @@ func DeleteTestDeliveryServiceServers(t *testing.T) {
 	}
 }
 
-func getServerAndDSofSameCDN(t *testing.T) (tc.DeliveryServiceNullableV30, tc.ServerV40) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+func getServerAndDSofSameCDN(t *testing.T) (tc.DeliveryServiceV4, tc.ServerV40) {
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET DeliveryServices: %v", err)
 	}
@@ -612,5 +612,5 @@ func getServerAndDSofSameCDN(t *testing.T) (tc.DeliveryServiceNullableV30, tc.Se
 	}
 	t.Fatal("expected at least one delivery service and server in the same CDN")
 
-	return tc.DeliveryServiceNullableV30{}, tc.ServerV40{}
+	return tc.DeliveryServiceV4{}, tc.ServerV40{}
 }
diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go
index df21a32..2806c25 100644
--- a/traffic_ops/testing/api/v4/servers_test.go
+++ b/traffic_ops/testing/api/v4/servers_test.go
@@ -508,7 +508,7 @@ func GetTestServersDetails(t *testing.T) {
 }
 
 func GetTestServersQueryParameters(t *testing.T) {
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, url.Values{"xmlId": []string{"ds1"}})
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, url.Values{"xmlId": []string{"ds1"}})
 	if err != nil {
 		t.Fatalf("Failed to get Delivery Services: %v", err)
 	}
@@ -542,7 +542,7 @@ func GetTestServersQueryParameters(t *testing.T) {
 		t.Errorf("Expected a status code of 304, got %v", reqInf.StatusCode)
 	}
 
-	dses, _, err = TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err = TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("Failed to get Delivery Services: %v", err)
 	}
@@ -578,7 +578,7 @@ func GetTestServersQueryParameters(t *testing.T) {
 
 	dsTopologyField, dsFirstHeaderRewriteField, innerHeaderRewriteField, lastHeaderRewriteField := *ds.Topology, *ds.FirstHeaderRewrite, *ds.InnerHeaderRewrite, *ds.LastHeaderRewrite
 	ds.Topology, ds.FirstHeaderRewrite, ds.InnerHeaderRewrite, ds.LastHeaderRewrite = nil, nil, nil, nil
-	ds, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	ds, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Fatalf("unable to temporary remove topology-related fields from deliveryservice %s: %s", topDsXmlId, err)
 	}
@@ -587,7 +587,7 @@ func GetTestServersQueryParameters(t *testing.T) {
 		t.Fatalf("unable to assign server %s to deliveryservice %s: %s", *otherServer.HostName, topDsXmlId, err)
 	}
 	ds.Topology, ds.FirstHeaderRewrite, ds.InnerHeaderRewrite, ds.LastHeaderRewrite = &dsTopologyField, &dsFirstHeaderRewriteField, &innerHeaderRewriteField, &lastHeaderRewriteField
-	ds, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
+	ds, _, err = TOSession.UpdateDeliveryServiceV4(*ds.ID, ds, nil)
 	if err != nil {
 		t.Fatalf("unable to re-add topology-related fields to deliveryservice %s: %s", topDsXmlId, err)
 	}
@@ -647,7 +647,7 @@ func GetTestServersQueryParameters(t *testing.T) {
 	}
 
 	const topDsWithNoMids = "ds-based-top-with-no-mids"
-	dses, _, err = TOSession.GetDeliveryServicesV30WithHdr(nil, url.Values{"xmlId": []string{topDsWithNoMids}})
+	dses, _, err = TOSession.GetDeliveryServicesV4(nil, url.Values{"xmlId": []string{topDsWithNoMids}})
 	if err != nil {
 		t.Fatalf("Failed to get Delivery Services: %v", err)
 	}
diff --git a/traffic_ops/testing/api/v4/serverservercapability_test.go b/traffic_ops/testing/api/v4/serverservercapability_test.go
index 02b8dd6..2a307d8 100644
--- a/traffic_ops/testing/api/v4/serverservercapability_test.go
+++ b/traffic_ops/testing/api/v4/serverservercapability_test.go
@@ -230,11 +230,11 @@ func DeleteTestServerServerCapabilities(t *testing.T) {
 		t.Fatal("returned server capabilities assigned to servers was nil\n")
 	}
 
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, nil)
 	if err != nil {
 		t.Fatalf("cannot GET delivery services: %v", err)
 	}
-	dsIDtoDS := make(map[int]tc.DeliveryServiceNullableV30, len(dses))
+	dsIDtoDS := make(map[int]tc.DeliveryServiceV4, len(dses))
 	for _, ds := range dses {
 		dsIDtoDS[*ds.ID] = ds
 	}
diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json b/traffic_ops/testing/api/v4/tc-fixtures.json
index 9be5a37..5ba9ac0 100644
--- a/traffic_ops/testing/api/v4/tc-fixtures.json
+++ b/traffic_ops/testing/api/v4/tc-fixtures.json
@@ -365,7 +365,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl1",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -433,7 +432,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl2",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": ["fmt", "limit", "somethingelse"],
@@ -502,7 +500,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl3",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": null,
@@ -571,7 +568,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "deepCachingType": "NEVER",
@@ -630,7 +626,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl1",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": ["a", "b", "c"],
@@ -690,7 +685,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl1",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -758,7 +752,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl1",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -826,7 +819,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -894,7 +886,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -959,7 +950,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1024,7 +1014,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1088,7 +1077,6 @@
         {
             "active": true,
             "cdnName": "cdn2",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1153,7 +1141,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1218,7 +1205,6 @@
         {
             "active": true,
             "cdnName": "cdn1",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1283,7 +1269,6 @@
         {
             "active": true,
             "cdnName": "cdn2",
-            "cacheurl": "",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
@@ -1348,7 +1333,6 @@
         {
             "active": false,
             "cdnName": "cdn1",
-            "cacheurl": "cacheUrl1",
             "ccrDnsTtl": 3600,
             "checkPath": "",
             "consistentHashQueryParams": [],
diff --git a/traffic_ops/testing/api/v4/topologies_queue_update_test.go b/traffic_ops/testing/api/v4/topologies_queue_update_test.go
index 4c8e981..1a60d58 100644
--- a/traffic_ops/testing/api/v4/topologies_queue_update_test.go
+++ b/traffic_ops/testing/api/v4/topologies_queue_update_test.go
@@ -48,7 +48,7 @@ func getCdnIdAndDsId(t *testing.T) (int64, int) {
 	xmlId := "ds-top"
 	params := url.Values{}
 	params.Set("xmlId", xmlId)
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("unable to get deliveryservice %s: %s", xmlId, err)
 	}
diff --git a/traffic_ops/testing/api/v4/topologies_test.go b/traffic_ops/testing/api/v4/topologies_test.go
index a9a9333..951379b 100644
--- a/traffic_ops/testing/api/v4/topologies_test.go
+++ b/traffic_ops/testing/api/v4/topologies_test.go
@@ -192,7 +192,7 @@ func UpdateTestTopologies(t *testing.T) {
 	}
 	params := url.Values{}
 	params.Add("topology", "top-used-by-cdn1-and-cdn2")
-	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	dses, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Fatalf("cannot GET delivery services: %v", err)
 	}
@@ -247,7 +247,7 @@ func UpdateValidateTopologyORGServerCacheGroup(t *testing.T) {
 	params.Set("xmlId", "ds-top")
 
 	//Get the correct DS
-	remoteDS, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+	remoteDS, _, err := TOSession.GetDeliveryServicesV4(nil, params)
 	if err != nil {
 		t.Errorf("cannot GET Delivery Services: %v", err)
 	}
diff --git a/traffic_ops/testing/api/v4/traffic_control_test.go b/traffic_ops/testing/api/v4/traffic_control_test.go
index da58b20..a750916 100644
--- a/traffic_ops/testing/api/v4/traffic_control_test.go
+++ b/traffic_ops/testing/api/v4/traffic_control_test.go
@@ -30,7 +30,7 @@ type TrafficControl struct {
 	DeliveryServicesRegexes                           []tc.DeliveryServiceRegexesTest         `json:"deliveryServicesRegexes"`
 	DeliveryServiceRequests                           []tc.DeliveryServiceRequest             `json:"deliveryServiceRequests"`
 	DeliveryServiceRequestComments                    []tc.DeliveryServiceRequestComment      `json:"deliveryServiceRequestComments"`
-	DeliveryServices                                  []tc.DeliveryServiceNullableV30         `json:"deliveryservices"`
+	DeliveryServices                                  []tc.DeliveryServiceV4                  `json:"deliveryservices"`
 	DeliveryServicesRequiredCapabilities              []tc.DeliveryServicesRequiredCapability `json:"deliveryservicesRequiredCapabilities"`
 	TopologyBasedDeliveryServicesRequiredCapabilities []tc.DeliveryServicesRequiredCapability `json:"topologyBasedDeliveryServicesRequiredCapabilities"`
 	Divisions                                         []tc.Division                           `json:"divisions"`
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/dspost.go b/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
index 2c12064..e1fff4b 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
@@ -37,7 +37,7 @@ import (
 	"github.com/lib/pq"
 )
 
-func DSPostHandler(w http.ResponseWriter, r *http.Request) {
+func DSPostHandlerV31(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"})
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
@@ -50,73 +50,114 @@ func DSPostHandler(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("malformed JSON: "+err.Error()), nil)
 		return
 	}
-	vals := map[string]interface{}{
-		"alerts": tc.CreateAlerts(tc.SuccessLevel, "Delivery services successfully assigned to all the servers of cache group "+strconv.Itoa(inf.IntParams["id"])+".").Alerts,
+
+	resp, vals, userErr, sysErr, errCode := postDSes(inf.Tx.Tx, inf.User, inf.IntParams["id"], req.DeliveryServices)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+
+	if err := updateDSParam(inf.Tx.Tx, req.DeliveryServices, "cacheurl_", "cacheurl"); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("updating delivery service parameters: "+err.Error()))
+		return
+	}
+
+	if err, errCode := writeChangeLog(inf.Tx.Tx, inf.User, inf.IntParams["id"]); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err)
+		return
 	}
 
-	resp, userErr, sysErr, errCode := postDSes(inf.Tx.Tx, inf.User, inf.IntParams["id"], req.DeliveryServices)
+	api.WriteRespVals(w, r, resp, vals)
+}
+
+func DSPostHandlerV40(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"})
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
+	defer inf.Close()
+
+	req := tc.CachegroupPostDSReq{}
+	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("malformed JSON: "+err.Error()), nil)
+		return
+	}
+
+	resp, vals, userErr, sysErr, errCode := postDSes(inf.Tx.Tx, inf.User, inf.IntParams["id"], req.DeliveryServices)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+
+	if err, errCode := writeChangeLog(inf.Tx.Tx, inf.User, inf.IntParams["id"]); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err)
+	}
+
 	api.WriteRespVals(w, r, resp, vals)
 }
 
+func writeChangeLog(tx *sql.Tx, user *auth.CurrentUser, cgID int) (error, int) {
+	cgName, ok, err := dbhelpers.GetCacheGroupNameFromID(tx, cgID)
+	if err != nil {
+		return fmt.Errorf("getting cachegroup name from ID %d: %s", cgID, err.Error()), http.StatusInternalServerError
+	} else if !ok {
+		return fmt.Errorf("cachegroup %d does not exist", cgID), http.StatusNotFound
+	}
+	api.CreateChangeLogRawTx(api.ApiChange, "CACHEGROUP: "+string(cgName)+", ID: "+strconv.Itoa(cgID)+", ACTION: Assign DSes to CacheGroup servers", user, tx)
+	return nil, 0
+}
+
 // postDSes returns the post response, any user error, any system error, and the HTTP status code to be returned in the event of an error.
-func postDSes(tx *sql.Tx, user *auth.CurrentUser, cgID int, dsIDs []int) (tc.CacheGroupPostDSResp, error, error, int) {
+func postDSes(tx *sql.Tx, user *auth.CurrentUser, cgID int, dsIDs []int) (tc.CacheGroupPostDSResp, map[string]interface{}, error, error, int) {
 	cdnName, usrErr, sysErr, errCode := getCachegroupCDN(tx, cgID)
 	if sysErr != nil {
 		sysErr = errors.New("getting cachegroup CDN: " + sysErr.Error())
 	}
 	if usrErr != nil || sysErr != nil {
-		return tc.CacheGroupPostDSResp{}, usrErr, sysErr, errCode
+		return tc.CacheGroupPostDSResp{}, nil, usrErr, sysErr, errCode
 	}
 
 	tenantIDs, err := getDSTenants(tx, dsIDs)
 	if err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("getting delivery service tenant IDs: " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("getting delivery service tenant IDs: " + err.Error()), http.StatusInternalServerError
 	}
 	for _, tenantID := range tenantIDs {
 		ok, err := tenant.IsResourceAuthorizedToUserTx(int(tenantID), user, tx)
 		if err != nil {
-			return tc.CacheGroupPostDSResp{}, nil, errors.New("checking tenancy: " + err.Error()), http.StatusInternalServerError
+			return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("checking tenancy: " + err.Error()), http.StatusInternalServerError
 		}
 		if !ok {
-			return tc.CacheGroupPostDSResp{}, fmt.Errorf("not authorized for delivery service tenant %d", tenantID), nil, http.StatusForbidden
+			return tc.CacheGroupPostDSResp{}, nil, fmt.Errorf("not authorized for delivery service tenant %d", tenantID), nil, http.StatusForbidden
 		}
 	}
 
-	cgName, ok, err := dbhelpers.GetCacheGroupNameFromID(tx, cgID)
-	if err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, fmt.Errorf("getting cachegroup name from ID %d: %s", cgID, err.Error()), http.StatusInternalServerError
-	} else if !ok {
-		return tc.CacheGroupPostDSResp{}, fmt.Errorf("cachegroup %d does not exist", cgID), nil, http.StatusNotFound
-	}
-
 	topologyDSes, err := dbhelpers.GetDeliveryServicesWithTopologies(tx, dsIDs)
 	if err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("getting delivery services with topologies: " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("getting delivery services with topologies: " + err.Error()), http.StatusInternalServerError
 	}
 	if len(topologyDSes) > 0 {
-		return tc.CacheGroupPostDSResp{}, fmt.Errorf("delivery services %v are already assigned to a topology", topologyDSes), nil, http.StatusBadRequest
+		return tc.CacheGroupPostDSResp{}, nil, fmt.Errorf("delivery services %v are already assigned to a topology", topologyDSes), nil, http.StatusBadRequest
 	}
 
 	if err := verifyDSesCDN(tx, dsIDs, cdnName); err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("verifying delivery service CDNs match cachegroup server CDNs: " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("verifying delivery service CDNs match cachegroup server CDNs: " + err.Error()), http.StatusInternalServerError
 	}
 	cgServers, err := getCachegroupServers(tx, cgID)
 	if err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("getting cachegroup server names " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("getting cachegroup server names " + err.Error()), http.StatusInternalServerError
 	}
 	if err := insertCachegroupDSes(tx, cgID, dsIDs); err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("inserting cachegroup delivery services: " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("inserting cachegroup delivery services: " + err.Error()), http.StatusInternalServerError
 	}
 
 	if err := updateParams(tx, dsIDs); err != nil {
-		return tc.CacheGroupPostDSResp{}, nil, errors.New("updating delivery service parameters: " + err.Error()), http.StatusInternalServerError
+		return tc.CacheGroupPostDSResp{}, nil, nil, errors.New("updating delivery service parameters: " + err.Error()), http.StatusInternalServerError
 	}
-	api.CreateChangeLogRawTx(api.ApiChange, "CACHEGROUP: "+string(cgName)+", ID: "+strconv.Itoa(cgID)+", ACTION: Assign DSes to CacheGroup servers", user, tx)
-	return tc.CacheGroupPostDSResp{ID: util.JSONIntStr(cgID), ServerNames: cgServers, DeliveryServices: dsIDs}, nil, nil, http.StatusOK
+	vals := map[string]interface{}{
+		"alerts": tc.CreateAlerts(tc.SuccessLevel, "Delivery services successfully assigned to all the servers of cache group "+strconv.Itoa(cgID)+".").Alerts,
+	}
+	return tc.CacheGroupPostDSResp{ID: util.JSONIntStr(cgID), ServerNames: cgServers, DeliveryServices: dsIDs}, vals, nil, nil, http.StatusOK
 }
 
 func insertCachegroupDSes(tx *sql.Tx, cgID int, dsIDs []int) error {
@@ -209,14 +250,11 @@ AND (type.name LIKE 'EDGE%' OR type.name LIKE 'ORG%')
 	return cdn, nil, nil, http.StatusOK
 }
 
-// updateParams updated the header rewrite, cacheurl, and regex remap params for the given edge caches, on the given delivery services. NOTE it does not update Mid params.
+// updateParams updated the header rewrite, and regex remap params for the given edge caches, on the given delivery services. NOTE it does not update Mid params.
 func updateParams(tx *sql.Tx, dsIDs []int) error {
 	if err := updateDSParam(tx, dsIDs, "hdr_rw_", "edge_header_rewrite"); err != nil {
 		return err
 	}
-	if err := updateDSParam(tx, dsIDs, "cacheurl_", "cacheurl"); err != nil {
-		return err
-	}
 	if err := updateDSParam(tx, dsIDs, "regex_remap_", "regex_remap"); err != nil {
 		return err
 	}
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index 103e88f..05f9db0 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -854,7 +854,7 @@ func TopologyExists(tx *sql.Tx, name string) (bool, error) {
 
 // CheckTopology returns an error if the given Topology does not exist or if one of the Topology's Cache Groups is
 // empty with respect to the Delivery Service's CDN.
-func CheckTopology(tx *sqlx.Tx, ds tc.DeliveryServiceNullableV30) (int, error, error) {
+func CheckTopology(tx *sqlx.Tx, ds tc.DeliveryServiceV4) (int, error, error) {
 	statusCode, userErr, sysErr := http.StatusOK, error(nil), error(nil)
 
 	if ds.Topology == nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index c15ff20..e5ef05e 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -24,7 +24,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
+	"github.com/asaskevich/govalidator"
+	validation "github.com/go-ozzo/ozzo-validation"
 	"net/http"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -42,11 +46,17 @@ import (
 	"github.com/lib/pq"
 )
 
-//we need a type alias to define functions on
+type tierType int
+
+const (
+	midTier tierType = iota
+	edgeTier
+)
 
+//we need a type alias to define functions on
 type TODeliveryService struct {
 	api.APIInfoImpl
-	tc.DeliveryServiceNullableV30
+	tc.DeliveryServiceV4
 }
 
 // TODeliveryServiceOldDetails is the struct to store the old details while updating a DS.
@@ -59,11 +69,11 @@ type TODeliveryServiceOldDetails struct {
 }
 
 func (ds TODeliveryService) MarshalJSON() ([]byte, error) {
-	return json.Marshal(ds.DeliveryServiceNullableV30)
+	return json.Marshal(ds.DeliveryServiceV4)
 }
 
 func (ds *TODeliveryService) UnmarshalJSON(data []byte) error {
-	return json.Unmarshal(data, ds.DeliveryServiceNullableV30)
+	return json.Unmarshal(data, &ds.DeliveryServiceV4)
 }
 
 func (ds *TODeliveryService) APIInfo() *api.APIInfo { return ds.ReqInfo }
@@ -97,7 +107,7 @@ func (ds *TODeliveryService) GetType() string {
 
 // IsTenantAuthorized checks that the user is authorized for both the delivery service's existing tenant, and the new tenant they're changing it to (if different).
 func (ds *TODeliveryService) IsTenantAuthorized(user *auth.CurrentUser) (bool, error) {
-	return isTenantAuthorized(ds.ReqInfo, &ds.DeliveryServiceNullableV30)
+	return isTenantAuthorized(ds.ReqInfo, &ds.DeliveryServiceV4)
 }
 
 func CreateV12(w http.ResponseWriter, r *http.Request) {
@@ -121,7 +131,6 @@ func CreateV12(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceNullableV12{*res})
 }
-
 func CreateV13(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
 	if userErr != nil || sysErr != nil {
@@ -143,7 +152,6 @@ func CreateV13(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceNullableV13{*res})
 }
-
 func CreateV14(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
 	if userErr != nil || sysErr != nil {
@@ -188,7 +196,27 @@ func CreateV15(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceNullableV15{*res})
 }
+func CreateV30(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
 
+	ds := tc.DeliveryServiceV30{}
+	if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("decoding: "+err.Error()), nil)
+		return
+	}
+
+	res, status, userErr, sysErr := createV30(w, r, inf, ds)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
+		return
+	}
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceV30{*res})
+}
 func CreateV31(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
 	if userErr != nil || sysErr != nil {
@@ -210,8 +238,7 @@ func CreateV31(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceV31{*res})
 }
-
-func CreateV30(w http.ResponseWriter, r *http.Request) {
+func CreateV40(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
@@ -219,18 +246,18 @@ func CreateV30(w http.ResponseWriter, r *http.Request) {
 	}
 	defer inf.Close()
 
-	ds := tc.DeliveryServiceV30{}
+	ds := tc.DeliveryServiceV40{}
 	if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("decoding: "+err.Error()), nil)
 		return
 	}
 
-	res, status, userErr, sysErr := createV30(w, r, inf, ds)
+	res, status, userErr, sysErr := createV40(w, r, inf, ds)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
 		return
 	}
-	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceV30{*res})
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation was successful.", []tc.DeliveryServiceV40{*res})
 }
 
 func createV12(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS tc.DeliveryServiceNullableV12) (*tc.DeliveryServiceNullableV12, int, error, error) {
@@ -241,7 +268,6 @@ func createV12(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS t
 	}
 	return nil, status, userErr, sysErr
 }
-
 func createV13(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS tc.DeliveryServiceNullableV13) (*tc.DeliveryServiceNullableV13, int, error, error) {
 	dsV14 := tc.DeliveryServiceNullableV14{DeliveryServiceNullableV13: reqDS}
 	res, status, userErr, sysErr := createV14(w, r, inf, dsV14)
@@ -250,7 +276,6 @@ func createV13(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS t
 	}
 	return nil, status, userErr, sysErr
 }
-
 func createV14(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS tc.DeliveryServiceNullableV14) (*tc.DeliveryServiceNullableV14, int, error, error) {
 	dsV15 := tc.DeliveryServiceNullableV15{DeliveryServiceNullableV14: reqDS}
 	res, status, userErr, sysErr := createV15(w, r, inf, dsV15)
@@ -259,7 +284,6 @@ func createV14(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS t
 	}
 	return nil, status, userErr, sysErr
 }
-
 func createV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS tc.DeliveryServiceNullableV15) (*tc.DeliveryServiceNullableV15, int, error, error) {
 	dsV30 := tc.DeliveryServiceV30{DeliveryServiceNullableV15: reqDS}
 	res, status, userErr, sysErr := createV30(w, r, inf, dsV30)
@@ -268,8 +292,6 @@ func createV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS t
 	}
 	return nil, status, userErr, sysErr
 }
-
-// create creates the given ds in the database, and returns the DS with its id and other fields created on insert set. On error, the HTTP status code, user error, and system error are returned. The status code SHOULD NOT be used, if both errors are nil.
 func createV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV30 tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {
 	ds := tc.DeliveryServiceV31{DeliveryServiceV30: dsV30}
 	res, status, userErr, sysErr := createV31(w, r, inf, ds)
@@ -278,14 +300,41 @@ func createV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV30 t
 	}
 	return nil, status, userErr, sysErr
 }
+func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {
+	tx := inf.Tx.Tx
+	dsNullable := tc.DeliveryServiceNullableV30(dsV31)
+	ds := dsNullable.UpgradeToV4()
+	res, status, userErr, sysErr := createV40(w, r, inf, tc.DeliveryServiceV40(ds))
+	if res == nil {
+		return nil, status, userErr, sysErr
+	}
+
+	ds = tc.DeliveryServiceV4(*res)
+	if dsV31.CacheURL != nil {
+		_, err := tx.Exec("UPDATE deliveryservice SET cacheurl = $1 WHERE ID = $2",
+			&dsV31.CacheURL,
+			&ds.ID)
+		if err != nil {
+			usrErr, sysErr, code := api.ParseDBError(err)
+			return nil, code, usrErr, sysErr
+		}
+	}
+
+	if err := EnsureCacheURLParams(tx, *ds.ID, *ds.XMLID, dsV31.CacheURL); err != nil {
+		return nil, http.StatusInternalServerError, nil, err
+	}
+
+	oldRes := tc.DeliveryServiceV31(ds.DowngradeToV3())
+	return &oldRes, status, userErr, sysErr
+}
 
 // create creates the given ds in the database, and returns the DS with its id and other fields created on insert set. On error, the HTTP status code, user error, and system error are returned. The status code SHOULD NOT be used, if both errors are nil.
-func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {
+func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 tc.DeliveryServiceV40) (*tc.DeliveryServiceV40, int, error, error) {
 	user := inf.User
 	tx := inf.Tx.Tx
 	cfg := inf.Config
-	ds := tc.DeliveryServiceNullableV30(dsV31)
-	if err := ds.Validate(tx); err != nil {
+	ds := tc.DeliveryServiceV4(dsV40)
+	if err := Validate(tx, &ds); err != nil {
 		return nil, http.StatusBadRequest, errors.New("invalid request: " + err.Error()), nil
 	}
 
@@ -307,7 +356,6 @@ func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 t
 	resultRows, err := tx.Query(insertQuery(),
 		&ds.Active,
 		&ds.AnonymousBlockingEnabled,
-		&ds.CacheURL,
 		&ds.CCRDNSTTL,
 		&ds.CDNID,
 		&ds.CheckPath,
@@ -427,7 +475,7 @@ func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 t
 
 	ds.ExampleURLs = MakeExampleURLs(ds.Protocol, *ds.Type, *ds.RoutingName, *ds.MatchList, cdnDomain)
 
-	if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, dsType, ds.MaxOriginConnections); err != nil {
+	if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.SigningAlgorithm, dsType, ds.MaxOriginConnections); err != nil {
 		return nil, http.StatusInternalServerError, nil, errors.New("ensuring ds parameters:: " + err.Error())
 	}
 
@@ -446,8 +494,8 @@ func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 t
 		return nil, http.StatusInternalServerError, nil, errors.New("error writing to audit log: " + err.Error())
 	}
 
-	dsV31 = tc.DeliveryServiceV31(ds)
-	return &dsV31, http.StatusOK, nil, nil
+	dsV40 = tc.DeliveryServiceV40(ds)
+	return &dsV40, http.StatusOK, nil, nil
 }
 
 func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
@@ -461,7 +509,6 @@ func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
 	}
 	return nil
 }
-
 func createConsistentHashQueryParams(tx *sql.Tx, dsID int, consistentHashQueryParams []string) (int, error) {
 	if len(consistentHashQueryParams) == 0 {
 		return 0, nil
@@ -477,7 +524,6 @@ func createConsistentHashQueryParams(tx *sql.Tx, dsID int, consistentHashQueryPa
 
 	return c, nil
 }
-
 func (ds *TODeliveryService) Read(h http.Header, useIMS bool) ([]interface{}, error, error, int, *time.Time) {
 	version := ds.APIInfo().Version
 	if version == nil {
@@ -501,20 +547,20 @@ func (ds *TODeliveryService) Read(h http.Header, useIMS bool) ([]interface{}, er
 	for _, ds := range dses {
 		switch {
 		// NOTE: it's required to handle minor version cases in a descending >= manner
-		case version.Major > 3 && version.Major >= 0:
+		case version.Major > 3 && version.Minor >= 0:
 			returnable = append(returnable, ds)
 		case version.Major > 2 && version.Minor >= 1:
-			returnable = append(returnable, ds)
+			returnable = append(returnable, ds.DowngradeToV3())
 		case version.Major > 2:
-			returnable = append(returnable, ds.DeliveryServiceV30)
+			returnable = append(returnable, ds.DowngradeToV3().DeliveryServiceV30)
 		case version.Major > 1 || version.Minor >= 5:
-			returnable = append(returnable, ds.DeliveryServiceNullableV15)
+			returnable = append(returnable, ds.DowngradeToV3().DeliveryServiceNullableV15)
 		case version.Minor >= 4:
-			returnable = append(returnable, ds.DeliveryServiceNullableV14)
+			returnable = append(returnable, ds.DowngradeToV3().DeliveryServiceNullableV14)
 		case version.Minor >= 3:
-			returnable = append(returnable, ds.DeliveryServiceNullableV13)
+			returnable = append(returnable, ds.DowngradeToV3().DeliveryServiceNullableV13)
 		case version.Minor >= 1:
-			returnable = append(returnable, ds.DeliveryServiceNullableV12)
+			returnable = append(returnable, ds.DowngradeToV3().DeliveryServiceNullableV12)
 		default:
 			return nil, nil, fmt.Errorf("TODeliveryService.Read called with invalid API version: %d.%d", version.Major, version.Minor), http.StatusInternalServerError, nil
 		}
@@ -546,7 +592,6 @@ func UpdateV12(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceNullableV12{*res})
 }
-
 func UpdateV13(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
 	if userErr != nil || sysErr != nil {
@@ -571,7 +616,6 @@ func UpdateV13(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceNullableV13{*res})
 }
-
 func UpdateV14(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
 	if userErr != nil || sysErr != nil {
@@ -596,7 +640,6 @@ func UpdateV14(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceNullableV14{*res})
 }
-
 func UpdateV15(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
 	if userErr != nil || sysErr != nil {
@@ -621,7 +664,6 @@ func UpdateV15(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceNullableV15{*res})
 }
-
 func UpdateV30(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
 	if userErr != nil || sysErr != nil {
@@ -646,7 +688,6 @@ func UpdateV30(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceV30{*res})
 }
-
 func UpdateV31(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
 	if userErr != nil || sysErr != nil {
@@ -670,6 +711,29 @@ func UpdateV31(w http.ResponseWriter, r *http.Request) {
 	}
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceV31{*res})
 }
+func UpdateV40(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	id := inf.IntParams["id"]
+
+	ds := tc.DeliveryServiceV40{}
+	if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("malformed JSON: "+err.Error()), nil)
+		return
+	}
+	ds.ID = &id
+	res, status, userErr, sysErr := updateV40(w, r, inf, &ds)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
+		return
+	}
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update was successful.", []tc.DeliveryServiceV40{*res})
+}
 
 func updateV12(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS *tc.DeliveryServiceNullableV12) (*tc.DeliveryServiceNullableV12, int, error, error) {
 	dsV13 := tc.DeliveryServiceNullableV13{DeliveryServiceNullableV12: *reqDS}
@@ -707,7 +771,6 @@ WHERE
 	}
 	return nil, status, userErr, sysErr
 }
-
 func updateV13(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS *tc.DeliveryServiceNullableV13) (*tc.DeliveryServiceNullableV13, int, error, error) {
 	dsV14 := tc.DeliveryServiceNullableV14{DeliveryServiceNullableV13: *reqDS}
 	// query the DB for existing 1.4 fields in order to "upgrade" this 1.3 request into a 1.4 request
@@ -738,7 +801,6 @@ WHERE
 	}
 	return nil, status, userErr, sysErr
 }
-
 func updateV14(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS *tc.DeliveryServiceNullableV14) (*tc.DeliveryServiceNullableV14, int, error, error) {
 	dsV15 := tc.DeliveryServiceNullableV15{DeliveryServiceNullableV14: *reqDS}
 	// query the DB for existing 1.5 fields in order to "upgrade" this 1.4 request into a 1.5 request
@@ -765,7 +827,6 @@ WHERE
 	}
 	return nil, status, userErr, sysErr
 }
-
 func updateV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS *tc.DeliveryServiceNullableV15) (*tc.DeliveryServiceNullableV15, int, error, error) {
 	dsV30 := tc.DeliveryServiceV30{DeliveryServiceNullableV15: *reqDS}
 	// query the DB for existing 3.0 fields in order to "upgrade" this 1.5 request into a 3.0 request
@@ -798,7 +859,6 @@ WHERE
 	}
 	return nil, status, userErr, sysErr
 }
-
 func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV30 *tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {
 	dsV31 := tc.DeliveryServiceV31{DeliveryServiceV30: *dsV30}
 	// query the DB for existing 3.1 fields in order to "upgrade" this 3.0 request into a 3.1 request
@@ -823,12 +883,38 @@ WHERE
 	}
 	return nil, status, userErr, sysErr
 }
-
 func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 *tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {
+	dsNull := tc.DeliveryServiceNullableV30(*dsV31)
+	ds := dsNull.UpgradeToV4()
+	dsV40 := tc.DeliveryServiceV40(ds)
+	tx := inf.Tx.Tx
+	res, status, usrErr, sysErr := updateV40(w, r, inf, &dsV40)
+	if res == nil {
+		return nil, status, usrErr, sysErr
+	}
+	ds = tc.DeliveryServiceV4(*res)
+	if dsV31.CacheURL != nil {
+		_, err := tx.Exec("UPDATE deliveryservice SET cacheurl = $1 WHERE ID = $2",
+			&dsV31.CacheURL,
+			&ds.ID)
+		if err != nil {
+			usrErr, sysErr, code := api.ParseDBError(err)
+			return nil, code, usrErr, sysErr
+		}
+	}
+
+	if err := EnsureCacheURLParams(tx, *ds.ID, *ds.XMLID, dsV31.CacheURL); err != nil {
+		return nil, http.StatusInternalServerError, nil, err
+	}
+
+	oldRes := tc.DeliveryServiceV31(ds.DowngradeToV3())
+	return &oldRes, http.StatusOK, nil, nil
+}
+func updateV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 *tc.DeliveryServiceV40) (*tc.DeliveryServiceV40, int, error, error) {
 	tx := inf.Tx.Tx
 	user := inf.User
-	ds := tc.DeliveryServiceNullableV30(*dsV31)
-	if err := ds.Validate(tx); err != nil {
+	ds := tc.DeliveryServiceV4(*dsV40)
+	if err := Validate(tx, &ds); err != nil {
 		return nil, http.StatusBadRequest, errors.New("invalid request: " + err.Error()), nil
 	}
 
@@ -920,7 +1006,6 @@ func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 *
 
 	resultRows, err := tx.Query(updateDSQuery(),
 		&ds.Active,
-		&ds.CacheURL,
 		&ds.CCRDNSTTL,
 		&ds.CDNID,
 		&ds.CheckPath,
@@ -1034,7 +1119,7 @@ func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 *
 		ds.MatchList = &ml
 	}
 
-	if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, newDSType, ds.MaxOriginConnections); err != nil {
+	if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.SigningAlgorithm, newDSType, ds.MaxOriginConnections); err != nil {
 		return nil, http.StatusInternalServerError, nil, errors.New("ensuring ds parameters:: " + err.Error())
 	}
 
@@ -1062,8 +1147,8 @@ func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 *
 	if err := api.CreateChangeLogRawErr(api.ApiChange, "Updated ds: "+*ds.XMLID+" id: "+strconv.Itoa(*ds.ID), user, tx); err != nil {
 		return nil, http.StatusInternalServerError, nil, errors.New("writing change log entry: " + err.Error())
 	}
-	dsV31 = (*tc.DeliveryServiceV31)(&ds)
-	return dsV31, http.StatusOK, nil, nil
+	dsV40 = (*tc.DeliveryServiceV40)(&ds)
+	return dsV40, http.StatusOK, nil, nil
 }
 
 //Delete is the DeliveryService implementation of the Deleter interface.
@@ -1112,7 +1197,7 @@ func (v *TODeliveryService) DeleteQuery() string {
 	return `DELETE FROM deliveryservice WHERE id = :id`
 }
 
-func readGetDeliveryServices(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth.CurrentUser, useIMS bool) ([]tc.DeliveryServiceNullableV30, error, error, int, *time.Time) {
+func readGetDeliveryServices(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth.CurrentUser, useIMS bool) ([]tc.DeliveryServiceV4, error, error, int, *time.Time) {
 	var maxTime time.Time
 	var runSecond bool
 	if strings.HasSuffix(params["id"], ".json") {
@@ -1147,7 +1232,7 @@ func readGetDeliveryServices(h http.Header, params map[string]string, tx *sqlx.T
 		runSecond, maxTime = ims.TryIfModifiedSinceQuery(tx, h, queryValues, selectMaxLastUpdatedQuery(where))
 		if !runSecond {
 			log.Debugln("IMS HIT")
-			return []tc.DeliveryServiceNullableV30{}, nil, nil, http.StatusNotModified, &maxTime
+			return []tc.DeliveryServiceV4{}, nil, nil, http.StatusNotModified, &maxTime
 		}
 		log.Debugln("IMS MISS")
 	} else {
@@ -1184,6 +1269,206 @@ func readGetDeliveryServices(h http.Header, params map[string]string, tx *sqlx.T
 	return r, e1, e2, code, &maxTime
 }
 
+func requiredIfMatchesTypeName(patterns []string, typeName string) func(interface{}) error {
+	return func(value interface{}) error {
+		switch v := value.(type) {
+		case *int:
+			if v != nil {
+				return nil
+			}
+		case *bool:
+			if v != nil {
+				return nil
+			}
+		case *string:
+			if v != nil {
+				return nil
+			}
+		case *float64:
+			if v != nil {
+				return nil
+			}
+		default:
+			return fmt.Errorf("validation failure: unknown type %T", value)
+		}
+		pattern := strings.Join(patterns, "|")
+		err := error(nil)
+		match := false
+		if typeName != "" {
+			match, err = regexp.MatchString(pattern, typeName)
+			if match {
+				return fmt.Errorf("is required if type is '%s'", typeName)
+			}
+		}
+		return err
+	}
+}
+
+func Validate(tx *sql.Tx, ds *tc.DeliveryServiceV4) error {
+	sanitize(ds)
+	neverOrAlways := validation.NewStringRule(tovalidate.IsOneOfStringICase("NEVER", "ALWAYS"),
+		"must be one of 'NEVER' or 'ALWAYS'")
+	isDNSName := validation.NewStringRule(govalidator.IsDNSName, "must be a valid hostname")
+	noPeriods := validation.NewStringRule(tovalidate.NoPeriods, "cannot contain periods")
+	noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot contain spaces")
+	noLineBreaks := validation.NewStringRule(tovalidate.NoLineBreaks, "cannot contain line breaks")
+	errs := tovalidate.ToErrors(validation.Errors{
+		"active":              validation.Validate(ds.Active, validation.NotNil),
+		"cdnId":               validation.Validate(ds.CDNID, validation.Required),
+		"deepCachingType":     validation.Validate(ds.DeepCachingType, neverOrAlways),
+		"displayName":         validation.Validate(ds.DisplayName, validation.Required, validation.Length(1, 48)),
+		"dscp":                validation.Validate(ds.DSCP, validation.NotNil, validation.Min(0)),
+		"geoLimit":            validation.Validate(ds.GeoLimit, validation.NotNil),
+		"geoProvider":         validation.Validate(ds.GeoProvider, validation.NotNil),
+		"logsEnabled":         validation.Validate(ds.LogsEnabled, validation.NotNil),
+		"regionalGeoBlocking": validation.Validate(ds.RegionalGeoBlocking, validation.NotNil),
+		"remapText":           validation.Validate(ds.RemapText, noLineBreaks),
+		"routingName":         validation.Validate(ds.RoutingName, isDNSName, noPeriods, validation.Length(1, 48)),
+		"typeId":              validation.Validate(ds.TypeID, validation.Required, validation.Min(1)),
+		"xmlId":               validation.Validate(ds.XMLID, validation.Required, noSpaces, noPeriods, validation.Length(1, 48)),
+	})
+	if err := validateTopologyFields(ds); err != nil {
+		errs = append(errs, err)
+	}
+	if err := validateTypeFields(tx, ds); err != nil {
+		errs = append(errs, errors.New("type fields: "+err.Error()))
+	}
+	if len(errs) == 0 {
+		return nil
+	}
+	return util.JoinErrs(errs)
+}
+
+func validateTopologyFields(ds *tc.DeliveryServiceV4) error {
+	if ds.Topology != nil && (ds.EdgeHeaderRewrite != nil || ds.MidHeaderRewrite != nil) {
+		return errors.New("cannot set edgeHeaderRewrite or midHeaderRewrite while a Topology is assigned. Use firstHeaderRewrite, innerHeaderRewrite, and/or lastHeaderRewrite instead")
+	}
+	if ds.Topology == nil && (ds.FirstHeaderRewrite != nil || ds.InnerHeaderRewrite != nil || ds.LastHeaderRewrite != nil) {
+		return errors.New("cannot set firstHeaderRewrite, innerHeaderRewrite, or lastHeaderRewrite unless this delivery service is assigned to a Topology. Use edgeHeaderRewrite and/or midHeaderRewrite instead")
+	}
+	return nil
+}
+
+func parseOrgServerFQDN(orgServerFQDN string) (*string, *string, *string, error) {
+	originRegex := regexp.MustCompile(`^(https?)://([^:]+)(:(\d+))?$`)
+	matches := originRegex.FindStringSubmatch(orgServerFQDN)
+	if len(matches) == 0 {
+		return nil, nil, nil, fmt.Errorf("unable to parse invalid orgServerFqdn: '%s'", orgServerFQDN)
+	}
+
+	protocol := strings.ToLower(matches[1])
+	FQDN := matches[2]
+
+	if len(protocol) == 0 || len(FQDN) == 0 {
+		return nil, nil, nil, fmt.Errorf("empty Origin protocol or FQDN parsed from '%s'", orgServerFQDN)
+	}
+
+	var port *string
+	if len(matches[4]) != 0 {
+		port = &matches[4]
+	}
+	return &protocol, &FQDN, port, nil
+}
+
+func validateOrgServerFQDN(orgServerFQDN string) bool {
+	_, fqdn, port, err := parseOrgServerFQDN(orgServerFQDN)
+	if err != nil || !govalidator.IsHost(*fqdn) || (port != nil && !govalidator.IsPort(*port)) {
+		return false
+	}
+	return true
+}
+
+func validateTypeFields(tx *sql.Tx, ds *tc.DeliveryServiceV4) error {
+	// Validate the TypeName related fields below
+	err := error(nil)
+	DNSRegexType := "^DNS.*$"
+	HTTPRegexType := "^HTTP.*$"
+	SteeringRegexType := "^STEERING.*$"
+	latitudeErr := "Must be a floating point number within the range +-90"
+	longitudeErr := "Must be a floating point number within the range +-180"
+
+	typeName, err := tc.ValidateTypeID(tx, ds.TypeID, "deliveryservice")
+	if err != nil {
+		return err
+	}
+
+	errs := validation.Errors{
+		"consistentHashQueryParams": validation.Validate(ds,
+			validation.By(func(dsi interface{}) error {
+				ds := dsi.(*tc.DeliveryServiceV4)
+				if len(ds.ConsistentHashQueryParams) == 0 || tc.DSType(typeName).IsHTTP() {
+					return nil
+				}
+				return fmt.Errorf("consistentHashQueryParams not allowed for '%s' deliveryservice type", typeName)
+			})),
+		"initialDispersion": validation.Validate(ds.InitialDispersion,
+			validation.By(requiredIfMatchesTypeName([]string{HTTPRegexType}, typeName)),
+			validation.By(tovalidate.IsGreaterThanZero)),
+		"ipv6RoutingEnabled": validation.Validate(ds.IPV6RoutingEnabled,
+			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
+		"missLat": validation.Validate(ds.MissLat,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
+			validation.Min(-90.0).Error(latitudeErr),
+			validation.Max(90.0).Error(latitudeErr)),
+		"missLong": validation.Validate(ds.MissLong,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
+			validation.Min(-180.0).Error(longitudeErr),
+			validation.Max(180.0).Error(longitudeErr)),
+		"multiSiteOrigin": validation.Validate(ds.MultiSiteOrigin,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
+		"orgServerFqdn": validation.Validate(ds.OrgServerFQDN,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName)),
+			validation.NewStringRule(validateOrgServerFQDN, "must start with http:// or https:// and be followed by a valid hostname with an optional port (no trailing slash)")),
+		"rangeSliceBlockSize": validation.Validate(ds,
+			validation.By(func(dsi interface{}) error {
+				ds := dsi.(*tc.DeliveryServiceV4)
+				if ds.RangeRequestHandling != nil {
+					if *ds.RangeRequestHandling == 3 {
+						return validation.Validate(ds.RangeSliceBlockSize, validation.Required,
+							// Per Slice Plugin implementation
+							validation.Min(tc.MinRangeSliceBlockSize), // 256KiB
+							validation.Max(tc.MaxRangeSliceBlockSize), // 32MiB
+						)
+					}
+					if ds.RangeSliceBlockSize != nil {
+						return errors.New("rangeSliceBlockSize can only be set if the rangeRequestHandling is set to 3 (Use the Slice Plugin)")
+					}
+				}
+				return nil
+			})),
+		"protocol": validation.Validate(ds.Protocol,
+			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
+		"qstringIgnore": validation.Validate(ds.QStringIgnore,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
+		"rangeRequestHandling": validation.Validate(ds.RangeRequestHandling,
+			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
+		"topology": validation.Validate(ds,
+			validation.By(func(dsi interface{}) error {
+				ds := dsi.(*tc.DeliveryServiceV4)
+				if ds.Topology != nil && tc.DSType(typeName).IsSteering() {
+					return fmt.Errorf("steering deliveryservice types cannot be assigned to a topology")
+				}
+				return nil
+			})),
+		"maxRequestHeaderBytes": validation.Validate(ds,
+			validation.By(func(dsi interface{}) error {
+				ds := dsi.(*tc.DeliveryServiceV4)
+				if ds.MaxRequestHeaderBytes == nil {
+					return errors.New("maxRequestHeaderBytes empty, must be a valid positive value")
+				}
+				if *ds.MaxRequestHeaderBytes < 0 || *ds.MaxRequestHeaderBytes > 2147483647 {
+					return errors.New("maxRequestHeaderBytes must be a valid non negative value between 0 and 2147483647")
+				}
+				return nil
+			})),
+	}
+	toErrs := tovalidate.ToErrors(errs)
+	if len(toErrs) > 0 {
+		return errors.New(util.JoinErrsStr(toErrs))
+	}
+	return nil
+}
+
 func selectMaxLastUpdatedQuery(where string) string {
 	return `SELECT max(t) from (
 		SELECT max(ds.last_updated) as t from deliveryservice as ds
@@ -1225,7 +1510,7 @@ func getTypeFromID(id int, tx *sql.Tx) (tc.DSType, error) {
 	return tc.DSTypeFromString(name), nil
 }
 
-func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServiceNullableV30) error {
+func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServiceV4) error {
 	count := 0
 	q := `SELECT count(*) FROM origin WHERE deliveryservice = $1 AND is_primary`
 	if err := tx.QueryRow(q, *ds.ID).Scan(&count); err != nil {
@@ -1249,7 +1534,7 @@ func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServi
 		return createPrimaryOrigin(tx, user, ds)
 	}
 
-	protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
+	protocol, fqdn, port, err := parseOrgServerFQDN(*ds.OrgServerFQDN)
 	if err != nil {
 		return fmt.Errorf("updating primary origin: %v", err)
 	}
@@ -1265,12 +1550,12 @@ func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServi
 	return nil
 }
 
-func createPrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServiceNullableV30) error {
+func createPrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds tc.DeliveryServiceV4) error {
 	if ds.OrgServerFQDN == nil {
 		return nil
 	}
 
-	protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
+	protocol, fqdn, port, err := parseOrgServerFQDN(*ds.OrgServerFQDN)
 	if err != nil {
 		return fmt.Errorf("creating primary origin: %v", err)
 	}
@@ -1297,25 +1582,24 @@ func getDSType(tx *sql.Tx, xmlid string) (tc.DSType, bool, error) {
 	return tc.DSTypeFromString(name), true, nil
 }
 
-func GetDeliveryServices(query string, queryValues map[string]interface{}, tx *sqlx.Tx) ([]tc.DeliveryServiceNullableV30, error, error, int) {
+func GetDeliveryServices(query string, queryValues map[string]interface{}, tx *sqlx.Tx) ([]tc.DeliveryServiceV4, error, error, int) {
 	rows, err := tx.NamedQuery(query, queryValues)
 	if err != nil {
 		return nil, nil, fmt.Errorf("querying: %v", err), http.StatusInternalServerError
 	}
 	defer rows.Close()
 
-	dses := []tc.DeliveryServiceNullableV30{}
+	dses := []tc.DeliveryServiceV4{}
 	dsCDNDomains := map[string]string{}
 
 	// ensure json generated from this slice won't come out as `null` if empty
 	dsQueryParams := []string{}
 
 	for rows.Next() {
-		ds := tc.DeliveryServiceNullableV30{}
+		ds := tc.DeliveryServiceV4{}
 		cdnDomain := ""
 		err := rows.Scan(&ds.Active,
 			&ds.AnonymousBlockingEnabled,
-			&ds.CacheURL,
 			&ds.CCRDNSTTL,
 			&ds.CDNID,
 			&ds.CDNName,
@@ -1432,43 +1716,6 @@ func GetDeliveryServices(query string, queryValues map[string]interface{}, tx *s
 	return dses, nil, nil, http.StatusOK
 }
 
-// getHostName gets the host name used for delivery service requests. The dsProtocol may be nil, if the delivery service type doesn't have a protocol (e.g. ANY_MAP).
-func getHostName(dsProtocol *int, dsType tc.DSType, dsRoutingName string, dsMatchList []tc.DeliveryServiceMatch, cdnDomain string) (string, error) {
-	exampleURLs := MakeExampleURLs(dsProtocol, dsType, dsRoutingName, dsMatchList, cdnDomain)
-
-	exampleURL := ""
-	if dsProtocol != nil && *dsProtocol == 2 {
-		if len(exampleURLs) < 2 {
-			return "", errors.New("missing example URLs (does your delivery service have matchsets?)")
-		}
-		exampleURL = exampleURLs[1]
-	} else {
-		if len(exampleURLs) < 1 {
-			return "", errors.New("missing example URLs (does your delivery service have matchsets?)")
-		}
-		exampleURL = exampleURLs[0]
-	}
-
-	host := strings.NewReplacer(`http://`, ``, `https://`, ``).Replace(exampleURL)
-	if dsType.IsHTTP() {
-		if firstDot := strings.Index(host, "."); firstDot == -1 {
-			host = "*" // TODO warn? error?
-		} else {
-			host = "*" + host[firstDot:]
-		}
-	}
-	return host, nil
-}
-
-func getCDNName(cdnID int, tx *sql.Tx) (string, error) {
-	q := `SELECT cdn.name FROM cdn where cdn.id = $1`
-	cdnName := ""
-	if err := tx.QueryRow(q, cdnID).Scan(&cdnName); err != nil {
-		return "", fmt.Errorf("getting CDN name with id '%v': %v", cdnID, err)
-	}
-	return cdnName, nil
-}
-
 func getCDNDomain(dsID int, tx *sql.Tx) (string, error) {
 	q := `SELECT cdn.domain_name from cdn where cdn.id = (SELECT ds.cdn_id from deliveryservice as ds where ds.id = $1)`
 	cdnDomain := ""
@@ -1569,16 +1816,9 @@ ORDER BY dsr.set_number
 	return matches, nil
 }
 
-type tierType int
-
-const (
-	midTier tierType = iota
-	edgeTier
-)
-
 // EnsureParams ensures the given delivery service's necessary parameters exist on profiles of servers assigned to the delivery service.
-// Note the edgeHeaderRewrite, midHeaderRewrite, regexRemap, and cacheURL may be nil, if the delivery service does not have those values.
-func EnsureParams(tx *sql.Tx, dsID int, xmlID string, edgeHeaderRewrite *string, midHeaderRewrite *string, regexRemap *string, cacheURL *string, signingAlgorithm *string, dsType tc.DSType, maxOriginConns *int) error {
+// Note the edgeHeaderRewrite, midHeaderRewrite, regexRemap may be nil, if the delivery service does not have those values.
+func EnsureParams(tx *sql.Tx, dsID int, xmlID string, edgeHeaderRewrite *string, midHeaderRewrite *string, regexRemap *string, signingAlgorithm *string, dsType tc.DSType, maxOriginConns *int) error {
 	if err := ensureHeaderRewriteParams(tx, dsID, xmlID, edgeHeaderRewrite, edgeTier, dsType, maxOriginConns); err != nil {
 		return errors.New("creating edge header rewrite parameters: " + err.Error())
 	}
@@ -1588,15 +1828,25 @@ func EnsureParams(tx *sql.Tx, dsID int, xmlID string, edgeHeaderRewrite *string,
 	if err := ensureRegexRemapParams(tx, dsID, xmlID, regexRemap); err != nil {
 		return errors.New("creating mid regex remap parameters: " + err.Error())
 	}
-	if err := ensureCacheURLParams(tx, dsID, xmlID, cacheURL); err != nil {
-		return errors.New("creating mid cacheurl parameters: " + err.Error())
-	}
 	if err := ensureURLSigParams(tx, dsID, xmlID, signingAlgorithm); err != nil {
 		return errors.New("creating urlsig parameters: " + err.Error())
 	}
 	return nil
 }
 
+// EnsureCacheURLParams ensures the given delivery service's cachrurl parameters exist on profiles of servers assigned to the delivery service.
+func EnsureCacheURLParams(tx *sql.Tx, dsID int, xmlID string, cacheURL *string) error {
+	configFile := "cacheurl_" + xmlID + ".config"
+	if cacheURL == nil || *cacheURL == "" {
+		return deleteLocationParam(tx, configFile)
+	}
+	locationParamID, err := ensureLocation(tx, configFile)
+	if err != nil {
+		return err
+	}
+	return createDSLocationProfileParams(tx, locationParamID, dsID)
+}
+
 func ensureHeaderRewriteParams(tx *sql.Tx, dsID int, xmlID string, hdrRW *string, tier tierType, dsType tc.DSType, maxOriginConns *int) error {
 	configFile := "hdr_rw_" + xmlID + ".config"
 	if tier == midTier {
@@ -1656,18 +1906,6 @@ func ensureRegexRemapParams(tx *sql.Tx, dsID int, xmlID string, regexRemap *stri
 	return createDSLocationProfileParams(tx, locationParamID, dsID)
 }
 
-func ensureCacheURLParams(tx *sql.Tx, dsID int, xmlID string, cacheURL *string) error {
-	configFile := "cacheurl_" + xmlID + ".config"
-	if cacheURL == nil || *cacheURL == "" {
-		return deleteLocationParam(tx, configFile)
-	}
-	locationParamID, err := ensureLocation(tx, configFile)
-	if err != nil {
-		return err
-	}
-	return createDSLocationProfileParams(tx, locationParamID, dsID)
-}
-
 // createDSLocationProfileParams adds the given parameter to all profiles assigned to servers which are assigned to the given delivery service.
 func createDSLocationProfileParams(tx *sql.Tx, locationParamID int, deliveryServiceID int) error {
 	profileParameterQuery := `
@@ -1741,7 +1979,7 @@ func GetDSSelectQuery() string {
 }
 
 // getTenantID returns the tenant Id of the given delivery service. Note it may return a nil id and nil error, if the tenant ID in the database is nil.
-func getTenantID(tx *sql.Tx, ds *tc.DeliveryServiceNullableV30) (*int, error) {
+func getTenantID(tx *sql.Tx, ds *tc.DeliveryServiceV4) (*int, error) {
 	if ds.ID == nil && ds.XMLID == nil {
 		return nil, errors.New("delivery service has no ID or XMLID")
 	}
@@ -1753,7 +1991,7 @@ func getTenantID(tx *sql.Tx, ds *tc.DeliveryServiceNullableV30) (*int, error) {
 	return existingID, err
 }
 
-func isTenantAuthorized(inf *api.APIInfo, ds *tc.DeliveryServiceNullableV30) (bool, error) {
+func isTenantAuthorized(inf *api.APIInfo, ds *tc.DeliveryServiceV4) (bool, error) {
 	tx := inf.Tx.Tx
 	user := inf.User
 
@@ -1829,12 +2067,59 @@ func getSSLVersion(xmlId string, tx *sql.Tx) (bool, error) {
 	return exists, err
 }
 
+func setNilIfEmpty(ptrs ...**string) {
+	for _, s := range ptrs {
+		if *s != nil && strings.TrimSpace(**s) == "" {
+			*s = nil
+		}
+	}
+}
+
+func sanitize(ds *tc.DeliveryServiceV4) {
+	if ds.GeoLimitCountries != nil {
+		*ds.GeoLimitCountries = strings.ToUpper(strings.Replace(*ds.GeoLimitCountries, " ", "", -1))
+	}
+	if ds.ProfileID != nil && *ds.ProfileID == -1 {
+		ds.ProfileID = nil
+	}
+	setNilIfEmpty(
+		&ds.EdgeHeaderRewrite,
+		&ds.MidHeaderRewrite,
+		&ds.FirstHeaderRewrite,
+		&ds.InnerHeaderRewrite,
+		&ds.LastHeaderRewrite,
+	)
+	if ds.RoutingName == nil || *ds.RoutingName == "" {
+		ds.RoutingName = util.StrPtr(tc.DefaultRoutingName)
+	}
+	if ds.AnonymousBlockingEnabled == nil {
+		ds.AnonymousBlockingEnabled = util.BoolPtr(false)
+	}
+	signedAlgorithm := tc.SigningAlgorithmURLSig
+	if ds.Signed && (ds.SigningAlgorithm == nil || *ds.SigningAlgorithm == "") {
+		ds.SigningAlgorithm = &signedAlgorithm
+	}
+	if !ds.Signed && ds.SigningAlgorithm != nil && *ds.SigningAlgorithm == signedAlgorithm {
+		ds.Signed = true
+	}
+	if ds.MaxOriginConnections == nil || *ds.MaxOriginConnections < 0 {
+		ds.MaxOriginConnections = util.IntPtr(0)
+	}
+	if ds.DeepCachingType == nil {
+		s := tc.DeepCachingType("")
+		ds.DeepCachingType = &s
+	}
+	*ds.DeepCachingType = tc.DeepCachingTypeFromString(string(*ds.DeepCachingType))
+	if ds.MaxRequestHeaderBytes == nil {
+		ds.MaxRequestHeaderBytes = util.IntPtr(tc.DefaultMaxRequestHeaderBytes)
+	}
+}
+
 func selectQuery() string {
 	return `
 SELECT
 ds.active,
 ds.anonymous_blocking_enabled,
-ds.cacheurl,
 ds.ccr_dns_ttl,
 ds.cdn_id,
 cdn.name as cdnName,
@@ -1920,66 +2205,65 @@ func updateDSQuery() string {
 UPDATE
 deliveryservice SET
 active=$1,
-cacheurl=$2,
-ccr_dns_ttl=$3,
-cdn_id=$4,
-check_path=$5,
-deep_caching_type=$6,
-display_name=$7,
-dns_bypass_cname=$8,
-dns_bypass_ip=$9,
-dns_bypass_ip6=$10,
-dns_bypass_ttl=$11,
-dscp=$12,
-edge_header_rewrite=$13,
-geolimit_redirect_url=$14,
-geo_limit=$15,
-geo_limit_countries=$16,
-geo_provider=$17,
-global_max_mbps=$18,
-global_max_tps=$19,
-fq_pacing_rate=$20,
-http_bypass_fqdn=$21,
-info_url=$22,
-initial_dispersion=$23,
-ipv6_routing_enabled=$24,
-logs_enabled=$25,
-long_desc=$26,
-long_desc_1=$27,
-long_desc_2=$28,
-max_dns_answers=$29,
-mid_header_rewrite=$30,
-miss_lat=$31,
-miss_long=$32,
-multi_site_origin=$33,
-origin_shield=$34,
-profile=$35,
-protocol=$36,
-qstring_ignore=$37,
-range_request_handling=$38,
-regex_remap=$39,
-regional_geo_blocking=$40,
-remap_text=$41,
-routing_name=$42,
-signing_algorithm=$43,
-ssl_key_version=$44,
-tenant_id=$45,
-tr_request_headers=$46,
-tr_response_headers=$47,
-type=$48,
-xml_id=$49,
-anonymous_blocking_enabled=$50,
-consistent_hash_regex=$51,
-max_origin_connections=$52,
-ecs_enabled=$53,
-range_slice_block_size=$54,
-topology=$55,
-first_header_rewrite=$56,
-inner_header_rewrite=$57,
-last_header_rewrite=$58,
-service_category=$59,
-max_request_header_bytes=$60
-WHERE id=$61
+ccr_dns_ttl=$2,
+cdn_id=$3,
+check_path=$4,
+deep_caching_type=$5,
+display_name=$6,
+dns_bypass_cname=$7,
+dns_bypass_ip=$8,
+dns_bypass_ip6=$9,
+dns_bypass_ttl=$10,
+dscp=$11,
+edge_header_rewrite=$12,
+geolimit_redirect_url=$13,
+geo_limit=$14,
+geo_limit_countries=$15,
+geo_provider=$16,
+global_max_mbps=$17,
+global_max_tps=$18,
+fq_pacing_rate=$19,
+http_bypass_fqdn=$20,
+info_url=$21,
+initial_dispersion=$22,
+ipv6_routing_enabled=$23,
+logs_enabled=$24,
+long_desc=$25,
+long_desc_1=$26,
+long_desc_2=$27,
+max_dns_answers=$28,
+mid_header_rewrite=$29,
+miss_lat=$30,
+miss_long=$31,
+multi_site_origin=$32,
+origin_shield=$33,
+profile=$34,
+protocol=$35,
+qstring_ignore=$36,
+range_request_handling=$37,
+regex_remap=$38,
+regional_geo_blocking=$39,
+remap_text=$40,
+routing_name=$41,
+signing_algorithm=$42,
+ssl_key_version=$43,
+tenant_id=$44,
+tr_request_headers=$45,
+tr_response_headers=$46,
+type=$47,
+xml_id=$48,
+anonymous_blocking_enabled=$49,
+consistent_hash_regex=$50,
+max_origin_connections=$51,
+ecs_enabled=$52,
+range_slice_block_size=$53,
+topology=$54,
+first_header_rewrite=$55,
+inner_header_rewrite=$56,
+last_header_rewrite=$57,
+service_category=$58,
+max_request_header_bytes=$59
+WHERE id=$60
 RETURNING last_updated
 `
 }
@@ -1989,7 +2273,6 @@ func insertQuery() string {
 INSERT INTO deliveryservice (
 active,
 anonymous_blocking_enabled,
-cacheurl,
 ccr_dns_ttl,
 cdn_id,
 check_path,
@@ -2048,7 +2331,7 @@ last_header_rewrite,
 service_category,
 max_request_header_bytes
 )
-VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$60)
+VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59)
 RETURNING id, last_updated
 `
 }
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
index 521d6c2..d2fbeeb 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
@@ -58,7 +58,7 @@ func TestGetDeliveryServiceRequest(t *testing.T) {
 	b := true
 	u := "UPDATE"
 	st := tc.RequestStatusSubmitted
-	ds := tc.DeliveryServiceNullableV30{}
+	ds := tc.DeliveryServiceV4{}
 	ds.XMLID = &s
 	ds.CDNID = &i
 	ds.LogsEnabled = &b
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/validate.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/validate.go
index 99d0306..c72ac60 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/validate.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/validate.go
@@ -22,6 +22,7 @@ package request
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice"
 	"strconv"
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
@@ -60,7 +61,7 @@ func (req *TODeliveryServiceRequest) Validate() error {
 	}
 	errs := tovalidate.ToErrors(errMap)
 	// ensure the deliveryservice requested is valid
-	e := req.DeliveryService.Validate(req.APIInfo().Tx.Tx)
+	e := deliveryservice.Validate(req.APIInfo().Tx.Tx, req.DeliveryService)
 
 	errs = append(errs, e)
 
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
index 119eb75..27a75bf 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
@@ -97,14 +97,14 @@ func UpdateSafe(w http.ResponseWriter, r *http.Request) {
 	if inf.Version != nil && inf.Version.Major == 1 && inf.Version.Minor < 5 {
 		switch inf.Version.Minor {
 		case 4:
-			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV14{ds.DeliveryServiceNullableV14})
+			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV14{ds.DowngradeToV3().DeliveryServiceNullableV14})
 		case 3:
-			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV13{ds.DeliveryServiceNullableV13})
+			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV13{ds.DowngradeToV3().DeliveryServiceNullableV13})
 		default:
-			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV12{ds.DeliveryServiceNullableV12})
+			api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV12{ds.DowngradeToV3().DeliveryServiceNullableV12})
 		}
 	} else {
-		api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV30{ds})
+		api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, []tc.DeliveryServiceNullableV30{ds.DowngradeToV3()})
 	}
 
 	api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("DS: %s, ID: %d, ACTION: Updated safe fields", *ds.XMLID, *ds.ID), inf.User, tx)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
index 1cb906f..2c2829c 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -474,7 +474,11 @@ func GetReplaceHandler(w http.ResponseWriter, r *http.Request) {
 		respServers = append(respServers, server)
 	}
 
-	if err := deliveryservice.EnsureParams(inf.Tx.Tx, *dsId, ds.Name, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, ds.Type, ds.MaxOriginConnections); err != nil {
+	if err := deliveryservice.EnsureParams(inf.Tx.Tx, *dsId, ds.Name, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.SigningAlgorithm, ds.Type, ds.MaxOriginConnections); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_server replace ensuring ds parameters: "+err.Error()))
+		return
+	}
+	if err := deliveryservice.EnsureCacheURLParams(inf.Tx.Tx, ds.ID, ds.Name, ds.CacheURL); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_server replace ensuring ds parameters: "+err.Error()))
 		return
 	}
@@ -547,7 +551,11 @@ func GetCreateHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if err := deliveryservice.EnsureParams(inf.Tx.Tx, ds.ID, ds.Name, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, ds.Type, ds.MaxOriginConnections); err != nil {
+	if err := deliveryservice.EnsureParams(inf.Tx.Tx, ds.ID, ds.Name, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.SigningAlgorithm, ds.Type, ds.MaxOriginConnections); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_server replace ensuring ds parameters: "+err.Error()))
+		return
+	}
+	if err := deliveryservice.EnsureCacheURLParams(inf.Tx.Tx, ds.ID, ds.Name, ds.CacheURL); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_server replace ensuring ds parameters: "+err.Error()))
 		return
 	}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index a2e4e7d..1c55e8d 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -168,7 +168,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{api.Version{4, 0}, http.MethodDelete, `cachegroups/{id}$`, api.DeleteHandler(&cachegroup.TOCacheGroup{}), auth.PrivLevelOperations, Authenticated, nil, 4278693653},
 
 		{api.Version{4, 0}, http.MethodPost, `cachegroups/{id}/queue_update$`, cachegroup.QueueUpdates, auth.PrivLevelOperations, Authenticated, nil, 40716441103},
-		{api.Version{4, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandler, auth.PrivLevelOperations, Authenticated, nil, 45202404313},
+		{api.Version{4, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandlerV40, auth.PrivLevelOperations, Authenticated, nil, 45202404313},
 
 		//CacheGroup Parameters: CRUD
 		{api.Version{4, 0}, http.MethodGet, `cachegroupparameters/?$`, cachegroupparameter.ReadAllCacheGroupParameters, auth.PrivLevelReadOnly, Authenticated, nil, 4124497243},
@@ -473,8 +473,8 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 
 		////DeliveryServices
 		{api.Version{4, 0}, http.MethodGet, `deliveryservices/?$`, api.ReadHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelReadOnly, Authenticated, nil, 42383172943},
-		{api.Version{4, 0}, http.MethodPost, `deliveryservices/?$`, deliveryservice.CreateV31, auth.PrivLevelOperations, Authenticated, nil, 4064315323},
-		{api.Version{4, 0}, http.MethodPut, `deliveryservices/{id}/?$`, deliveryservice.UpdateV31, auth.PrivLevelOperations, Authenticated, nil, 47665675673},
+		{api.Version{4, 0}, http.MethodPost, `deliveryservices/?$`, deliveryservice.CreateV40, auth.PrivLevelOperations, Authenticated, nil, 4064315323},
+		{api.Version{4, 0}, http.MethodPut, `deliveryservices/{id}/?$`, deliveryservice.UpdateV40, auth.PrivLevelOperations, Authenticated, nil, 47665675673},
 		{api.Version{4, 0}, http.MethodPut, `deliveryservices/{id}/safe/?$`, deliveryservice.UpdateSafe, auth.PrivLevelOperations, Authenticated, nil, 4472109313},
 		{api.Version{4, 0}, http.MethodDelete, `deliveryservices/{id}/?$`, api.DeleteHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelOperations, Authenticated, nil, 4226420743},
 		{api.Version{4, 0}, http.MethodGet, `deliveryservices/{id}/servers/eligible/?$`, deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, Authenticated, nil, 4747615843},
@@ -555,7 +555,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{api.Version{3, 0}, http.MethodDelete, `cachegroups/{id}$`, api.DeleteHandler(&cachegroup.TOCacheGroup{}), auth.PrivLevelOperations, Authenticated, nil, 2278693653},
 
 		{api.Version{3, 0}, http.MethodPost, `cachegroups/{id}/queue_update$`, cachegroup.QueueUpdates, auth.PrivLevelOperations, Authenticated, nil, 20716441103},
-		{api.Version{3, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandler, auth.PrivLevelOperations, Authenticated, nil, 25202404313},
+		{api.Version{3, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandlerV31, auth.PrivLevelOperations, Authenticated, nil, 25202404313},
 
 		//CacheGroup Parameters: CRUD
 		{api.Version{3, 0}, http.MethodGet, `cachegroupparameters/?$`, cachegroupparameter.ReadAllCacheGroupParameters, auth.PrivLevelReadOnly, Authenticated, nil, 2124497243},
@@ -932,7 +932,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{api.Version{2, 0}, http.MethodDelete, `cachegroups/{id}$`, api.DeleteHandler(&cachegroup.TOCacheGroup{}), auth.PrivLevelOperations, Authenticated, nil, 227869365},
 
 		{api.Version{2, 0}, http.MethodPost, `cachegroups/{id}/queue_update$`, cachegroup.QueueUpdates, auth.PrivLevelOperations, Authenticated, nil, 2071644110},
-		{api.Version{2, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandler, auth.PrivLevelOperations, Authenticated, nil, 2520240431},
+		{api.Version{2, 0}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandlerV31, auth.PrivLevelOperations, Authenticated, nil, 2520240431},
 
 		//CacheGroup Parameters: CRUD
 		{api.Version{2, 0}, http.MethodGet, `cachegroupparameters/?$`, cachegroupparameter.ReadAllCacheGroupParameters, auth.PrivLevelReadOnly, Authenticated, nil, 212449724},
@@ -1294,7 +1294,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{api.Version{1, 1}, http.MethodDelete, `cachegroups/{id}$`, api.DeleteHandler(&cachegroup.TOCacheGroup{}), auth.PrivLevelOperations, Authenticated, nil, 257869365},
 
 		{api.Version{1, 1}, http.MethodPost, `cachegroups/{id}/queue_update$`, cachegroup.QueueUpdates, auth.PrivLevelOperations, Authenticated, nil, 1071644110},
-		{api.Version{1, 1}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandler, auth.PrivLevelOperations, Authenticated, nil, 1520240431},
+		{api.Version{1, 1}, http.MethodPost, `cachegroups/{id}/deliveryservices/?$`, cachegroup.DSPostHandlerV31, auth.PrivLevelOperations, Authenticated, nil, 1520240431},
 
 		//CacheGroup Parameters: CRUD
 		{api.Version{1, 1}, http.MethodGet, `cachegroupparameters/?(\.json)?$`, cachegroupparameter.ReadAllCacheGroupParameters, auth.PrivLevelReadOnly, Authenticated, nil, 912449724},
diff --git a/traffic_ops/v4-client/deliveryservice.go b/traffic_ops/v4-client/deliveryservice.go
index 36ecea7..cc49cdc 100644
--- a/traffic_ops/v4-client/deliveryservice.go
+++ b/traffic_ops/v4-client/deliveryservice.go
@@ -155,6 +155,19 @@ func (to *Session) GetDeliveryServicesV30WithHdr(header http.Header, params url.
 	return data.Response, reqInf, err
 }
 
+// GetDeliveryServicesV4 returns all (tenant-visible) Delivery Services that
+// satisfy the passed query string parameters. See the API documentation for
+// information on the available parameters.
+func (to *Session) GetDeliveryServicesV4(header http.Header, params url.Values) ([]tc.DeliveryServiceV4, ReqInf, error) {
+	uri := API_DELIVERY_SERVICES
+	if params != nil {
+		uri += "?" + params.Encode()
+	}
+	var data tc.DeliveryServicesResponseV4
+	reqInf, err := to.get(uri, header, &data)
+	return data.Response, reqInf, err
+}
+
 func (to *Session) GetDeliveryServicesNullableWithHdr(header http.Header) ([]tc.DeliveryServiceNullable, ReqInf, error) {
 	data := struct {
 		Response []tc.DeliveryServiceNullable `json:"response"`
@@ -195,9 +208,9 @@ func (to *Session) GetDeliveryServicesByCDNID(cdnID int) ([]tc.DeliveryServiceNu
 }
 
 // GetDeliveryServiceNullableWithHdr fetches the Delivery Service with the given ID.
-func (to *Session) GetDeliveryServiceNullableWithHdr(id string, header http.Header) (*tc.DeliveryServiceNullableV30, ReqInf, error) {
+func (to *Session) GetDeliveryServiceNullableWithHdr(id string, header http.Header) (*tc.DeliveryServiceV4, ReqInf, error) {
 	data := struct {
-		Response []tc.DeliveryServiceNullableV30 `json:"response"`
+		Response []tc.DeliveryServiceV4 `json:"response"`
 	}{}
 	route := fmt.Sprintf("%s?id=%s", API_DELIVERY_SERVICES, id)
 	reqInf, err := to.get(route, header, &data)
@@ -261,6 +274,61 @@ func (to *Session) GetDeliveryServiceByXMLIDNullable(XMLID string) ([]tc.Deliver
 	return ret, reqInf, err
 }
 
+func (to *Session) CreateDeliveryServiceV4(ds tc.DeliveryServiceV4) (tc.DeliveryServiceV4, ReqInf, error) {
+	var reqInf ReqInf
+	if ds.TypeID == nil && ds.Type != nil {
+		ty, _, err := to.GetTypeByNameWithHdr(ds.Type.String(), nil)
+		if err != nil {
+			return tc.DeliveryServiceV4{}, reqInf, err
+		}
+		if len(ty) == 0 {
+			return tc.DeliveryServiceV4{}, reqInf, fmt.Errorf("no type named %s", ds.Type)
+		}
+		ds.TypeID = &ty[0].ID
+	}
+
+	if ds.CDNID == nil && ds.CDNName != nil {
+		cdns, _, err := to.GetCDNByNameWithHdr(*ds.CDNName, nil)
+		if err != nil {
+			return tc.DeliveryServiceV4{}, reqInf, err
+		}
+		if len(cdns) == 0 {
+			return tc.DeliveryServiceV4{}, reqInf, errors.New("no CDN named " + *ds.CDNName)
+		}
+		ds.CDNID = &cdns[0].ID
+	}
+
+	if ds.ProfileID == nil && ds.ProfileName != nil {
+		profiles, _, err := to.GetProfileByNameWithHdr(*ds.ProfileName, nil)
+		if err != nil {
+			return tc.DeliveryServiceV4{}, reqInf, err
+		}
+		if len(profiles) == 0 {
+			return tc.DeliveryServiceV4{}, reqInf, errors.New("no Profile named " + *ds.ProfileName)
+		}
+		ds.ProfileID = &profiles[0].ID
+	}
+
+	if ds.TenantID == nil && ds.Tenant != nil {
+		ten, _, err := to.TenantByNameWithHdr(*ds.Tenant, nil)
+		if err != nil {
+			return tc.DeliveryServiceV4{}, reqInf, err
+		}
+		ds.TenantID = &ten.ID
+	}
+
+	var data tc.DeliveryServicesResponseV4
+	reqInf, err := to.post(API_DELIVERY_SERVICES, ds, nil, &data)
+	if err != nil {
+		return tc.DeliveryServiceV4{}, reqInf, err
+	}
+	if len(data.Response) != 1 {
+		return tc.DeliveryServiceV4{}, reqInf, fmt.Errorf("failed to create Delivery Service, response indicated %d were created", len(data.Response))
+	}
+
+	return data.Response[0], reqInf, nil
+}
+
 // CreateDeliveryServiceV30 creates the Delivery Service it's passed.
 func (to *Session) CreateDeliveryServiceV30(ds tc.DeliveryServiceNullableV30) (tc.DeliveryServiceNullableV30, ReqInf, error) {
 	var reqInf ReqInf
@@ -376,19 +444,18 @@ func (to *Session) CreateDeliveryServiceNullable(ds *tc.DeliveryServiceNullable)
 	return &data, nil
 }
 
-// UpdateDeliveryServiceV30WithHdr replaces the Delivery Service identified by the
+// UpdateDeliveryServiceV4 replaces the Delivery Service identified by the
 // integral, unique identifier 'id' with the one it's passed.
-func (to *Session) UpdateDeliveryServiceV30WithHdr(id int, ds tc.DeliveryServiceNullableV30, header http.Header) (tc.DeliveryServiceNullableV30, ReqInf, error) {
-	var data tc.DeliveryServicesResponseV30
+func (to *Session) UpdateDeliveryServiceV4(id int, ds tc.DeliveryServiceV4, header http.Header) (tc.DeliveryServiceV4, ReqInf, error) {
+	var data tc.DeliveryServicesResponseV4
 	reqInf, err := to.put(fmt.Sprintf(API_DELIVERY_SERVICE_ID, id), ds, header, &data)
 	if err != nil {
-		return tc.DeliveryServiceNullableV30{}, reqInf, err
+		return tc.DeliveryServiceV4{}, reqInf, err
 	}
 	if len(data.Response) != 1 {
-		return tc.DeliveryServiceNullableV30{}, reqInf, fmt.Errorf("failed to update Delivery Service #%d; response indicated that %d were updated", id, len(data.Response))
+		return tc.DeliveryServiceV4{}, reqInf, fmt.Errorf("failed to update Delivery Service #%d; response indicated that %d were updated", id, len(data.Response))
 	}
 	return data.Response[0], reqInf, nil
-
 }
 
 // UpdateDeliveryServiceNullable updates the DeliveryService matching the ID it's
diff --git a/traffic_ops_ort/atstccfg/cfgfile/cfgfile_test.go b/traffic_ops_ort/atstccfg/cfgfile/cfgfile_test.go
index 4cbb966..4b4c4de 100644
--- a/traffic_ops_ort/atstccfg/cfgfile/cfgfile_test.go
+++ b/traffic_ops_ort/atstccfg/cfgfile/cfgfile_test.go
@@ -79,12 +79,14 @@ func TestPreprocessConfigFile(t *testing.T) {
 	{
 		server := &atscfg.Server{}
 		server.TCPPort = util.IntPtr(8080)
-		server.Interfaces = []tc.ServerInterfaceInfo{
-			tc.ServerInterfaceInfo{
-				IPAddresses: []tc.ServerIPAddress{
-					tc.ServerIPAddress{
-						Address:        "127.0.2.1",
-						ServiceAddress: true,
+		server.Interfaces = []tc.ServerInterfaceInfoV40{
+			tc.ServerInterfaceInfoV40{
+				ServerInterfaceInfo: tc.ServerInterfaceInfo{
+					IPAddresses: []tc.ServerIPAddress{
+						tc.ServerIPAddress{
+							Address:        "127.0.2.1",
+							ServiceAddress: true,
+						},
 					},
 				},
 			},
@@ -105,12 +107,14 @@ func TestPreprocessConfigFile(t *testing.T) {
 	{
 		server := &atscfg.Server{}
 		server.TCPPort = util.IntPtr(80)
-		server.Interfaces = []tc.ServerInterfaceInfo{
-			tc.ServerInterfaceInfo{
-				IPAddresses: []tc.ServerIPAddress{
-					tc.ServerIPAddress{
-						Address:        "127.0.2.1",
-						ServiceAddress: true,
+		server.Interfaces = []tc.ServerInterfaceInfoV40{
+			tc.ServerInterfaceInfoV40{
+				ServerInterfaceInfo: tc.ServerInterfaceInfo{
+					IPAddresses: []tc.ServerIPAddress{
+						tc.ServerIPAddress{
+							Address:        "127.0.2.1",
+							ServiceAddress: true,
+						},
 					},
 				},
 			},
@@ -250,7 +254,6 @@ func randDS() *atscfg.DeliveryService {
 	ds.TRRequestHeaders = randStr()
 	ds.Active = randBool()
 	ds.AnonymousBlockingEnabled = randBool()
-	ds.CacheURL = randStr()
 	ds.CCRDNSTTL = randInt()
 	ds.CDNID = randInt()
 	ds.CDNName = randStr()
@@ -334,21 +337,25 @@ func randServer() *atscfg.Server {
 	sv.ILOPassword = randStr()
 	sv.ILOUsername = randStr()
 
-	sv.Interfaces = []tc.ServerInterfaceInfo{
-		tc.ServerInterfaceInfo{
-			Name: *randStr(),
-			IPAddresses: []tc.ServerIPAddress{
-				tc.ServerIPAddress{
-					Address:        *randStr(),
-					Gateway:        randStr(),
-					ServiceAddress: true,
-				},
-				tc.ServerIPAddress{
-					Address:        *randStr(),
-					Gateway:        randStr(),
-					ServiceAddress: true,
+	sv.Interfaces = []tc.ServerInterfaceInfoV40{
+		tc.ServerInterfaceInfoV40{
+			ServerInterfaceInfo: tc.ServerInterfaceInfo{
+				Name: *randStr(),
+				IPAddresses: []tc.ServerIPAddress{
+					tc.ServerIPAddress{
+						Address:        *randStr(),
+						Gateway:        randStr(),
+						ServiceAddress: true,
+					},
+					tc.ServerIPAddress{
+						Address:        *randStr(),
+						Gateway:        randStr(),
+						ServiceAddress: true,
+					},
 				},
 			},
+			RouterHostName: *randStr(),
+			RouterPortName: *randStr(),
 		},
 	}
 
@@ -364,8 +371,6 @@ func randServer() *atscfg.Server {
 	sv.ProfileID = randInt()
 	sv.Rack = randStr()
 	sv.RevalPending = randBool()
-	sv.RouterHostName = randStr()
-	sv.RouterPortName = randStr()
 	sv.Status = randStr()
 	sv.StatusID = randInt()
 	sv.TCPPort = randInt()
diff --git a/traffic_ops_ort/atstccfg/cfgfile/routing.go b/traffic_ops_ort/atstccfg/cfgfile/routing.go
index 82df4d8..059e7fa 100644
--- a/traffic_ops_ort/atstccfg/cfgfile/routing.go
+++ b/traffic_ops_ort/atstccfg/cfgfile/routing.go
@@ -81,7 +81,6 @@ var configFileLiteralFuncs = []ConfigFileLiteralFunc{
 	{"astats.config", MakeAstatsDotConfig},
 	{"bg_fetch.config", MakeBGFetchDotConfig},
 	{"cache.config", MakeCacheDotConfig},
-	{"cacheurl.config", MakeCacheURLPlain},
 	{"chkconfig", MakeChkconfig},
 	{"drop_qstring.config", MakeDropQStringDotConfig},
 	{"hosting.config", MakeHostingDotConfig},
@@ -102,7 +101,6 @@ var configFileLiteralFuncs = []ConfigFileLiteralFunc{
 }
 
 var configFilePrefixSuffixFuncs = []ConfigFilePrefixSuffixFunc{
-	{"cacheurl", ".config", MakeCacheURL},
 	{atscfg.HeaderRewriteFirstPrefix, ".config", MakeTopologyHeaderRewrite},
 	{atscfg.HeaderRewriteInnerPrefix, ".config", MakeTopologyHeaderRewrite},
 	{atscfg.HeaderRewriteLastPrefix, ".config", MakeTopologyHeaderRewrite},
diff --git a/traffic_ops_ort/atstccfg/cfgfile/wrappers.go b/traffic_ops_ort/atstccfg/cfgfile/wrappers.go
index 5d78867..eb71884 100644
--- a/traffic_ops_ort/atstccfg/cfgfile/wrappers.go
+++ b/traffic_ops_ort/atstccfg/cfgfile/wrappers.go
@@ -67,14 +67,6 @@ func MakeCacheDotConfig(toData *config.TOData, fileName string, hdrCommentTxt st
 	return atscfg.MakeCacheDotConfig(toData.Server, toData.Servers, toData.DeliveryServices, toData.DeliveryServiceServers, hdrCommentTxt)
 }
 
-func MakeCacheURL(toData *config.TOData, fileName string, hdrCommentTxt string, cfg config.TCCfg) (atscfg.Cfg, error) {
-	return atscfg.MakeCacheURLDotConfig(fileName, toData.Server, toData.DeliveryServices, toData.DeliveryServiceServers, hdrCommentTxt)
-}
-
-func MakeCacheURLPlain(toData *config.TOData, fileName string, hdrCommentTxt string, cfg config.TCCfg) (atscfg.Cfg, error) {
-	return MakeCacheURL(toData, "cacheurl.config", hdrCommentTxt, cfg)
-}
-
 func MakeChkconfig(toData *config.TOData, fileName string, hdrCommentTxt string, cfg config.TCCfg) (atscfg.Cfg, error) {
 	return atscfg.MakeChkconfig(toData.ServerParams)
 }
diff --git a/traffic_ops_ort/atstccfg/toreq/toreq.go b/traffic_ops_ort/atstccfg/toreq/toreq.go
index 8830316..b8f812e 100644
--- a/traffic_ops_ort/atstccfg/toreq/toreq.go
+++ b/traffic_ops_ort/atstccfg/toreq/toreq.go
@@ -190,7 +190,7 @@ func serverToLatest(sv *tc.ServerV1) (*atscfg.Server, error) {
 		IPIsService:       util.BoolPtr(true),
 		IP6IsService:      util.BoolPtr(svn.IP6Address != nil && *svn.IP6Address != ""),
 	}
-	svLatest, err := sv2.Upgrade()
+	svLatest, err := sv2.UpgradeToV40()
 	if err != nil {
 		return nil, errors.New("upgrading: " + err.Error())
 	}
diff --git a/traffic_ops_ort/atstccfg/toreqnew/toreqnew.go b/traffic_ops_ort/atstccfg/toreqnew/toreqnew.go
index 517eac0..4178c3d 100644
--- a/traffic_ops_ort/atstccfg/toreqnew/toreqnew.go
+++ b/traffic_ops_ort/atstccfg/toreqnew/toreqnew.go
@@ -42,7 +42,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 
-	toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
+	toclient "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 	"github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/torequtil"
 )
 
@@ -81,7 +81,7 @@ func (cl *TOClient) GetCDNDeliveryServices(cdnID int) ([]atscfg.DeliveryService,
 	err := torequtil.GetRetry(cl.NumRetries, "cdn_"+strconv.Itoa(cdnID)+"_deliveryservices", &deliveryServices, func(obj interface{}) error {
 		params := url.Values{}
 		params.Set("cdn", strconv.Itoa(cdnID))
-		toDSes, reqInf, err := cl.C.GetDeliveryServicesV30WithHdr(nil, params)
+		toDSes, reqInf, err := cl.C.GetDeliveryServicesV4(nil, params)
 		if err != nil {
 			if IsUnsupportedErr(err) {
 				unsupported = true
@@ -135,7 +135,7 @@ func (cl *TOClient) GetServerUpdateStatus(cacheHostName tc.CacheName) (tc.Server
 	unsupported := false
 	toAddr := net.Addr(nil)
 	err := torequtil.GetRetry(cl.NumRetries, "server_update_status_"+string(cacheHostName), &status, func(obj interface{}) error {
-		toStatus, reqInf, err := cl.C.GetServerUpdateStatus(string(cacheHostName))
+		toStatus, reqInf, err := cl.C.GetServerUpdateStatus(string(cacheHostName), nil)
 		if err != nil {
 			if IsUnsupportedErr(err) {
 				unsupported = true
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/steeringtargets.go b/traffic_ops_ort/testing/ort-tests/tcdata/steeringtargets.go
index 6ffe48a..a9a80fb 100644
--- a/traffic_ops_ort/testing/ort-tests/tcdata/steeringtargets.go
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/steeringtargets.go
@@ -20,7 +20,7 @@ import (
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-util"
-	"github.com/apache/trafficcontrol/traffic_ops/client"
+	"github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
 var SteeringUserSession *client.Session
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
index 3732198..43b3935 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
@@ -708,34 +708,6 @@ under the License.
                             </aside>
                         </div>
                     </div>
-                    <div class="form-group" ng-class="{'has-error': hasError(cacheConfig.cacheurl), 'has-feedback': hasError(cacheConfig.cacheurl)}">
-                        <label class="has-tooltip control-label col-md-2 col-sm-2 col-xs-12" for="cacheurl">Cache URL Expression<div class="helptooltip">
-                            <div class="helptext">
-                                Allows you to manipulate the cache key of the incoming requests. Normally, the cache key is the origin domain. This can be changed so that multiple services can share a cache key, can also be used to preserve cached content if service origin is changed.
-                                <br>
-                                <aside>
-                                    <h6>Note</h6>
-                                    <p>This only works with Edge-tier cache servers running <abbr title="Apache Traffic Server">ATS</abbr> version 6 and earlier. This <em>must</em> be empty if using <abbr title="Apache Traffic Server">ATS</abbr> 7 and / or the <a href="https://docs.trafficserver.apache.org/en/7.1.x/admin-guide/plugins/cachekey.en.html" target="_blank">cachekey plugin.</a></p>
-                                    <br>
-                                    <br>
-                                    See <a href="https://docs.trafficserver.apache.org/en/6.2.x/admin-guide/plugins/cacheurl.en.html" target="_blank">ATS documentation on cacheurl</a>
-                                </aside>
-                                <aside class="warning">
-                                    <h6>Warning</h6>
-                                    <p>This field is deprecated, and is subject to removal in upcoming releases. It is recommended that this be left blank.</p>
-                                </aside>
-                            </div>
-                        </div>
-                        </label>
-                        <div class="col-md-10 col-sm-10 col-xs-12">
-                            <input id="cacheurl" name="cacheurl" type="text" class="form-control" ng-model="deliveryService.cacheurl" maxlength="1024">
-                            <small class="input-error" ng-show="hasPropertyError(cacheConfig.cacheurl, 'maxlength')">Too Long</small>
-                            <aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.cacheurl != dsCurrent.cacheurl">
-                                <h3>Current Value</h3>
-                                <pre>{{::dsCurrent.cacheurl}}</pre>
-                            </aside>
-                        </div>
-                    </div>
                 </ng-form>
             </fieldset>
             <fieldset>
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
index cf7f05d..76b21d0 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
@@ -708,34 +708,6 @@ under the License.
                             </aside>
                         </div>
                     </div>
-                    <div class="form-group" ng-class="{'has-error': hasError(cacheConfig.cacheurl), 'has-feedback': hasError(cacheConfig.cacheurl)}">
-                        <label class="has-tooltip control-label col-md-2 col-sm-2 col-xs-12" for="cacheurl">Cache URL Expression<div class="helptooltip">
-                            <div class="helptext">
-                                Allows you to manipulate the cache key of the incoming requests. Normally, the cache key is the origin domain. This can be changed so that multiple services can share a cache key, can also be used to preserve cached content if service origin is changed.
-                                <br>
-                                <aside>
-                                    <h6>Note</h6>
-                                    <p>This only works with Edge-tier cache servers running <abbr title="Apache Traffic Server">ATS</abbr> version 6 and earlier. This <em>must</em> be empty if using <abbr title="Apache Traffic Server">ATS</abbr> 7 and / or the <a href="https://docs.trafficserver.apache.org/en/7.1.x/admin-guide/plugins/cachekey.en.html" target="_blank">cachekey plugin.</a></p>
-                                    <br>
-                                    <br>
-                                    See <a href="https://docs.trafficserver.apache.org/en/6.2.x/admin-guide/plugins/cacheurl.en.html" target="_blank">ATS documentation on cacheurl</a>
-                                </aside>
-                                <aside class="warning">
-                                    <h6>Warning</h6>
-                                    <p>This field is deprecated, and is subject to removal in upcoming releases. It is recommended that this be left blank.</p>
-                                </aside>
-                            </div>
-                        </div>
-                        </label>
-                        <div class="col-md-10 col-sm-10 col-xs-12">
-                            <input id="cacheurl" name="cacheurl" type="text" class="form-control" ng-model="deliveryService.cacheurl" maxlength="1024">
-                            <small class="input-error" ng-show="hasPropertyError(cacheConfig.cacheurl, 'maxlength')">Too Long</small>
-                            <aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.cacheurl != dsCurrent.cacheurl">
-                                <h3>Current Value</h3>
-                                <pre>{{::dsCurrent.cacheurl}}</pre>
-                            </aside>
-                        </div>
-                    </div>
                 </ng-form>
             </fieldset>
             <fieldset>
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
index 288433d..638532a 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
@@ -302,34 +302,6 @@ under the License.
                             </aside>
                         </div>
                     </div>
-                    <div class="form-group" ng-class="{'has-error': hasError(cacheConfig.cacheurl), 'has-feedback': hasError(cacheConfig.cacheurl)}">
-                        <label class="has-tooltip control-label col-md-2 col-sm-2 col-xs-12" for="cacheurl">Cache URL Expression<div class="helptooltip">
-                            <div class="helptext">
-                                Allows you to manipulate the cache key of the incoming requests. Normally, the cache key is the origin domain. This can be changed so that multiple services can share a cache key, can also be used to preserve cached content if service origin is changed.
-                                <br>
-                                <aside>
-                                    <h6>Note</h6>
-                                    <p>This only works with Edge-tier cache servers running <abbr title="Apache Traffic Server">ATS</abbr> version 6 and earlier. This <em>must</em> be empty if using <abbr title="Apache Traffic Server">ATS</abbr> 7 and / or the <a href="https://docs.trafficserver.apache.org/en/7.1.x/admin-guide/plugins/cachekey.en.html" target="_blank">cachekey plugin.</a></p>
-                                    <br>
-                                    <br>
-                                    See <a href="https://docs.trafficserver.apache.org/en/6.2.x/admin-guide/plugins/cacheurl.en.html" target="_blank">ATS documentation on cacheurl</a>
-                                </aside>
-                                <aside class="warning">
-                                    <h6>Warning</h6>
-                                    <p>This field is deprecated, and is subject to removal in upcoming releases. It is recommended that this be left blank.</p>
-                                </aside>
-                            </div>
-                        </div>
-                        </label>
-                        <div class="col-md-10 col-sm-10 col-xs-12">
-                            <input id="cacheurl" name="cacheurl" type="text" class="form-control" ng-model="deliveryService.cacheurl" maxlength="1024">
-                            <small class="input-error" ng-show="hasPropertyError(cacheConfig.cacheurl, 'maxlength')">Too Long</small>
-                            <aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.cacheurl != dsCurrent.cacheurl">
-                                <h3>Current Value</h3>
-                                <pre>{{::dsCurrent.cacheurl}}</pre>
-                            </aside>
-                        </div>
-                    </div>
                 </ng-form>
             </fieldset>
             <div class="modal-footer">