diff --git a/Makefile b/Makefile
index c7be29f16924a35ee46c99c32b1c80e5b8989345..678352b5e06c1ad3e706d6b68a1042d84d35a20b 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ SRC_DIR = src
 OBJS = $(addprefix $(SRC_DIR)/, bulkdata.o config.o report.o buci.o utils.o )
 
 PROG_CFLAGS = $(CFLAGS) -Wall -Wextra -Werror -fstrict-aliasing -fPIC
-PROG_LDFLAGS = $(LDFLAGS) -lubus -luci -lubox -ljson-c -lcurl -lblobmsg_json
+PROG_LDFLAGS = $(LDFLAGS) -lubus -luci -lubox -ljson-c -lcurl -lblobmsg_json -lz
 
 .PHONY: all clean
 
diff --git a/bbf_plugin/datamodel.c b/bbf_plugin/datamodel.c
index ddcebd90198b4cbb4295e302bf473f69278c4629..cfb95a4b544ff9438acc8349166c8b1a5acf0597 100644
--- a/bbf_plugin/datamodel.c
+++ b/bbf_plugin/datamodel.c
@@ -24,6 +24,7 @@ DM_MAP_OBJ tDynamicObj[] = {
 {0}
 };
 
+char *compression_supported[] = {"None", "GZIP", NULL};
 /*************************************************************
 * ENTRY METHOD
 *************************************************************/
