diff --git a/http.c b/http.c
index 3d80bd6116e..2017e909054 100644
--- a/http.c
+++ b/http.c
@@ -1419,6 +1419,7 @@ struct active_request_slot *get_active_slot(void)
 		newslot->curl = NULL;
 		newslot->in_use = 0;
 		newslot->next = NULL;
+		newslot->errstr = NULL;
 
 		slot = active_queue_head;
 		if (!slot) {
diff --git a/http.h b/http.h
index 3af19a8bf53..cb542c62933 100644
--- a/http.h
+++ b/http.h
@@ -30,6 +30,7 @@ struct active_request_slot {
 	void *callback_data;
 	void (*callback_func)(void *data);
 	struct active_request_slot *next;
+	struct strbuf *errstr;
 };
 
 struct buffer {
diff --git a/remote-curl.c b/remote-curl.c
index 0b6d7815fdd..9b2a41b2451 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -804,8 +804,11 @@ static size_t rpc_in(char *ptr, size_t eltsize,
 	if (curl_easy_getinfo(data->slot->curl, CURLINFO_RESPONSE_CODE,
 			      &response_code) != CURLE_OK)
 		return size;
-	if (response_code >= 300)
+	if (response_code >= 300) {
+		strbuf_reset(data->slot->errstr);
+		strbuf_add(data->slot->errstr, ptr, size);
 		return size;
+	}
 	if (size)
 		data->rpc->any_written = 1;
 	if (data->check_pktline)
@@ -837,6 +840,8 @@ static int run_slot(struct active_request_slot *slot,
 				strbuf_addch(&msg, ' ');
 				strbuf_addstr(&msg, curl_errorstr);
 			}
+			if (slot->errstr && slot->errstr->len)
+				error(_("%s"), slot->errstr->buf);
 		}
 		error(_("RPC failed; %s"), msg.buf);
 		strbuf_release(&msg);
@@ -896,6 +901,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 	int err, large_request = 0;
 	int needs_100_continue = 0;
 	struct rpc_in_data rpc_in_data;
+	struct strbuf errstr = STRBUF_INIT;
 
 	/* Try to load the entire request, if we can fit it into the
 	 * allocated buffer space we can use HTTP/1.0 and avoid the
@@ -1030,6 +1036,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
+	slot->errstr = &errstr;
 	rpc_in_data.rpc = rpc;
 	rpc_in_data.slot = slot;
 	rpc_in_data.check_pktline = stateless_connect;
@@ -1040,6 +1047,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 
 	rpc->any_written = 0;
 	err = run_slot(slot, NULL);
+	slot->errstr = NULL;
 	if (err == HTTP_REAUTH && !large_request) {
 		credential_fill(&http_auth);
 		goto retry;
@@ -1060,6 +1068,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 
 	curl_slist_free_all(headers);
 	free(gzip_body);
+	strbuf_release(&errstr);
 	return err;
 }
 
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index d83bafeab32..8f5b6357176 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -164,6 +164,7 @@ prepare_httpd() {
 	install_script error-no-report.sh
 	install_script broken-smart-http.sh
 	install_script error-smart-http.sh
+	install_script wrap-git-http-backend.sh
 	install_script error.sh
 	install_script apply-one-time-perl.sh
 	install_script nph-custom-auth.sh
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 022276a6b9a..0ca58f54d72 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -108,6 +108,10 @@ SetEnv PERL_PATH ${PERL_PATH}
 	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
 	SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
+<LocationMatch /smart_fail_on_post_with_body/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+	SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
 <LocationMatch /smart_noexport/>
 	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
 </LocationMatch>
@@ -155,6 +159,7 @@ ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pa
 ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
 ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
 ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
+ScriptAliasMatch /smart_fail_on_post_with_body/(.*) wrap-git-http-backend.sh/$1
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 ScriptAlias /error_smart/ error-smart-http.sh/
@@ -182,6 +187,9 @@ ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
 <Files error.sh>
   Options ExecCGI
 </Files>
+<Files wrap-git-http-backend.sh>
+  Options ExecCGI
+</Files>
 <Files apply-one-time-perl.sh>
 	Options ExecCGI
 </Files>
diff --git a/t/lib-httpd/wrap-git-http-backend.sh b/t/lib-httpd/wrap-git-http-backend.sh
new file mode 100755
index 00000000000..d1fc540330e
--- /dev/null
+++ b/t/lib-httpd/wrap-git-http-backend.sh
@@ -0,0 +1,7 @@
+if [ "$REQUEST_METHOD" = "GET" ]; then
+	"$GIT_EXEC_PATH"/git-http-backend "$@"
+elif [ "$REQUEST_METHOD" = "POST" ]; then
+	printf "Status: 429 Too Many Requests\n"
+	echo
+	printf "Too many requests"
+fi
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index a623a1058cd..badf37b4d68 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -302,6 +302,11 @@ test_expect_success 'dumb clone via http-backend respects namespace' '
 	test_cmp expect actual
 '
 
+test_expect_success 'smart http should display an error message on POST request failure' '
+	test_must_fail git clone --single-branch $HTTPD_URL/smart_fail_on_post_with_body/repo.git 2>actual &&
+	grep -q "error: Too many requests" actual
+'
+
 test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
 	cat >cookies.txt <<-\EOF &&
 	127.0.0.1	FALSE	/smart_cookies/	FALSE	0	othername	othervalue
