Repository: prebid/prebid-server
Pull request #1131 (2019-12-04 15:46:13)

Revert changes to GetExtCacheData() made in #1104 (#1130)
As described in #1130 the changes removing the left most `/` in `external_cache.path` breaks the cache response send by PBS when delivering a line item using the universal creative js (https://github.com/prebid/prebid-universal-creative).  There is no documentation on where the slash should be placed for the `external_cache`.  Should it be
```yaml
external_cache:
  host: myhost.com
  path: /c/v1/cache
```
or 
```yaml
external_cache:
  host: myhost.com/
  path: c/v1/cache
```
or neither and the universal creative needs to be updated?

The one test that exists has no leading slash on `external_cache.path` or trailing slash on `external_cache.host` so I am unsure where it is supposed to go. See https://github.com/prebid/prebid-server/blob/master/exchange/exchange_test.go#L1293.  

Or does the current code need to be changed to add in `/` where missing to make a valid url as it relates to the universal creative at https://github.com/prebid/prebid-universal-creative/blob/master/src/nativeAssetManager.js#L66?


Austinb: Tests should be updated as well to enforce the location of a leading/trailing slash (if any).  Will wait until a maintainer weighs in on what the expected behavior should be. 

Patch:
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 61f4f3e7..1f234b85 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -91,7 +91,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	}
 	requestJson, err := ioutil.ReadAll(lr)
 	if err != nil {
-		handleError(labels, w, []error{err}, ao)
+		handleError(&labels, w, []error{err}, &ao)
 		return
 	}
 