@@ -867,7 +868,7 @@ static int set_BulkDataProfileHTTP_Password(char *refparam, struct dmctx *ctx, v
 
 static int get_BulkDataProfileHTTP_CompressionsSupported(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	*value = "GZIP,Compress,Deflate";
+	*value = "GZIP";
 	return 0;
 }
 
@@ -877,10 +878,9 @@ static int get_BulkDataProfileHTTP_Compression(char *refparam, struct dmctx *ctx
 	dmuci_get_value_by_section_string(((struct dmmap_dup *)data)->config_section, "http_compression", value);
 	if(strcmp(*value, "gzip") == 0)
 		*value = "GZIP";
-	else if(strcmp(*value, "compress") == 0)
-		*value = "Compress";
-	else if(strcmp(*value, "deflate") == 0)
-		*value = "Deflate";
+	else
+		*value = "None";
+
 	return 0;
 }
 
@@ -888,16 +888,14 @@ static int set_BulkDataProfileHTTP_Compression(char *refparam, struct dmctx *ctx
 {
 	switch (action)	{
 		case VALUECHECK:
-			if (dm_validate_string(value, -1, -1, NULL, NULL))
+			if (dm_validate_string(value, -1, -1, compression_supported, NULL))
 				return FAULT_9007;
 			break;
 		case VALUESET:
 			if(strcmp(value, "GZIP") == 0)
 				dmuci_set_value_by_section(((struct dmmap_dup *)data)->config_section, "http_compression", "gzip");
-			else if(strcmp(value, "Compress") == 0)
-				dmuci_set_value_by_section(((struct dmmap_dup *)data)->config_section, "http_compression", "compress");
-			else if(strcmp(value, "Deflate") == 0)
-				dmuci_set_value_by_section(((struct dmmap_dup *)data)->config_section, "http_compression", "deflate");
+			else
+				dmuci_set_value_by_section(((struct dmmap_dup *)data)->config_section, "http_compression", "none");
 			break;
 	}
 	return 0;
diff --git a/src/utils.c b/src/utils.c
index 5859cb22af375e3f8a07bb940e3f50f3dc1f07b1..2e6466a45076018c5fec61c9146cf40953f26409 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -10,6 +10,7 @@
  */
 
 #include <curl/curl.h>
+#include <zlib.h>
 #include <libubus.h>
 #include <libubox/blob.h>
 #include <libubox/blobmsg.h>
@@ -21,6 +22,12 @@
 static unsigned char LogLevel = DEFAULT_LOG_LEVEL;
 static bool g_usp_object_available = false;
 
+enum compress_type {
+	COMPRESS_TYPE_GZIP,
+	COMPRESS_TYPE_COMPRESS,
+	COMPRESS_TYPE_DEFLATE
+};
+
 void set_log_level(unsigned char level)
 {
 	LogLevel = level;
@@ -365,6 +372,77 @@ static void create_request_url(struct profile *profile, char *url, size_t len)
 	}
 }
 
+static unsigned char *compress_report(char *input_buf, long input_len, long *content_len, int type)
+{
+	z_stream zlib_ctx;
+	int err;
+	int output_len;
+	unsigned char *output_buf;
+
+	if (content_len == NULL) {
+		return NULL;
+	}
+
+	// Exit if not GZIP compression - in this case we just don't compress the report
+	if (type != COMPRESS_TYPE_GZIP || input_buf == NULL) {
+		*content_len = input_len;
+		return (unsigned char *)input_buf;
+	}
+
+	// Initialise the zlib context
+	memset(&zlib_ctx, 0, sizeof(zlib_ctx));
+	zlib_ctx.zalloc = Z_NULL;
+	zlib_ctx.zfree = Z_NULL;
+	zlib_ctx.opaque = NULL;
+
+	// Exit if unable to start deflate
+	#define WINDOW_BITS  (15+16)  // Plus 16 to get a gzip wrapper, as suggested by the zlib documentation
+	#define MEM_LEVEL 8           // This is the default value, as suggested by the zlib documentation
+	err = deflateInit2(&zlib_ctx, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS, MEM_LEVEL, Z_DEFAULT_STRATEGY);
+	if (err != Z_OK) {
+		WARNING("deflateInit2 returned %d. Falling back to sending uncompressed data", err);
+		*content_len = input_len;
+		return (unsigned char *)input_buf;
+	}
+
+	// Allocate a worst case buffer to hold the compressed data
+	output_len = (int)deflateBound(&zlib_ctx, input_len);
+	output_buf = malloc(output_len);  // Use malloc because the uncompressed report was generated with malloc() and this needs to be consistent
+	if (output_buf == NULL) {
+		WARNING("malloc failed. Falling back to sending uncompressed data", err);
+		deflateEnd(&zlib_ctx);
+		*content_len = input_len;
+		return (unsigned char *)input_buf;
+	}
+
+	// Initialise the zlib context for this compression
+	zlib_ctx.next_in  = (unsigned char *)input_buf;
+	zlib_ctx.avail_in = input_len;
+	zlib_ctx.next_out = output_buf;
+	zlib_ctx.avail_out= output_len;
+
+	// Exit if compression failed
+	err = deflate(&zlib_ctx, Z_FINISH);
+	if (err != Z_STREAM_END) {
+		WARNING("deflate failed (err=%d). Falling back to sending uncompressed data", err);
+		deflateEnd(&zlib_ctx);
+		free(output_buf);
+		*content_len = input_len;
+		return (unsigned char *)input_buf;
+	}
+
+	// Deallocate all compression state stored in the zlib context
+	// NOTE: We ignore errors from this and just log them
+	err = deflateEnd(&zlib_ctx);
+	if (err != Z_OK) {
+		WARNING("deflateEnd failed (err=%d, %s). Ignoring error", err, zlib_ctx.msg);
+	}
+
+	INFO("BulkDataReport(uncompressed size=%d, compressed size=%lu)", input_len, zlib_ctx.total_out);
+	*content_len = zlib_ctx.total_out;
+	return output_buf;
+}
+
 int http_send_message(struct profile *profile, char *msg_out)
 {
 #define HTTP_TIMEOUT 20
@@ -427,23 +505,33 @@ int http_send_message(struct profile *profile, char *msg_out)
 	curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
 	curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
 
+	unsigned char *content = NULL;
+	long content_len = 0;
+	long msg_out_len = msg_out ? strlen(msg_out) : 0;
 	if (strcasecmp(profile->http_compression, "gzip") == 0) {
 		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
 		header_list = curl_slist_append(header_list, "Content-Encoding: gzip");
+		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_GZIP);
 	} else if (strcasecmp(profile->http_compression, "compress") == 0) {
 		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "compress");
 		header_list = curl_slist_append(header_list, "Content-Encoding: compress");
+		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_COMPRESS);
 	} else if (strcasecmp(profile->http_compression, "deflate") == 0) {
 		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
-		header_list = curl_slist_append(header_list, "Content-Encoding: deflate");	
+		header_list = curl_slist_append(header_list, "Content-Encoding: deflate");
+		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_DEFLATE);
+	} else {
+		/* no compression */
+		content = (unsigned char *)msg_out;
+		content_len = msg_out ? msg_out_len : 0;
 	}
 
 	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
 	if (strcasecmp(profile->http_method, "put") == 0)
 		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
 
-	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg_out);
-	curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, msg_out ? (long)strlen(msg_out) : 0);
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content);
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, content_len);
 
 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
 
@@ -460,6 +548,9 @@ int http_send_message(struct profile *profile, char *msg_out)
 		}
 	}
 
+	if (content != (unsigned char *)msg_out)
+		FREE(content);
+
 	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
 	if (http_code == 200)
 		INFO("Receive HTTP 200 OK from Bulkdata Collector");