diff --git a/http.c b/http.c
index 69da4454d8f754598d0316d0e1cb34870aba2b8e..a108658b7f67ba799eeee549c21d59fd9e419191 100644
--- a/http.c
+++ b/http.c
@@ -1373,17 +1373,19 @@ static void http_opt_request_remainder(CURL *curl, off_t pos)
 
 static int http_request(const char *url,
 			void *result, int target,
-			const struct http_get_options *options)
+			const struct http_request_options *options)
 {
 	struct active_request_slot *slot;
 	struct slot_results results;
 	struct curl_slist *headers = NULL;
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf content_type = STRBUF_INIT;
 	const char *accept_language;
 	int ret;
 
 	slot = get_active_slot();
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+	if (options && !options->postfields)
+		curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 
 	if (result == NULL) {
 		curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
@@ -1407,6 +1409,19 @@ static int http_request(const char *url,
 	if (accept_language)
 		headers = curl_slist_append(headers, accept_language);
 
+	if (options && options->postfields && options->post_content_type) {
+		strbuf_addstr(&content_type, "Content-Type: ");
+		strbuf_addbuf(&content_type, options->post_content_type);
+		headers = curl_slist_append(headers, content_type.buf);
+	}
+
+	if (options && options->postfields) {
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS,
+				 options->postfields->buf);
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE,
+				 options->postfields->len);
+	}
+
 	strbuf_addstr(&buf, "Pragma:");
 	if (options && options->no_cache)
 		strbuf_addstr(&buf, " no-cache");
@@ -1434,6 +1449,7 @@ static int http_request(const char *url,
 				options->effective_url);
 
 	curl_slist_free_all(headers);
+	strbuf_release(&content_type);
 	strbuf_release(&buf);
 
 	return ret;
@@ -1491,7 +1507,7 @@ static int update_url_from_redirect(struct strbuf *base,
 
 static int http_request_reauth(const char *url,
 			       void *result, int target,
-			       struct http_get_options *options)
+			       struct http_request_options *options)
 {
 	int ret = http_request(url, result, target, options);
 
@@ -1529,7 +1545,7 @@ static int http_request_reauth(const char *url,
 
 int http_get_strbuf(const char *url,
 		    struct strbuf *result,
-		    struct http_get_options *options)
+		    struct http_request_options *options)
 {
 	return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
 }
@@ -1541,7 +1557,7 @@ int http_get_strbuf(const char *url,
  * file is still around) the download is resumed.
  */
 static int http_get_file(const char *url, const char *filename,
-			 struct http_get_options *options)
+			 struct http_request_options *options)
 {
 	int ret;
 	struct strbuf tmpfile = STRBUF_INIT;
@@ -1567,7 +1583,7 @@ cleanup:
 
 int http_fetch_ref(const char *base, struct ref *ref)
 {
-	struct http_get_options options = {0};
+	struct http_request_options options = {0};
 	char *url;
 	struct strbuf buffer = STRBUF_INIT;
 	int ret = -1;
@@ -1659,7 +1675,7 @@ add_pack:
 
 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
 {
-	struct http_get_options options = {0};
+	struct http_request_options options = {0};
 	int ret = 0, i = 0;
 	char *url, *data;
 	struct strbuf buf = STRBUF_INIT;
diff --git a/http.h b/http.h
index 4ef4bbda7d86d8ca5238d45897b277709b30414a..0b7cebf90e84268e7989a6134f02d8239645fc7f 100644
--- a/http.h
+++ b/http.h
@@ -136,7 +136,7 @@ extern char *get_remote_object_url(const char *url, const char *hex,
 				   int only_two_digit_prefix);
 
 /* Options for http_get_*() */
-struct http_get_options {
+struct http_request_options {
 	unsigned no_cache:1,
 		 keep_error:1;
 
@@ -163,6 +163,18 @@ struct http_get_options {
 	 * for details.
 	 */
 	struct strbuf *base_url;
+
+	/*
+	 * If non-NULL, a HTTP POST request is sent with the raw post data
+	 * contained in postfields.
+	 */
+	struct strbuf *postfields;
+
+	/*
+	 * If non-NULL, a content-type header is applied to the HTTP POST
+	 * request.
+	 */
+	struct strbuf *post_content_type;
 };
 
 /* Return values for http_get_*() */
@@ -178,7 +190,8 @@ struct http_get_options {
  *
  * If the result pointer is NULL, a HTTP HEAD request is made instead of GET.
  */
-int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
+int http_get_strbuf(const char *url, struct strbuf *result,
+		    struct http_request_options *options);
 
 extern int http_fetch_ref(const char *base, struct ref *ref);
 
diff --git a/remote-curl.c b/remote-curl.c
index 15e48e25fb9fb8cd2e9e3e7a63cf08d2f9483ea2..345a5e6e47d152dc1b9b4425f5ac9714077e7f80 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -254,7 +254,7 @@ static struct discovery *discover_refs(const char *service, int for_push)
 	struct strbuf effective_url = STRBUF_INIT;
 	struct discovery *last = last_discovery;
 	int http_ret, maybe_smart = 0;
-	struct http_get_options options;
+	struct http_request_options options;
 
 	if (last && !strcmp(service, last->service))
 		return last;