@@ -102,27 +102,27 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	if err != nil {
 		if deps.cfg.VideoStoredRequestRequired {
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	} else {
 		storedRequest, errs := deps.loadStoredVideoRequest(context.Background(), storedRequestId)
 		if len(errs) > 0 {
-			handleError(labels, w, errs, ao)
+			handleError(&labels, w, errs, &ao)
 			return
 		}
 
 		//merge incoming req with stored video req
 		resolvedRequest, err = jsonpatch.MergePatch(storedRequest, requestJson)
 		if err != nil {
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	}
 	//unmarshal and validate combined result
 	videoBidReq, errL, podErrors := deps.parseVideoRequest(resolvedRequest)
 	if len(errL) > 0 {
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -130,7 +130,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	if deps.defaultRequest {
 		if err := json.Unmarshal(deps.defReqJSON, bidReq); err != nil {
 			err = fmt.Errorf("Invalid JSON in Default Request Settings: %s", err)
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	}
@@ -154,7 +154,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		}
 		err := errors.New(fmt.Sprintf("all pods are incorrect: %s", strings.Join(resPodErr, "; ")))
 		errL = append(errL, err)
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -166,7 +166,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	errL = deps.validateRequest(bidReq)
 	if len(errL) > 0 {
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -194,7 +194,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil {
 		errL = append(errL, acctIdErr)
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 	//execute auction logic
@@ -203,7 +203,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	ao.Response = response
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -211,7 +211,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	bidResp, err := buildVideoResponse(response, podErrors)
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 	if bidReq.Test == 1 {
@@ -222,7 +222,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	//resp, err := json.Marshal(response)
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -238,7 +238,7 @@ func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []P
 	return videoReq
 }
 
-func handleError(labels pbsmetrics.Labels, w http.ResponseWriter, errL []error, ao analytics.AuctionObject) {
+func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, ao *analytics.AuctionObject) {
 	labels.RequestStatus = pbsmetrics.RequestStatusErr
 	var errors string
 	var status int = http.StatusInternalServerError
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 76a4d302..e92e813f 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -615,11 +615,11 @@ func (e *exchange) getBidCacheInfo(bid *pbsOrtbBid, auc *auction) (openrtb_ext.E
 		if cacheUUID, found = auc.cacheIds[bid.bid]; found {
 			cacheInfo.CacheId = cacheUUID
 			extCacheHost, extCachePath = e.cache.GetExtCacheData()
-			cacheInfo.Url = extCacheHost + "/" + extCachePath + "?uuid=" + cacheUUID
+			cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID
 		} else if cacheUUID, found = auc.vastCacheIds[bid.bid]; found {
 			cacheInfo.CacheId = cacheUUID
 			extCacheHost, extCachePath = e.cache.GetExtCacheData()
-			cacheInfo.Url = extCacheHost + "/" + extCachePath + "?uuid=" + cacheUUID
+			cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID
 		}
 	}
 	return cacheInfo, found
diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go
index 88da5c3a..6da69f68 100644
--- a/prebid_cache_client/client.go
+++ b/prebid_cache_client/client.go
@@ -72,9 +72,11 @@ type clientImpl struct {
 func (c *clientImpl) GetExtCacheData() (string, string) {
 	path := c.externalCachePath
 	if path == "/" {
+		// Only the slash for the path, remove it to empty
 		path = ""
-	} else if strings.Index(path, "/") == 0 {
-		path = strings.TrimLeft(path, "/")
+	} else if len(path) > 0 && !strings.HasPrefix(path, "/") {
+		// Path defined but does not start with "/", prepend it
+		path = "/" + path
 	}
 
 	return c.externalCacheHost, path


Test patch:
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 85285a30..6a8e913a 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -3,12 +3,14 @@ package openrtb2
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"io/ioutil"
 	"net/http/httptest"
 	"strings"
 	"testing"
 
 	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/analytics"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/exchange"
@@ -641,6 +643,91 @@ func TestMergeOpenRTBToVideoRequest(t *testing.T) {
 	assert.Equal(t, videoReq.Site.Page, bidReq.Site.Page, "Device.Site.Page is incorrect")
 }
 
+func TestHandleError(t *testing.T) {
+	ao := analytics.AuctionObject{
+		Status: 200,
+		Errors: make([]error, 0),
+	}
+
+	labels := pbsmetrics.Labels{
+		Source:        pbsmetrics.DemandUnknown,
+		RType:         pbsmetrics.ReqTypeVideo,
+		PubID:         pbsmetrics.PublisherUnknown,
+		Browser:       "test browser",
+		CookieFlag:    pbsmetrics.CookieFlagUnknown,
+		RequestStatus: pbsmetrics.RequestStatusOK,
+	}
+
+	recorder := httptest.NewRecorder()
+	err1 := errors.New("Error for testing handleError 1")
+	err2 := errors.New("Error for testing handleError 2")
+	handleError(&labels, recorder, []error{err1, err2}, &ao)
+
+	assert.Equal(t, pbsmetrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error")
+	assert.Equal(t, 500, recorder.Code, "Error status should be written to writer")
+	assert.Equal(t, 500, ao.Status, "AnalyticsObject should have error status")
+	assert.Equal(t, 2, len(ao.Errors), "New errors should be appended to AnalyticsObject Errors")
+	assert.Equal(t, "Error for testing handleError 1", ao.Errors[0].Error(), "Error in AnalyticsObject should have test error message for first error")
+	assert.Equal(t, "Error for testing handleError 2", ao.Errors[1].Error(), "Error in AnalyticsObject should have test error message for second error")
+}
+
+func TestHandleErrorMetrics(t *testing.T) {
+	ex := &mockExchangeVideo{}
+	reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json")
+	if err != nil {
+		t.Fatalf("Failed to fetch a valid request: %v", err)
+	}
+	reqBody := string(getRequestPayload(t, reqData))
+	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
+	recorder := httptest.NewRecorder()
+
+	deps, met, mod := mockDepsWithMetrics(t, ex)
+	deps.VideoAuctionEndpoint(recorder, req, nil)
+
+	assert.Equal(t, int64(0), met.RequestStatuses[pbsmetrics.ReqTypeVideo][pbsmetrics.RequestStatusOK].Count(), "OK requests count should be 0")
+	assert.Equal(t, int64(1), met.RequestStatuses[pbsmetrics.ReqTypeVideo][pbsmetrics.RequestStatusErr].Count(), "Error requests count should be 1")
+	assert.Equal(t, 1, len(mod.auctionObjects), "Mock AnalyticsModule should have 1 AuctionObject")
+	assert.Equal(t, 500, mod.auctionObjects[0].Status, "AnalyticsObject should have 500 status")
+	assert.Equal(t, 2, len(mod.auctionObjects[0].Errors), "AnalyticsObject should have Errors length of 2")
+	assert.Equal(t, "request missing required field: PodConfig.DurationRangeSec", mod.auctionObjects[0].Errors[0].Error(), "First error in AnalyticsObject should have message regarding DurationRangeSec")
+	assert.Equal(t, "request missing required field: PodConfig.Pods", mod.auctionObjects[0].Errors[1].Error(), "Second error in AnalyticsObject should have message regarding Pods")
+}
+
+func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) {
+	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
+	mockModule := &mockAnalyticsModule{}
+	edep := &endpointDeps{
+		ex,
+		newParamsValidator(t),
+		&mockVideoStoredReqFetcher{},
+		&mockVideoStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		theMetrics,
+		mockModule,
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BidderMap,
+	}
+
+	return edep, theMetrics, mockModule
+}
+
+type mockAnalyticsModule struct {
+	auctionObjects []*analytics.AuctionObject
+}
+
+func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) {
+	m.auctionObjects = append(m.auctionObjects, ao)
+}
+
+func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { return }
+
+func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) { return }
+
+func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { return }
+
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
 	edep := &endpointDeps{
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index f5bf23d5..2b0e37e6 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -1291,7 +1291,7 @@ func mockHandler(statusCode int, getBody string, postBody string) http.Handler {
 type wellBehavedCache struct{}
 
 func (c *wellBehavedCache) GetExtCacheData() (string, string) {
-	return "www.pbcserver.com", "pbcache/endpoint"
+	return "www.pbcserver.com", "/pbcache/endpoint"
 }
 
 func (c *wellBehavedCache) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) {
diff --git a/exchange/exchangetest/include-brand-category.json b/exchange/exchangetest/include-brand-category.json
index f27c0e6d..68e93e3a 100644
--- a/exchange/exchangetest/include-brand-category.json
+++ b/exchange/exchangetest/include-brand-category.json
@@ -106,8 +106,8 @@
                      "hb_bidder_appnexus": "appnexus",
                      "hb_cache_host": "www.pbcserver.com",
                      "hb_cache_host_appnex": "www.pbcserver.com",
-                     "hb_cache_path": "pbcache/endpoint",
-                     "hb_cache_path_appnex": "pbcache/endpoint",
+                     "hb_cache_path": "/pbcache/endpoint",
+                     "hb_cache_path_appnex": "/pbcache/endpoint",
                      "hb_pb": "0.20",
                      "hb_pb_appnexus": "0.20",
                      "hb_pb_cat_dur": "0.20_VideoGames_0s",
@@ -128,8 +128,8 @@
                       "hb_bidder_appnexus": "appnexus",
                       "hb_cache_host": "www.pbcserver.com",
                       "hb_cache_host_appnex": "www.pbcserver.com",
-                      "hb_cache_path": "pbcache/endpoint",
-                      "hb_cache_path_appnex": "pbcache/endpoint",
+                      "hb_cache_path": "/pbcache/endpoint",
+                      "hb_cache_path_appnex": "/pbcache/endpoint",
                       "hb_pb": "0.50",
                       "hb_pb_appnexus": "0.50",
                       "hb_pb_cat_dur": "0.50_HomeDecor_0s",
diff --git a/exchange/exchangetest/targeting-cache-vast-banner.json b/exchange/exchangetest/targeting-cache-vast-banner.json
index 5d0db274..215485cc 100644
--- a/exchange/exchangetest/targeting-cache-vast-banner.json
+++ b/exchange/exchangetest/targeting-cache-vast-banner.json
@@ -71,8 +71,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.00",
                   "hb_pb_appnexus": "0.00",
                   "hb_size": "200x250",
diff --git a/exchange/exchangetest/targeting-cache-vast.json b/exchange/exchangetest/targeting-cache-vast.json
index 743f2ce3..f348dd1b 100644
--- a/exchange/exchangetest/targeting-cache-vast.json
+++ b/exchange/exchangetest/targeting-cache-vast.json
@@ -78,8 +78,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_uuid": "0",
                   "hb_uuid_appnexus": "0",
                   "hb_pb": "0.00",
diff --git a/exchange/exchangetest/targeting-cache-zero.json b/exchange/exchangetest/targeting-cache-zero.json
index abe9df10..51301530 100644
--- a/exchange/exchangetest/targeting-cache-zero.json
+++ b/exchange/exchangetest/targeting-cache-zero.json
@@ -83,8 +83,8 @@
                   "hb_cache_host_appnex": "www.pbcserver.com",
                   "hb_cache_id": "0",
                   "hb_cache_id_appnexus": "0",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_uuid": "1",
                   "hb_uuid_appnexus": "1",
                   "hb_pb": "0.00",
diff --git a/exchange/exchangetest/targeting-mobile.json b/exchange/exchangetest/targeting-mobile.json
index d5661565..f3962cff 100644
--- a/exchange/exchangetest/targeting-mobile.json
+++ b/exchange/exchangetest/targeting-mobile.json
@@ -123,7 +123,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250",
                   "hb_env_audienceNetwo": "mobile-app"
@@ -148,8 +148,8 @@
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_bidder_appnexus": "appnexus",
                   "hb_pb": "0.70",
                   "hb_pb_appnexus": "0.70",
@@ -188,8 +188,8 @@
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_bidder_appnexus": "appnexus",
                   "hb_pb": "0.60",
                   "hb_pb_appnexus": "0.60",
diff --git a/exchange/exchangetest/targeting-no-winners.json b/exchange/exchangetest/targeting-no-winners.json
index 73bf812c..891ff0ff 100644
--- a/exchange/exchangetest/targeting-no-winners.json
+++ b/exchange/exchangetest/targeting-no-winners.json
@@ -125,7 +125,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250"
                 }
@@ -148,7 +148,7 @@
                 "targeting": {
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb_appnexus": "0.70",
                   "hb_size_appnexus": "200x250"
                 }
@@ -181,7 +181,7 @@
                 "targeting": {
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb_appnexus": "0.60",
                   "hb_size_appnexus": "300x500"
                 }
diff --git a/exchange/exchangetest/targeting-only-winners.json b/exchange/exchangetest/targeting-only-winners.json
index 807b7b6c..5f449af3 100644
--- a/exchange/exchangetest/targeting-only-winners.json
+++ b/exchange/exchangetest/targeting-only-winners.json
@@ -141,7 +141,7 @@
                 "targeting": {
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
                   "hb_pb": "0.70",
                   "hb_size": "200x250"
                 }
@@ -174,7 +174,7 @@
                 "targeting": {
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
                   "hb_pb": "0.60",
                   "hb_size": "300x500"
                 }
diff --git a/exchange/exchangetest/targeting-with-winners.json b/exchange/exchangetest/targeting-with-winners.json
index 208b3feb..f081b7ee 100644
--- a/exchange/exchangetest/targeting-with-winners.json
+++ b/exchange/exchangetest/targeting-with-winners.json
@@ -123,7 +123,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250"
                 }
@@ -148,8 +148,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.70",
                   "hb_pb_appnexus": "0.70",
                   "hb_size": "200x250",
@@ -186,8 +186,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.60",
                   "hb_pb_appnexus": "0.60",
                   "hb_size": "300x500",
diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go
index cbeaa38a..d3b5ee4b 100644
--- a/prebid_cache_client/client_test.go
+++ b/prebid_cache_client/client_test.go
@@ -156,10 +156,10 @@ func TestStripCacheHostAndPath(t *testing.T) {
 		{
 			inExtCacheURL: config.ExternalCache{
 				Host: "prebid-server.prebid.org",
-				Path: "pbcache/endpoint",
+				Path: "/pbcache/endpoint",
 			},
 			expectedHost: "prebid-server.prebid.org",
-			expectedPath: "pbcache/endpoint",
+			expectedPath: "/pbcache/endpoint",
 		},
 		{
 			inExtCacheURL: config.ExternalCache{
@@ -177,6 +177,22 @@ func TestStripCacheHostAndPath(t *testing.T) {
 			expectedHost: "",
 			expectedPath: "",
 		},
+		{
+			inExtCacheURL: config.ExternalCache{
+				Host: "prebid-server.prebid.org",
+				Path: "pbcache/endpoint",
+			},
+			expectedHost: "prebid-server.prebid.org",
+			expectedPath: "/pbcache/endpoint",
+		},
+		{
+			inExtCacheURL: config.ExternalCache{
+				Host: "prebidcache.net",
+				Path: "/",
+			},
+			expectedHost: "prebidcache.net",
+			expectedPath: "",
+		},
 	}
 	for _, test := range testInput {
 		//start client