diff --git a/.gitignore b/.gitignore
index f7a731d605..ba6f7e7238 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,7 +37,7 @@ Makefile
 Makefile.in
 aclocal.m4
 alp2/Makefile.in
-apache2/.dirstamp                                  
+apache2/.dirstamp
 apache2/Makefile
 apache2/Makefile.in
 apache2/ag_mdb/.deps/
@@ -51,7 +51,7 @@ apache2/waf_lock/.deps/
 apache2/waf_lock/.dirstamp
 apache2/waf_logging/.deps/
 apache2/waf_logging/.dirstamp
-apache2/waf_logging/waf_format.pb.h
+apache2/waf_logging/waf_format.pb.h
 
 apache2/waf_logging/waf_format.pb.cc
 autom4te.cache/
@@ -69,10 +69,11 @@ build/ltversion.m4
 build/lt~obsolete.m4
 build/missing
 build/test-driver
+build/compile
 config.status
 configure
-doc/Makefile.in                                
-ext/Makefile.in                                    
+doc/Makefile.in
+ext/Makefile.in
 libtool
 mlogc/Makefile.in
 standalone/.libs/
@@ -90,4 +91,5 @@ tests/run-regression-tests.pl
 tests/run-unit-tests.pl
 tools/Makefile
 tools/Makefile.in
-tools/rules-updater.pl
\ No newline at end of file
+tools/rules-updater.pl
+nginx/modsecurity/config
diff --git a/BUILD.md b/BUILD.md
new file mode 100644
index 0000000000..edd84cfdd5
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,98 @@
+# Building ModSecurity
+
+## Branches
+Two branches currently used for ModSecurity development and sustaining are `v2/master` and `iis_maintenance`. 
+All new fixes go to `v2/master` and so pull requests should be opened for this branch. 
+General changes and changes that are specific to ModSecurity for IIS are merged from `v2/master` to `iis_maintenance`. Fixes specific to ModSecurity for Nginx are not propagated from `v2/master`.
+
+## Building ModSecurity For IIS
+
+Open a `PowerShell` command line to follow the steps below.
+
+ 1. Clone ModSecurity repository along with all registered submodules:
+    ```bash
+    git clone --recursive https://github.com/Microsoft/ModSecurity
+    ```
+ 2. Switch to `iis_maintenance` branch:
+     ```bash
+    git checkout --recurse-submodules iis_maintenance
+    ```
+ 3.  Change to the `iis` subdirectory:
+     ```bash
+     cd iis
+     ```
+ 4. Download dependencies using helper PowerShell script:
+     ```bash
+     .\getModSecurityPkgs.ps1 https://modsecurity.blob.core.windows.net/windows-artifacts
+     ```
+ 5. Open Visual Studio 2015 x64 Native Tools Command Prompt, change to `iis` subdirectory and start the build:
+     ```bash
+     cd iis
+     .\build_release_amd64.bat
+     ```
+ 
+ This will produce the binaries and place them into `ModSecurity\iis\release\amd64` directory.
+
+If you only make changes to ModSecurity and not to any of its dependencies, you can later only rebuild it with `build_modsecurity.bat`. You still have to use `build_release_amd64` for the first time to get all the dependencies built.
+
+## Building ModSecurity For Nginx
+The steps below are for Ubuntu 16.04 LTS.
+
+ 1. Install required dependencies:
+	 ```
+	 apt-get update && apt-get install -y  \
+    vim \
+    curl \
+    apt-transport-https \
+    build-essential \
+    wget \
+    automake\
+    unzip\
+    m4 \
+    git \
+    libtool \
+    cmake \
+    apache2-dev \
+    g++-4.8 \
+    libaprutil1 \
+    libazurepal-dev \
+    libazurepal \
+    libcurl4-openssl-dev \
+    libexpat-dev\
+    libgd2-xpm-dev \
+    liblua5.1-dev \
+    libluajit-5.1-dev \
+    libpcre3-dev \
+    libperl-dev \
+    libunwind8 \
+    libyajl-dev \
+    libxml2\
+    libxml2-dev\
+    metricsext \
+    unzip \
+    uuid-dev \
+    openssh-server \
+    unzip \
+    uuid-runtime \
+    gdb
+    # Install jsoncons 0.122
+    mkdir -p /tmp/jsoncons/
+    curl -o /tmp/jsoncons/jsoncons-0.122.0.zip -OL https://github.com/danielaparker/jsoncons/archive/v0.122.0.zip
+    unzip /tmp/jsoncons/jsoncons-0.122.0.zip -d /tmp/jsoncons
+    cp -r /tmp/jsoncons/jsoncons-0.122.0/include/jsoncons /usr/include/
+	```
+
+ 3. Clone ModSecurity repository along with all registered submodules:
+    ```bash
+    git clone --recursive https://github.com/Microsoft/ModSecurity
+    ```
+ 4. Make sure you're in `v2/master` branch:
+     ```bash
+    git status
+    ```
+ 5.  Configure and build ModSecurity:
+	 ```bash
+	 ./autogen.sh
+	 ./configure LDFLAGS=-g  CXXFLAGS="-std=c++14 " CFLAGS="-fPIC " --enable-standalone-module --disable-mlogc --enable-waf_json_logging
+	 make
+	 ```
diff --git a/apache2/Makefile.am b/apache2/Makefile.am
index 9c3de4c515..cdedd86e46 100644
--- a/apache2/Makefile.am
+++ b/apache2/Makefile.am
@@ -1,17 +1,9 @@
 pkglibdir = $(prefix)/lib
 pkglib_LTLIBRARIES = mod_security2.la
 
-PROTOS         = $(waf_logging/waf_format.proto)
-PROTO_OBJS     = $(PROTOS:.proto=.pb.o)
-
-PBSRCS   = $(waf_logging/waf_format.proto)
-PBOBJS   = $(PROTOS:.proto=.pb.o)
-PBGENS   = $(PROTOS:.proto=.pb.cc) $(PROTOS:.proto=.pb.h)
-
 mod_security2_la_SOURCES = acmp.c \
     ag_mdb/murmur3.c \
     ag_mdb/ag_mdb.cpp \
-    waf_logging/waf_format.pb.cc \
     waf_logging/waf_log_util.cc \
     waf_lock/waf_lock.cpp \
     apache2_config.c \
@@ -203,8 +195,3 @@ install-exec-hook: $(pkglib_LTLIBRARIES)
 	done
 endif
 
-%.pb.cc: %.proto
-	protoc --cpp_out=. $<
-
-%.pb.o : %.pb.cc
-	$(CXX) $(CXX_FLAGS) -c -o $@ $<
\ No newline at end of file
diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c
index c93f811e93..28323d91ba 100755
--- a/apache2/apache2_config.c
+++ b/apache2/apache2_config.c
@@ -29,7 +29,7 @@
 #include "msc_lua.h"
 #endif
 #ifdef WAF_JSON_LOGGING_ENABLE
-#include "waf_log_util_external.h"
+#include "waf_log_util.h"
 #endif
 
 
@@ -114,9 +114,6 @@ void *create_directory_config(apr_pool_t *mp, char *path)
 
     /* Misc */
     dcfg->data_dir = NOT_SET_P;
-#ifdef WAF_JSON_LOGGING_ENABLE
-    dcfg->wafjsonlog_fd = NOT_SET_P;
-#endif
     dcfg->webappid = NOT_SET_P;
     dcfg->sensor_id = NOT_SET_P;
     dcfg->httpBlkey = NOT_SET_P;
@@ -128,6 +125,11 @@ void *create_directory_config(apr_pool_t *mp, char *path)
     dcfg->stream_inbody_inspection = NOT_SET;
     dcfg->stream_outbody_inspection = NOT_SET;
 
+    /* WAF policy identification information */
+    dcfg->waf_policy_id = NOT_SET_P;
+    dcfg->waf_policy_scope = NOT_SET_P;
+    dcfg->waf_policy_scope_name = NOT_SET_P;
+
     /* Geo Lookups */
     dcfg->geo = NOT_SET_P;
 
@@ -554,10 +556,6 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child)
     /* Misc */
     merged->data_dir = (child->data_dir == NOT_SET_P
         ? parent->data_dir : child->data_dir);
-#ifdef WAF_JSON_LOGGING_ENABLE
-    merged->wafjsonlog_fd = (child->wafjsonlog_fd == NOT_SET_P
-        ? parent->wafjsonlog_fd : child->wafjsonlog_fd);
-#endif
     merged->webappid = (child->webappid == NOT_SET_P
         ? parent->webappid : child->webappid);
     merged->sensor_id = (child->sensor_id == NOT_SET_P
@@ -575,6 +573,14 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child)
     merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET
         ? parent->stream_outbody_inspection : child->stream_outbody_inspection);
 
+    /* WAF policy identification information */
+    merged->waf_policy_id = (child->waf_policy_id == NOT_SET_P
+        ? parent->waf_policy_id : child->waf_policy_id);
+    merged->waf_policy_scope = (child->waf_policy_scope == NOT_SET_P
+        ? parent->waf_policy_scope : child->waf_policy_scope);
+    merged->waf_policy_scope_name = (child->waf_policy_scope_name == NOT_SET_P
+        ? parent->waf_policy_scope_name : child->waf_policy_scope_name);
+
     /* Geo Lookup */
     merged->geo = (child->geo == NOT_SET_P
         ? parent->geo : child->geo);
@@ -726,9 +732,6 @@ void init_directory_config(directory_config *dcfg)
 
     /* Misc */
     if (dcfg->data_dir == NOT_SET_P) dcfg->data_dir = NULL;
-#ifdef WAF_JSON_LOGGING_ENABLE
-    if (dcfg->wafjsonlog_fd == NOT_SET_P) dcfg->wafjsonlog_fd = NULL;
-#endif
     if (dcfg->webappid == NOT_SET_P) dcfg->webappid = "default";
     if (dcfg->sensor_id == NOT_SET_P) dcfg->sensor_id = "default";
     if (dcfg->httpBlkey == NOT_SET_P) dcfg->httpBlkey = NULL;
@@ -740,6 +743,11 @@ void init_directory_config(directory_config *dcfg)
     if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0;
     if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0;
 
+    /* WAF policy identification information */
+    if (dcfg->waf_policy_id == NOT_SET_P) dcfg->waf_policy_id = "";
+    if (dcfg->waf_policy_scope == NOT_SET_P) dcfg->waf_policy_scope = "";
+    if (dcfg->waf_policy_scope_name == NOT_SET_P) dcfg->waf_policy_scope_name = "";
+
     /* Geo Lookup */
     if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL;
 
@@ -1544,7 +1552,7 @@ static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
 {
 #ifdef WAF_JSON_LOGGING_ENABLE
     int rc;
-    char wafjsonlog_path[1024]; 
+    char wafjsonlog_path[WAF_LOG_PATH_LENGTH]; 
 #endif
     directory_config *dcfg = (directory_config *)_dcfg;
 
@@ -1557,7 +1565,7 @@ static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
 #ifdef WAF_JSON_LOGGING_ENABLE
     strcpy( wafjsonlog_path, dcfg->data_dir );
     strcat( wafjsonlog_path, WAF_LOG_UTIL_FILE );
-    rc = apr_file_open(&dcfg->wafjsonlog_fd, wafjsonlog_path,
+    rc = apr_file_open(&msc_waf_log_fd, wafjsonlog_path,
                    APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
                    CREATEMODE | APR_WREAD, cmd->pool);
 
@@ -1565,10 +1573,15 @@ static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
         return apr_psprintf(cmd->pool, "ModSecurity: Failed to open wafjson log file: %s",
             wafjsonlog_path);
     }
+    
+    // Global variable to share between threads
+    strncpy( msc_waf_log_path, wafjsonlog_path, WAF_LOG_PATH_LENGTH );
+    msc_waf_log_cmd = cmd;
 #endif
     return NULL;
 }
 
+
 static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1)
 {
     directory_config *dcfg = (directory_config *)_dcfg;
@@ -1793,6 +1806,79 @@ static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, in
     dcfg->stream_outbody_inspection = flag;
     return NULL;
 }
+
+/**
+* \brief Add SecWafPolicyId configuration option
+*
+* \param cmd Pointer to configuration data
+* \param _dcfg Pointer to directory configuration
+* \param p1 Pointer to configuration option
+*
+* \retval NULL On Success
+* \retval apr_psprintf On Failue
+*/
+static const char *cmd_waf_policy_id(cmd_parms *cmd, void *_dcfg, const char *p1)
+{
+    directory_config *dcfg = (directory_config *)_dcfg;
+    if (dcfg == NULL) {
+        return NULL;
+    }
+    if (p1 == NULL) {
+        return apr_psprintf(cmd->pool, "ModSecurity: No valid SecWafPolicyId specified.");
+    }
+
+    dcfg->waf_policy_id = p1;
+    return NULL;
+}
+
+/**
+* \brief Add SecWafPolicyScope configuration option
+*
+* \param cmd Pointer to configuration data
+* \param _dcfg Pointer to directory configuration
+* \param p1 Pointer to configuration option
+*
+* \retval NULL On Success
+* \retval apr_psprintf On Failue
+*/
+static const char *cmd_waf_policy_scope(cmd_parms *cmd, void *_dcfg, const char *p1)
+{
+    directory_config *dcfg = (directory_config *)_dcfg;
+    if (dcfg == NULL) {
+        return NULL;
+    }
+    if (p1 == NULL) {
+        return apr_psprintf(cmd->pool, "ModSecurity: No valid SecWafPolicyScope specified.");
+    }
+
+    dcfg->waf_policy_scope = p1;
+    return NULL;
+}
+
+/**
+* \brief Add SecWafPolicyScopeName configuration option
+*
+* \param cmd Pointer to configuration data
+* \param _dcfg Pointer to directory configuration
+* \param p1 Pointer to configuration option
+*
+* \retval NULL On Success
+* \retval apr_psprintf On Failue
+*/
+static const char *cmd_waf_policy_scope_name(cmd_parms *cmd, void *_dcfg, const char *p1)
+{
+    directory_config *dcfg = (directory_config *)_dcfg;
+    if (dcfg == NULL) {
+        return NULL;
+    }
+    if (p1 == NULL) {
+        return apr_psprintf(cmd->pool, "ModSecurity: No valid SecWafPolicyScopeName specified.");
+    }
+
+    dcfg->waf_policy_scope_name = p1;
+    return NULL;
+}
+
 /**
 * \brief Add SecRulePerfTime configuration option
 *
@@ -3453,6 +3539,30 @@ const command_rec module_directives[] = {
         "On or Off"
     ),
 
+    AP_INIT_TAKE1 (
+        "SecWafPolicyId",
+        cmd_waf_policy_id,
+        NULL,
+        CMD_SCOPE_ANY,
+        "WAF policy identifier"
+    ),
+
+    AP_INIT_TAKE1 (
+        "SecWafPolicyScope",
+        cmd_waf_policy_scope,
+        NULL,
+        CMD_SCOPE_ANY,
+        "WAF policy application scope"
+    ),
+
+    AP_INIT_TAKE1 (
+        "SecWafPolicyScopeName",
+        cmd_waf_policy_scope_name,
+        NULL,
+        CMD_SCOPE_ANY,
+        "name of the scope the WAF policy is assigned to"
+    ),
+
     AP_INIT_FLAG (
         "SecStreamInBodyInspection",
         cmd_stream_inbody_inspection,
diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c
index b91d2855d1..f462b3379c 100755
--- a/apache2/apache2_util.c
+++ b/apache2/apache2_util.c
@@ -18,7 +18,7 @@
 #include "util_script.h"
 
 #ifdef WAF_JSON_LOGGING_ENABLE
-#include "waf_log_util_external.h"
+#include "waf_log_util.h"
 #include "waf_lock_external.h"
 #include "string.h"
 #endif
@@ -318,9 +318,7 @@ static int write_file_with_lock(struct waf_lock* lock, apr_file_t* fd, char* str
 /**
  * send all waf fields in json format to a file.
  */
-static void send_waf_log(struct waf_lock* lock, apr_file_t* fd, const char* str1, const char* ip_port, const char* uri, int mode, const char* hostname, char* unique_id, request_rec *r) {
-    int rc = 0;
-    char* json_str;
+static void send_waf_log(struct waf_lock* lock, apr_file_t* fd, const char* str1, const char* ip_port, const char* uri, int mode, const char* hostname, char* unique_id, request_rec *r, const char* waf_policy_id, const char* waf_policy_scope, const char* waf_policy_scope_name) {
     char waf_filename[1024] = "";
     char waf_line[1024] = "";
     char waf_id[1024] = "";
@@ -346,8 +344,8 @@ static void send_waf_log(struct waf_lock* lock, apr_file_t* fd, const char* str1
     get_short_filename(waf_filename);
     get_ruleset_type_version(waf_ruleset_info, waf_ruleset_type, waf_ruleset_version); 
 
-    rc = generate_json(&json_str, msc_waf_resourceId, WAF_LOG_UTIL_OPERATION_NAME, WAF_LOG_UTIL_CATEGORY, msc_waf_instanceId, waf_ip, waf_port, uri, waf_ruleset_type, waf_ruleset_version, waf_id, waf_message, mode, 0, waf_detail_message, waf_data, waf_filename, waf_line, hostname, waf_unique_id);
-    if (rc == WAF_LOG_UTIL_FAILED) {
+    char* json_str = generate_json(msc_waf_resourceId, WAF_LOG_UTIL_OPERATION_NAME, WAF_LOG_UTIL_CATEGORY, msc_waf_instanceId, waf_ip, waf_port, uri, waf_ruleset_type, waf_ruleset_version, waf_id, waf_message, mode, 0, waf_detail_message, waf_data, waf_filename, waf_line, hostname, waf_unique_id, waf_policy_id, waf_policy_scope, waf_policy_scope_name);
+    if (!json_str) {
 #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
        ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
             "ModSecurity: can't print json log");
@@ -358,7 +356,7 @@ static void send_waf_log(struct waf_lock* lock, apr_file_t* fd, const char* str1
         return;
     }
 
-    rc = write_file_with_lock(lock, fd, json_str);
+    int rc = write_file_with_lock(lock, fd, json_str);
     if (rc == WAF_LOG_UTIL_FAILED) {
 #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
        ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
@@ -390,6 +388,7 @@ static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *
     char *str = NULL;
     char str1[1024] = "";
     char str2[1256] = "";
+    int rc = 0;
 
     /* Find the logging FD and determine the logging level from configuration. */
     if (dcfg != NULL) {
@@ -461,7 +460,25 @@ static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *
         else requestheaderhostname = "";
 
 #ifdef WAF_JSON_LOGGING_ENABLE
-        send_waf_log(msr->modsecurity->wafjsonlog_lock, dcfg->wafjsonlog_fd, str1, r->useragent_ip ? r->useragent_ip : r->connection->client_ip, log_escape(msr->mp, r->uri), dcfg->is_enabled, (char*)msr->hostname, unique_id, r);
+        if (msc_waf_log_reopened) {
+            rc = apr_file_open(&msc_waf_log_fd, msc_waf_log_path,
+                   APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
+                   CREATEMODE | APR_WREAD, msc_waf_log_cmd->pool);
+
+            if (rc != APR_SUCCESS) {
+                #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
+                	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
+                            "ModSecurity not able to reopen %s file", WAF_LOG_UTIL_FILE);
+                #else
+                        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
+                            "ModSecurity not able to reopen %s file", WAF_LOG_UTIL_FILE);
+                #endif
+            }
+
+            msc_waf_log_reopened = 0;
+        }
+
+        send_waf_log(msr->modsecurity->wafjsonlog_lock, msc_waf_log_fd, str1, r->useragent_ip ? r->useragent_ip : r->connection->client_ip, log_escape(msr->mp, r->uri), (!msr->allow_scope) ? dcfg->is_enabled : msr->allow_scope, r->hostname, unique_id, r, dcfg->waf_policy_id, dcfg->waf_policy_scope, dcfg->waf_policy_scope_name);
 #endif
 
 #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c
index d11093acc0..60a4b9eaa1 100755
--- a/apache2/mod_security2.c
+++ b/apache2/mod_security2.c
@@ -95,6 +95,10 @@ TreeRoot DSOLOCAL *conn_write_state_suspicious_list = 0;
 #ifdef WAF_JSON_LOGGING_ENABLE
 char DSOLOCAL *msc_waf_resourceId = NULL;
 char DSOLOCAL *msc_waf_instanceId = NULL;
+sig_atomic_t DSOLOCAL msc_waf_log_reopened = 1;
+apr_file_t DSOLOCAL *msc_waf_log_fd = NULL;
+char DSOLOCAL msc_waf_log_path[WAF_LOG_PATH_LENGTH] = ""; 
+cmd_parms DSOLOCAL *msc_waf_log_cmd = NULL;
 #endif
 
 #ifndef _WIN32
diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c
index 0faa5dff79..73660fc5ac 100755
--- a/apache2/modsecurity.c
+++ b/apache2/modsecurity.c
@@ -240,12 +240,23 @@ int modsecurity_init(msc_engine *msce, apr_pool_t *mp) {
     return 1;
 }
 
+#ifdef WAF_JSON_LOGGING_ENABLE
+void modsecurity_handle_signals_for_reopen(int signum)
+{
+    msc_waf_log_reopened = 1;
+}
+#endif
+
 /**
  * Performs per-child (new process) initialisation.
  */
 void modsecurity_child_init(msc_engine *msce) {
     struct waf_lock_args *lock_args;
     char *lock_name;
+#ifdef WAF_JSON_LOGGING_ENABLE
+    struct sigaction psa;
+    sigset_t block_mask;
+#endif
 
     /* Need to call this once per process before any other XML calls. */
     xmlInitParser();
@@ -266,7 +277,13 @@ void modsecurity_child_init(msc_engine *msce) {
 
     set_lock_args(lock_args, WAFJSONLOG_LOCK_ID);
 
-    waf_create_lock(msce->wafjsonlog_lock, lock_args);    
+    waf_create_lock(msce->wafjsonlog_lock, lock_args); 
+
+    sigfillset (&block_mask);
+    psa.sa_handler = modsecurity_handle_signals_for_reopen;
+    psa.sa_mask = block_mask;
+    psa.sa_flags = 0;
+    sigaction(SIGUSR1, &psa, NULL);     
 #endif
 
     if (msce->geo_lock == NULL) {
diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h
index 2ec5a77e4b..83c213f430 100755
--- a/apache2/modsecurity.h
+++ b/apache2/modsecurity.h
@@ -115,6 +115,10 @@ typedef struct msc_parm msc_parm;
 #define SECMARKER_ARGS                          "@noMatch"
 #define SECMARKER_BASE_ACTIONS                  "t:none,pass,marker:"
 
+#ifdef WAF_JSON_LOGGING_ENABLE
+#define WAF_LOG_PATH_LENGTH                     1024
+#endif
+
 #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
 #include "unixd.h"
 #define __SET_MUTEX_PERMS
@@ -171,6 +175,10 @@ extern DSOLOCAL int *unicode_map_table;
 #ifdef WAF_JSON_LOGGING_ENABLE
 extern DSOLOCAL char *msc_waf_resourceId;
 extern DSOLOCAL char *msc_waf_instanceId;
+extern DSOLOCAL sig_atomic_t msc_waf_log_reopened;
+extern DSOLOCAL apr_file_t *msc_waf_log_fd;
+extern DSOLOCAL char msc_waf_log_path[WAF_LOG_PATH_LENGTH];
+extern DSOLOCAL cmd_parms *msc_waf_log_cmd;
 #endif
 
 #ifndef _WIN32
@@ -596,9 +604,6 @@ struct directory_config {
 
     /* Misc */
     const char          *data_dir;
-#ifdef WAF_JSON_LOGGING_ENABLE
-    apr_file_t          *wafjsonlog_fd;
-#endif
     const char          *webappid;
     const char          *sensor_id;
     const char          *httpBlkey;
@@ -610,6 +615,11 @@ struct directory_config {
     int                 stream_inbody_inspection;
     int                 stream_outbody_inspection;
 
+    /* WAF policy identification information */
+    const char          *waf_policy_id;
+    const char          *waf_policy_scope;
+    const char          *waf_policy_scope_name;
+
     /* Geo Lookup */
     geo_db              *geo;
 
diff --git a/apache2/re_operators.c b/apache2/re_operators.c
old mode 100644
new mode 100755
index b6d1c03c94..f14f396cd1
--- a/apache2/re_operators.c
+++ b/apache2/re_operators.c
@@ -865,13 +865,8 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v
         char *hmac = NULL, *valid = NULL;
         char *hash_link = NULL, *nlink = NULL;
 
-        if (strlen(pattern) > 252) {
-            *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s.",
+        *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %.252s.",
                     pattern, var->name);
-        } else {
-            *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s.",
-                    pattern, var->name);
-        }
 
         valid = strstr(target, msr->txcfg->crypto_param_name);
 
@@ -880,13 +875,8 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v
             if (msr->txcfg->debuglog_level >= 9)
                 msr_log(msr, 9, "Request URI without hash parameter [%s]", target);
 
-            if (strlen(pattern) > 252) {
-                *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. No Hash parameter",
+            *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %.252s. No Hash parameter",
                         pattern, var->name);
-            } else {
-                *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. No Hash parameter",
-                        pattern, var->name);
-            }
             return 1;
         }   else    {
 
@@ -902,14 +892,8 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v
             hash_link = do_hash_link(msr, (char *)nlink, HASH_ONLY);
 
             if(strcmp(hmac, hash_link) != 0)    {
-
-                if (strlen(pattern) > 252) {
-                    *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]",
-                            pattern, var->name, hmac, hash_link);
-                } else {
-                    *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]",
+                *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %.252s. Hash parameter hash value = [%s] Requested URI hash value = [%s]",
                             pattern, var->name, hmac, hash_link);
-                }
                 return 1;
             }
         }
@@ -1178,16 +1162,14 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
 
     if (rc != PCRE_ERROR_NOMATCH) { /* Match. */
         /* We no longer escape the pattern here as it is done when logging */
+#ifdef WAF_JSON_LOGGING_ENABLE
+        char *pattern = apr_pstrdup(msr->mp, regex->pattern ? regex->pattern : "<Unknown Match>");
+#else
         char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "<Unknown Match>"));
-
-        /* This message will be logged. */
-        if (strlen(pattern) > 252) {
-            *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.",
-                    pattern, var->name);
-        } else {
-            *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.",
-                    pattern, var->name);
-        }
+#endif
+        /* This message will be logged. only the first 252 from the Pattern & the match */
+		*error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s\" at %.252s ....",
+			pattern, var->name);
 
         return 1;
     }
@@ -1425,13 +1407,8 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
         char *match_escaped = log_escape(msr->mp, match ? match : "<Unknown Match>");
 
         /* This message will be logged. */
-        if (strlen(match_escaped) > 252) {
-            *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.",
+        *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %.252s.",
                     match_escaped, var->name);
-        } else {
-            *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.",
-                    match_escaped, var->name);
-        }
 
         /* Handle capture as tx.0=match */
         if (capture) {
diff --git a/apache2/waf_logging/waf_format.proto b/apache2/waf_logging/waf_format.proto
deleted file mode 100755
index bb01e531b0..0000000000
--- a/apache2/waf_logging/waf_format.proto
+++ /dev/null
@@ -1,38 +0,0 @@
-syntax = "proto2";
-
-package waf_format;
-
-message Details {
-  optional string message = 1;
-  optional string data = 2;
-  optional string file = 3;
-  optional string line = 4;
-}
-
-
-message Properties {
-  optional string instanceId = 1;
-  optional string clientIp = 2;
-  optional string clientPort = 3;
-  optional string requestUri = 4;
-  optional string ruleSetType = 5;
-  optional string ruleSetVersion = 6;
-  optional string ruleId = 7;
-  optional string message = 8;
-  optional string action = 9;
-
-  enum SiteType {
-    Global = 0;
-  }
-  optional SiteType site = 10;
-  optional Details details = 11;
-  optional string hostname = 12;
-  optional string transactionId = 13;
-}
-
-message Waf_Format {
-  optional string resourceId = 1;
-  optional string operationName = 2;
-  optional string category = 3;
-  optional Properties properties = 4;
-}
diff --git a/apache2/waf_logging/waf_log_util.cc b/apache2/waf_logging/waf_log_util.cc
index c0a78246d5..d3d8bb6eb7 100755
--- a/apache2/waf_logging/waf_log_util.cc
+++ b/apache2/waf_logging/waf_log_util.cc
@@ -1,188 +1,283 @@
-#include "waf_log_util_internal.h"
-#include "waf_log_util_external.h"
+#include "waf_log_util.h"
 
-using namespace std;
-unordered_map<int, bool> appgw_ruleid_hash;
+#include <cstdlib>
+#include <fstream>
+#include <vector>
+#include <unordered_map>
+#include <experimental/optional>
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_options.hpp>
 
-// This function fills in a waf format message based on modsec input.
-void set_waf_format(waf_format::Waf_Format* waf_format, char* resourceId, char* operationName, char* category, char* instanceId, char* clientIP, char* clientPort, const char* requestUri, char* ruleSetType, char* ruleSetVersion, char* ruleId, char* messages, int action, int site, char* details_messages, char* details_data, char* details_file, char* details_line, const char* hostname, char* waf_unique_id) {
-    waf_format::Properties *properties;
-    waf_format::Details *details;
+namespace waf_logging {
 
-    properties = waf_format->mutable_properties();
-    details = properties->mutable_details();
+struct detail
+{
+    jsoncons::string_view message;
+    jsoncons::string_view data;
+    jsoncons::string_view file;
+    jsoncons::string_view line;
+};
 
-    if (resourceId != NULL) {
-        waf_format->set_resourceid(resourceId);
-    }
+struct property
+{
+    jsoncons::string_view instanceId;
+    jsoncons::string_view clientIp;
+    jsoncons::string_view clientPort;
+    jsoncons::string_view requestUri;
+    jsoncons::string_view ruleSetType;
+    jsoncons::string_view ruleSetVersion;
+    jsoncons::string_view ruleId;
+    jsoncons::string_view message;
+    jsoncons::string_view action;
+    jsoncons::string_view site;
+    detail details;
+    jsoncons::string_view hostname;
+    jsoncons::string_view transactionId;
+    jsoncons::string_view policyId;
+    jsoncons::string_view policyScope;
+    jsoncons::string_view policyScopeName;
+};
 
-    if (operationName != NULL) {
-        waf_format->set_operationname(operationName);
-    }
+struct waf_log
+{
+    jsoncons::string_view resourceId;
+    jsoncons::string_view operationName;
+    jsoncons::string_view category;
+    property properties;
+};
 
-    if (category != NULL) {
-        waf_format->set_category(category);
-    }
+}   // namespace waf_logging
 
-    if (instanceId != NULL) {
-        properties->set_instanceid(instanceId);
-    }
+JSONCONS_TYPE_TRAITS_DECL(waf_logging::detail, message, data, file, line);
+JSONCONS_TYPE_TRAITS_DECL(waf_logging::property, instanceId, clientIp, clientPort, requestUri, ruleSetType, ruleSetVersion, ruleId, message, action, site, details, hostname, transactionId, policyId, policyScope, policyScopeName);
+JSONCONS_TYPE_TRAITS_DECL(waf_logging::waf_log, resourceId, operationName, category, properties);
 
-    if (clientIP != NULL) {
-        properties->set_clientip(clientIP);
-    }
-    
-    if (clientPort != NULL) {
-        properties->set_clientport(clientPort);
-    }
-    
-    if (requestUri != NULL) {
-        properties->set_requesturi(requestUri);
-    }
-    
-    if (ruleSetType != NULL) {
-        properties->set_rulesettype(ruleSetType);
-    }
-    
-    if (ruleSetVersion != NULL) {
-        properties->set_rulesetversion(ruleSetVersion);
-    }
-    
-    if (ruleId != NULL) {
-		ruleId[strlen(ruleId) - 1] = '\0';
-		properties->set_ruleid(ruleId+1);
-    }
-    
-	bool is_mandatory = false;
-    try {
-		int tmpid = atoi(ruleId+1);
-		is_mandatory = rule_is_mandatory(tmpid);
-
-		if (ruleSetVersion[0] != '2' && !is_mandatory)
-			properties->set_action(WAF_ACTION_MATCHED);
-		else {
-			switch (action) {
-			case MODSEC_MODE_DETECT:
-				properties->set_action(WAF_ACTION_DETECTED);
-				break;
-			case MODSEC_MODE_PREVENT:
-				properties->set_action(WAF_ACTION_BLOCKED);
-				break;
-			default:
-				break;
-			}
-		}
-	}
-    catch (...) {
-		properties->set_action("");
-	}
-    
-	if (messages != NULL) {
-		if (is_mandatory) {
-			char mandatory_message[1024] = "Mandatory rule. Cannot be disabled. ";
-			int ind = strlen(mandatory_message) - 1;
-			int i;
-			for (i = 1; i < strlen(messages) - 1; i++) {
-				if (i + ind < 1023) {
-					mandatory_message[ind + i] = messages[i];
-				}
-				else {
-					break;
-				}
-			}
-			mandatory_message[ind + i] = '\0';
-			properties->set_message(mandatory_message);
-		}
-		else {
-			messages[strlen(messages) - 1] = '\0';
-			properties->set_message(messages+1);
-		}		
-	}
-
-	if (site == 0) {
-        properties->set_site(waf_format::Properties::Global);
-    }
-    
-    if (details_messages != NULL) {
-        details->set_message(details_messages);
-    }
-    
-    if (details_data != NULL) {
-		details_data[strlen(details_data) - 1] = '\0';
-        details->set_data(details_data+1);
+static std::string to_json_string(const waf_logging::waf_log& log) {
+    jsoncons::json_options options;
+    std::string json_output;
+    jsoncons::encode_json(jsoncons::ojson{}, log, json_output, options, jsoncons::indenting::indent);
+    return json_output;
+};
+
+enum class rule_set_types {
+    TYPE_UNKNOWN,
+    OWASP_CRS,
+};
+
+// Employing X-Macro trick (https://en.wikipedia.org/wiki/X_Macro) to keep the list of versions
+// automatically synced up with the map of file paths
+// If you need to add new OWASP version, just add it here and it will sync up in all the places
+#define LIST_OF_VARIABLES \
+    X(2_2_9) \
+    X(3_0) \
+    X(3_1)
+
+enum class rule_set_versions {
+    VERSION_UNKNOWN,
+    #define X(name) VERSION_##name,
+    LIST_OF_VARIABLES
+    #undef X
+};
+
+using rule_set_info = std::pair<rule_set_types, rule_set_versions>;
+#define X(name) static const rule_set_info OWASP_##name {std::make_pair(rule_set_types::OWASP_CRS, rule_set_versions::VERSION_##name)};
+    LIST_OF_VARIABLES
+#undef X
+
+static rule_set_types parse_rule_set_type(const char* type) {
+    if (!type) {
+        return rule_set_types::TYPE_UNKNOWN;
     }
-    
-    if (details_file != NULL) {
-        details->set_file(details_file);
+
+    return (std::strcmp(type, "OWASP_CRS") == 0) ? rule_set_types::OWASP_CRS : rule_set_types::TYPE_UNKNOWN;
+}
+
+static rule_set_versions parse_rule_set_version(const char* version) {
+    if (!version) {
+        return rule_set_versions::VERSION_UNKNOWN;
     }
-    
-    if (details_line != NULL) {
-		details_line[strlen(details_line) - 1] = '\0';
-        details->set_line(details_line+1);
+
+    return (std::strcmp(version, "2.2.9") == 0) ? rule_set_versions::VERSION_2_2_9 :
+        (std::strcmp(version, "3.0.0") == 0) ? rule_set_versions::VERSION_3_0 :
+        (std::strcmp(version, "3.1.0") == 0) ? rule_set_versions::VERSION_3_1 :
+        rule_set_versions::VERSION_UNKNOWN;
+}
+
+using rule_ids_vector = std::vector<int>;
+
+static rule_ids_vector current_rule_ids;
+static std::map<rule_set_info, rule_ids_vector> disableable_rule_ids_map;
+
+static const std::string WAF_ACTION_BLOCKED {"Blocked"};
+static const std::string WAF_ACTION_DETECTED {"Detected"};
+static const std::string WAF_ACTION_MATCHED {"Matched"};
+static const std::string WAF_ACTION_ALLOWED {"Allowed"};
+
+static bool is_mandatory_rule(const rule_set_types type, const rule_set_versions version, const int current_id) {
+    const auto contains_current_id = [current_id] (const rule_ids_vector& rule_ids) {
+        return !std::binary_search(std::begin(rule_ids), std::end(rule_ids), current_id);
+    };
+    const auto rules_id_iter = disableable_rule_ids_map.find(std::make_pair(type, version));
+    if (rules_id_iter != std::end(disableable_rule_ids_map)) {
+        return contains_current_id(rules_id_iter->second);
     }
-    
-	if (hostname != NULL) {
-		properties->set_hostname(hostname);
-	}
-
-	if (waf_unique_id != NULL) {
-		waf_unique_id[strlen(waf_unique_id) - 1] = '\0';
-		properties->set_transactionid(waf_unique_id+1);
-	}
+
+    return contains_current_id(current_rule_ids);
 }
 
-// Main function:  get fields from modsec, set the protobuf object and write to file in json.
-int generate_json(char** result_json, char* resourceId, char* operationName, char* category, char* instanceId, char* clientIP, char* clientPort, const char* requestUri, char* ruleSetType, char* ruleSetVersion, char* ruleId, char* messages, int action, int site, char* details_messages, char* details_data, char* details_file, char* details_line, const char* hostname, char* waf_unique_id) {
-    waf_format::Waf_Format waf_format;
-    string json_string;
-    google::protobuf::util::JsonPrintOptions options;
-    google::protobuf::util::Status convert_result;
-    char* json_str;
-    
-    try {
-        // Verify that the version of the library that we linked against is
-        // compatible with the version of the headers we compiled against.
-        GOOGLE_PROTOBUF_VERIFY_VERSION;
-        
-        // Set Waf format.
-        set_waf_format(&waf_format, resourceId, operationName, category, instanceId, clientIP, clientPort, requestUri, ruleSetType, ruleSetVersion, ruleId, messages, action, site, details_messages, details_data, details_file, details_line, hostname, waf_unique_id); 
-        
-        options.add_whitespace = false;
-        options.always_print_primitive_fields = true;
-        options.preserve_proto_field_names = true;
-        
-        convert_result = MessageToJsonString(waf_format, &json_string, options);
-        
-        if (!convert_result.ok()) {
-            return WAF_LOG_UTIL_FAILED;
+static std::string get_json_log_message(const char* resource_id, const char* operation_name, const char* category,
+        const char* instance_id, const char* client_ip, const char* client_port, const char* request_uri,
+        const char* rule_set_type, const char* rule_set_version, const char* rule_id, const char* messages,
+        const int action, const int site, const char* details_messages, const char* details_data,
+        const char* details_file, const char* details_line, const char* hostname, const char* waf_unique_id,
+        const char* waf_policy_id, const char* waf_policy_scope, const char* waf_policy_scope_name) {
+
+    using str_view = jsoncons::string_view;
+    const auto make_string_view_strip_quotes = [] (const char* str) -> str_view {
+        if (!str) {
+            return {};
+        }
+        auto length = strlen(str);
+        if (length < 2) {
+            return {};
+        }
+
+        return {str + 1, length - 2};
+    };
+    const str_view rule_id_str = make_string_view_strip_quotes(rule_id);
+    const rule_set_types type = parse_rule_set_type(rule_set_type);
+    const rule_set_versions version = parse_rule_set_version(rule_set_version);
+    const int current_id = std::stoi(static_cast<std::string>(rule_id_str));
+    const bool is_mandatory = is_mandatory_rule(type, version, current_id);
+
+    const str_view action_str =
+        (action == ACTION_ALLOW || action == ACTION_ALLOW_REQUEST || action == ACTION_ALLOW_PHASE) ? WAF_ACTION_ALLOWED :
+        (version != rule_set_versions::VERSION_2_2_9 && !is_mandatory)                             ? WAF_ACTION_MATCHED :
+        action == MODSEC_MODE_DETECT                                                               ? WAF_ACTION_DETECTED :
+        action == MODSEC_MODE_PREVENT                                                              ? WAF_ACTION_BLOCKED :
+        str_view{};
+
+    #define MANDATORY_RULE_MESSAGE ("Mandatory rule. Cannot be disabled. ")
+    #define MANDATORY_RULE_MESSAGE_LENGTH (sizeof(MANDATORY_RULE_MESSAGE) - 1)
+    std::array<char, 1024> mandatory_message {MANDATORY_RULE_MESSAGE};
+    str_view message_str;
+    if (messages) {
+        if (is_mandatory) {
+            size_t truncated_length = std::min(strlen(messages) - 1, mandatory_message.size() - MANDATORY_RULE_MESSAGE_LENGTH);
+            if (truncated_length > 1) {
+                const str_view messages_view {messages + 1, truncated_length - 1};
+                std::copy(messages_view.cbegin(), messages_view.cend(), mandatory_message.begin() + MANDATORY_RULE_MESSAGE_LENGTH);
+            }
+            message_str = {&mandatory_message.front(), MANDATORY_RULE_MESSAGE_LENGTH + truncated_length - 1};
+        }
+        else {
+            message_str = make_string_view_strip_quotes(messages);
         }
     }
-    catch (...) {
-        return WAF_LOG_UTIL_FAILED;
-    }
+
+    /* For historical reasons, the "site" property is included in log messages.
+     * However, it does not carry any useful meaning.
+     * We keep it around for compatibility reasons but may consider removing it at some point in the future.
+     */
+    const str_view site_str = (site == 0) ? "Global" : str_view{};
+
+    const str_view details_file_str = details_file ? str_view {details_file, strlen(details_file) - 1} : str_view{};
+    const str_view details_data_str = make_string_view_strip_quotes(details_data);
+    const str_view details_line_str = make_string_view_strip_quotes(details_line);
+    const str_view waf_unique_id_str = make_string_view_strip_quotes(waf_unique_id);
+
+    return to_json_string({
+            resource_id,
+            operation_name,
+            category,
+            waf_logging::property {
+                instance_id,
+                client_ip,
+                client_port,
+                request_uri,
+                rule_set_type,
+                rule_set_version,
+                rule_id_str,
+                message_str,
+                action_str,
+                site_str,
+                waf_logging::detail {
+                    details_messages,
+                    details_data_str,
+                    details_file_str,
+                    details_line_str
+                },
+            hostname,
+            waf_unique_id_str,
+            waf_policy_id,
+            waf_policy_scope,
+            waf_policy_scope_name
+        }
+    });
+}
+
+// Get fields from ModSec and format them to JSON.
+char* generate_json(const char* resource_id, const char* operation_name, const char* category,
+        const char* instance_id, const char* client_ip, const char* client_port, const char* request_uri,
+        const char* rule_set_type, const char* rule_set_version, const char* rule_id, const char* messages,
+        const int action, const int site, const char* details_messages, const char* details_data,
+        const char* details_file, const char* details_line, const char* hostname, const char* waf_unique_id,
+        const char* waf_policy_id, const char* waf_policy_scope, const char* waf_policy_scope_name) {
     
-    // Write the waf json string to disk.
-    *result_json = strdup((json_string + "\n").c_str());
-    return WAF_LOG_UTIL_SUCCESS; 
+    try {
+        const std::string json_string = get_json_log_message(resource_id, operation_name, category,
+                instance_id, client_ip, client_port, request_uri, rule_set_type, rule_set_version,
+                rule_id, messages, action, site, details_messages, details_data, details_file,
+                details_line, hostname, waf_unique_id, waf_policy_id, waf_policy_scope, waf_policy_scope_name);
+        char* result = static_cast<char*>(std::malloc(json_string.length() + 2)); // Two extra bytes for \n and \0
+        std::copy(json_string.begin(), json_string.end(), result);
+        result[json_string.length()] = '\n';
+        result[json_string.length() + 1] = '\0';
+        return result;
+    } catch (...) {}
+
+    return nullptr;
 }
 
 void free_json(char* str) {
-    free(str);
+    std::free(str);
 }
 
-void init_appgw_rules_id_hash() {
-	ifstream infile(RULES_ID_FILE);
-	string line;
-
-	while (getline(infile, line)) {
-		int rule_id = stoi(line);
-		appgw_ruleid_hash[rule_id] = true;
-	}
-	infile.close();
+namespace stdx = std::experimental;
+static stdx::optional<rule_ids_vector> read_disableable_rule_ids(const std::string& filename) {
+    std::ifstream ifs(filename);
+    if (ifs) {
+        rule_ids_vector rule_ids;
+        std::copy(std::istream_iterator<int>(ifs),
+                std::istream_iterator<int>(),
+                std::back_inserter(rule_ids));
+        std::sort(std::begin(rule_ids), std::end(rule_ids));
+        return rule_ids;
+    }
 
-	return;
+    return {};
 }
 
-bool rule_is_mandatory(int rule_id) {
-	return (appgw_ruleid_hash.find(rule_id) == appgw_ruleid_hash.end());
-}
\ No newline at end of file
+int init_appgw_rules_id_hash() {
+    const std::map<rule_set_info, std::string> disableableRulesPathsMap {
+        #define X(name) {OWASP_##name, "/etc/nginx/modsec/OWASP/" #name "/disableableRules.conf"},
+        LIST_OF_VARIABLES
+        #undef X
+    };
+    for (const auto& item : disableableRulesPathsMap) {
+        const auto vec = read_disableable_rule_ids(item.second);
+        if (vec) {
+            disableable_rule_ids_map[item.first] = *vec;
+        }
+    }
+    if (disableable_rule_ids_map.empty()) {
+        // If we haven't found type/version-specific files, we need to read the default (deprecated one)
+        const auto vec = read_disableable_rule_ids("/etc/nginx/modsec/disableableRules.conf");
+        if (!vec) {
+            return -1;
+        }
+
+        current_rule_ids = *vec;
+    }
+    return 0;
+}
diff --git a/apache2/waf_logging/waf_log_util.h b/apache2/waf_logging/waf_log_util.h
new file mode 100755
index 0000000000..9fb95a8140
--- /dev/null
+++ b/apache2/waf_logging/waf_log_util.h
@@ -0,0 +1,35 @@
+#ifndef _WAF_LOG_UTIL_EXTERNAL_HEADER
+#define _WAF_LOG_UTIL_EXTERNAL_HEADER
+
+#define WAF_LOG_UTIL_FAILED -1
+#define WAF_LOG_UTIL_SUCCESS 0
+#define MODSEC_MODE_DETECT 1
+#define MODSEC_MODE_PREVENT 2
+
+// ACTION_ALLOW/ACTION_ALLOW_REQUEST/ACTION_ALLOW_PHASE are defined in the modsecurity.h and copied here
+#define ACTION_ALLOW 5
+#define ACTION_ALLOW_REQUEST 6
+#define ACTION_ALLOW_PHASE 7
+
+#define WAF_RULESET_PREFIX "/RuleSets/"
+#define WAF_LOG_UTIL_FILE "waf_json.log"
+#define WAF_LOG_UTIL_OPERATION_NAME "ApplicationGatewayFirewall"
+#define WAF_LOG_UTIL_CATEGORY "ApplicationGatewayFirewallLog"
+
+#define RULE_TYPE_OWASP_CRS "OWASP_CRS"
+
+#define RULE_HASH_SIZE 499
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+char* generate_json(const char* resourceId, const char* operationName, const char* category, const char* instanceId, const char* clientIP, const char* clientPort, const char* requestUri, const char* ruleSetType, const char* ruleSetVersion, const char* ruleId, const char* messages, const int action, const int site, const char* details_messages, const char* details_data, const char* details_file, const char* details_line, const char* hostname, const char* waf_unique_id,
+const char* waf_policy_id, const char* waf_policy_scope, const char* waf_policy_scope_name);
+void free_json(char* str);
+// Returns 0 if succeeded, non-zero code otherwise
+int init_appgw_rules_id_hash();
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/apache2/waf_logging/waf_log_util_external.h b/apache2/waf_logging/waf_log_util_external.h
deleted file mode 100755
index 7c9f7dcbab..0000000000
--- a/apache2/waf_logging/waf_log_util_external.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef _WAF_LOG_UTIL_EXTERNAL_HEADER
-#define _WAF_LOG_UTIL_EXTERNAL_HEADER
-
-#define WAF_LOG_UTIL_FAILED -1
-#define WAF_LOG_UTIL_SUCCESS 0
-#define MODSEC_MODE_DETECT 1
-#define MODSEC_MODE_PREVENT 2
-#define WAF_RULESET_PREFIX "/RuleSets/"
-#define WAF_LOG_UTIL_FILE "waf_json.log"
-#define WAF_LOG_UTIL_OPERATION_NAME "ApplicationGatewayFirewall"
-#define WAF_LOG_UTIL_CATEGORY "ApplicationGatewayFirewallLog"
-
-#define WAF_ACTION_BLOCKED "Blocked"
-#define WAF_ACTION_DETECTED "Detected"
-#define WAF_ACTION_MATCHED "Matched"
-
-#define RULE_TYPE_OWASP_CRS "OWASP_CRS"
-
-#define RULE_HASH_SIZE 499
-#define RULES_ID_FILE "/etc/nginx/modsec/disableableRules.conf"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-int generate_json(char** result, char* resourceId, char* operationName, char* category, char* instanceId, char* clientIP, char* clientPort, const char* requestUri, char* ruleSetType, char* ruleSetVersion, char* ruleId, char* messages, int action, int site, char* details_messages, char* details_data, char* details_file, char* details_line, const char* hostname, char* waf_unique_id);
-void free_json(char* str);
-bool rule_is_mandatory(int ruleId);
-void init_appgw_rules_id_hash();
-void strip_number(char * in);
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/apache2/waf_logging/waf_log_util_internal.h b/apache2/waf_logging/waf_log_util_internal.h
deleted file mode 100644
index e265d2c7c8..0000000000
--- a/apache2/waf_logging/waf_log_util_internal.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _WAF_LOG_UTIL_INTERNAL_HEADER
-#define _WAF_LOG_UTIL_INTERNAL_HEADER
-
-#include <google/protobuf/util/json_util.h>
-#include"waf_format.pb.h"
-#include <fstream>
-#include <exception>
-
-#endif 
diff --git a/appveyor.yml b/appveyor.yml
index 85695c56e3..1e26ae1279 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -43,7 +43,7 @@ before_test:
         & "$env:SystemRoot\System32\inetsrv\appcmd.exe" set config /section:"system.webServer/ModSecurity" /"enabled:true" /"configFile:$modsec_iis_conf"
         #setup the crs repo and the modsec config
         $crs_path = "C:\projects\owasp-modsecurity-crs"
-        git clone -q https://github.com/SpiderLabs/owasp-modsecurity-crs.git $crs_path
+        git clone -b "v3.1.0" -q https://github.com/SpiderLabs/owasp-modsecurity-crs.git $crs_path
         cd "$modsec_repo\tests"
         Rename-Item -Path "$crs_path\crs-setup.conf.example" -NewName "crs-setup.conf"
         Copy-Item "$crs_path\util\regression-tests\requirements.txt" -Destination "$modsec_repo\tests" -Force
diff --git a/build/compile b/build/compile
deleted file mode 100755
index 1b1d232169..0000000000
--- a/build/compile
+++ /dev/null
@@ -1,142 +0,0 @@
-#! /bin/sh
-# Wrapper for compilers which do not understand `-c -o'.
-
-scriptversion=2005-05-14.22
-
-# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
-# Written by Tom Tromey <tromey@cygnus.com>.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# This file is maintained in Automake, please report
-# bugs to <bug-automake@gnu.org> or send patches to
-# <automake-patches@gnu.org>.
-
-case $1 in
-  '')
-     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
-     exit 1;
-     ;;
-  -h | --h*)
-    cat <<\EOF
-Usage: compile [--help] [--version] PROGRAM [ARGS]
-
-Wrapper for compilers which do not understand `-c -o'.
-Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
-arguments, and rename the output as expected.
-
-If you are trying to build a whole package this is not the
-right script to run: please start by reading the file `INSTALL'.
-
-Report bugs to <bug-automake@gnu.org>.
-EOF
-    exit $?
-    ;;
-  -v | --v*)
-    echo "compile $scriptversion"
-    exit $?
-    ;;
-esac
-
-ofile=
-cfile=
-eat=
-
-for arg
-do
-  if test -n "$eat"; then
-    eat=
-  else
-    case $1 in
-      -o)
-	# configure might choose to run compile as `compile cc -o foo foo.c'.
-	# So we strip `-o arg' only if arg is an object.
-	eat=1
-	case $2 in
-	  *.o | *.obj)
-	    ofile=$2
-	    ;;
-	  *)
-	    set x "$@" -o "$2"
-	    shift
-	    ;;
-	esac
-	;;
-      *.c)
-	cfile=$1
-	set x "$@" "$1"
-	shift
-	;;
-      *)
-	set x "$@" "$1"
-	shift
-	;;
-    esac
-  fi
-  shift
-done
-
-if test -z "$ofile" || test -z "$cfile"; then
-  # If no `-o' option was seen then we might have been invoked from a
-  # pattern rule where we don't need one.  That is ok -- this is a
-  # normal compilation that the losing compiler can handle.  If no
-  # `.c' file was seen then we are probably linking.  That is also
-  # ok.
-  exec "$@"
-fi
-
-# Name of file we expect compiler to create.
-cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
-
-# Create the lock directory.
-# Note: use `[/.-]' here to ensure that we don't use the same name
-# that we are using for the .o file.  Also, base the name on the expected
-# object file name, since that is what matters with a parallel build.
-lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d
-while true; do
-  if mkdir "$lockdir" >/dev/null 2>&1; then
-    break
-  fi
-  sleep 1
-done
-# FIXME: race condition here if user kills between mkdir and trap.
-trap "rmdir '$lockdir'; exit 1" 1 2 15
-
-# Run the compile.
-"$@"
-ret=$?
-
-if test -f "$cofile"; then
-  mv "$cofile" "$ofile"
-elif test -f "${cofile}bj"; then
-  mv "${cofile}bj" "$ofile"
-fi
-
-rmdir "$lockdir"
-exit $ret
-
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/configure.ac b/configure.ac
index eb3a09e1fb..e80472e404 100644
--- a/configure.ac
+++ b/configure.ac
@@ -580,22 +580,6 @@ AC_ARG_ENABLE(collection-memory-database,
   collection_memory_database="-I"`pwd`"/apache2/ag_mdb -DMEMORY_DATABASE_ENABLE"
 ])
 
-# ProtoBuf compiler.
-# First, specify with --with-protoc=/path/of/protoc.
-# Or, specify with env variable PROTOC.
-# If neither of the above, find it in the path.
-#AC_MSG_CHECKING([for ProtoBuf compiler protoc])
-AC_ARG_WITH([protoc],
-    [AS_HELP_STRING([--with-protoc=/path/of/protoc],
-        [Location of the protocol buffers compiler protoc. Defaults to looking on path.])],
-    [PROTOC="$withval"],
-    [ AS_IF([test "x${PROTOC}" == "x"],
-        [AC_PATH_PROG([PROTOC], [protoc], [no])])
-    ]
-)
-#AC_MSG_RESULT([${PROTOC}])
-AS_IF([test "${PROTOC}" == "no"], [AC_MSG_ERROR([ProtoBuf compiler "protoc" not found.])])
-
 # Enable json waf logging support
 AC_ARG_ENABLE(waf_json_logging,
               AS_HELP_STRING([--enable-waf_json_logging],
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 61da8dfe54..a344cd8cd3 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -21,7 +21,7 @@ services:
     working_dir: /microsoft/modsecurity
     entrypoint: 
       - ./configure
-      - CXXFLAGS=-std=c++11
+      - CXXFLAGS=-std=c++14
       - CFLAGS=-fPIC
       - --enable-standalone-module
       - --disable-mlogc
diff --git a/docker/Dockerfile b/docker/Dockerfile
old mode 100644
new mode 100755
index 357e0fd276..dc3350d74a
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -9,14 +9,11 @@ RUN apt-get install -y  \
     m4 \
     libtool
 
-# Install google protobuf 3.5.1
-RUN mkdir -p /tmp/protobuf/
-RUN curl -o /tmp/protobuf/protobuf-3.5.1.zip -OL https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-cpp-3.5.1.zip
-RUN unzip /tmp/protobuf/protobuf-3.5.1.zip -d /tmp/protobuf/
-WORKDIR /tmp/protobuf/protobuf-3.5.1
-RUN ./autogen.sh
-RUN ./configure --prefix=/usr
-RUN  make && make install
+# Install jsoncons 
+RUN mkdir -p /tmp/jsoncons/
+RUN curl -o /tmp/jsoncons/jsoncons-0.122.0.zip -OL https://github.com/danielaparker/jsoncons/archive/v0.122.0.zip
+RUN unzip /tmp/jsoncons/jsoncons-0.122.0.zip -d /tmp/jsoncons
+RUN cp -r /tmp/jsoncons/jsoncons-0.122.0/include/jsoncons /usr/include/
 
 RUN apt-get install -y \
         apache2-dev \
diff --git a/iis/build_modsecurity.bat b/iis/build_modsecurity.bat
index 4ee8348f56..ab297d19bf 100644
--- a/iis/build_modsecurity.bat
+++ b/iis/build_modsecurity.bat
@@ -15,21 +15,21 @@ set CURRENT_DIR=%cd%
 cd ..\apache2
 del *.obj *.dll *.lib
 del libinjection\*.obj libinjection\*.dll libinjection\*.lib
-NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl IIS_BUILD=yes
+NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre\build LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl IIS_BUILD=yes
 @if NOT (%ERRORLEVEL%) == (0) goto build_failed
 
 @echo mlogc...
 cd ..\mlogc
 del *.obj *.dll *.lib
 nmake -f Makefile.win clean
-nmake -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre CURL=..\iis\%DEPENDENCIES_DIR%\curl YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep VERSION=VERSION_IIS
+nmake -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre\build CURL=..\iis\%DEPENDENCIES_DIR%\curl YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep VERSION=VERSION_IIS
 @if NOT (%ERRORLEVEL%) == (0) goto build_failed
 
 @echo iis...
 cd ..\iis
 del *.obj *.dll *.lib
 nmake -f Makefile.win clean
-NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl
+NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre\build LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl
 @if NOT (%ERRORLEVEL%) == (0) goto build_failed
 
 cd %CURRENT_DIR%
diff --git a/iis/dependencies/build_pcre.bat b/iis/dependencies/build_pcre.bat
index 78a4df9847..b1cc496014 100644
--- a/iis/dependencies/build_pcre.bat
+++ b/iis/dependencies/build_pcre.bat
@@ -18,15 +18,17 @@ cd "pcre"
 cat CMakeLists.txt | sed "s/PCRE_STATIC_RUNTIME OFF CACHE BOOL/PCRE_STATIC_RUNTIME/g" | sed "s/PCRE_NO_RECURSE OFF/PCRE_NO_RECURSE ON/g" > CMakeLists.txt.ops
 move CMakeLists.txt CMakeLists.txt.old
 move CMakeLists.txt.ops CMakeLists.txt
-CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True
+mkdir build
+cd build
+CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True ../
 @if NOT (%ERRORLEVEL%) == (0) goto build_failed
 NMAKE
 @if NOT (%ERRORLEVEL%) == (0) goto build_failed
 cd "%WORK%"
 
-copy /y "%WORK_DIR%\pcre\pcre.dll" "%OUTPUT_DIR%"
-copy /y "%WORK_DIR%\pcre\pcre.pdb" "%OUTPUT_DIR%"
-copy /y "%WORK_DIR%\pcre\pcre.lib" "%OUTPUT_DIR%"
+copy /y "%WORK_DIR%\pcre\build\pcre.dll" "%OUTPUT_DIR%"
+copy /y "%WORK_DIR%\pcre\build\pcre.pdb" "%OUTPUT_DIR%"
+copy /y "%WORK_DIR%\pcre\build\pcre.lib" "%OUTPUT_DIR%"
 copy /y "%WORK_DIR%\pcre\pcre.h.generic" "%WORK_DIR%\pcre\pcre.h"
 echo "a"
 @exit /B 0
diff --git a/iis/moduleconfig.h b/iis/moduleconfig.h
index 2729006c70..1fb33e29a7 100644
--- a/iis/moduleconfig.h
+++ b/iis/moduleconfig.h
@@ -69,6 +69,8 @@ class MODSECURITY_STORED_CONTEXT : public IHttpStoredContext
 
 	void*			  m_Config;
 
+    bool configLoadingFailed = false;
+
 private:
     HRESULT 
     GetBooleanPropertyValue( 
diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp
index f7ee79e25e..1e5e32c3c8 100644
--- a/iis/mymodule.cpp
+++ b/iis/mymodule.cpp
@@ -743,9 +743,9 @@ CMyHttpModule::OnBeginRequest(
     
     if ( FAILED( hr ) )
     {
-        //hr = E_UNEXPECTED;
-		hr = S_OK;
-        goto Finished;
+        pHttpContext->GetResponse()->SetStatus(500, "WAF internal error. Unable to get config.");
+        pHttpContext->SetRequestHandled();
+        return RQ_NOTIFICATION_FINISH_REQUEST;
     }
 
 	// If module is disabled, dont go any further
@@ -755,6 +755,19 @@ CMyHttpModule::OnBeginRequest(
         goto Finished;
 	}
 
+    auto reportConfigurationError = [pConfig, pHttpContext] {
+        pConfig->configLoadingFailed = true;
+        pHttpContext->GetResponse()->SetStatus(500, "WAF internal error. Invalid configuration.");
+        pHttpContext->SetRequestHandled();
+        return RQ_NOTIFICATION_FINISH_REQUEST;
+    };
+
+    // If we previously failed to load the config, don't spam the event log by trying and failing again
+    if (pConfig->configLoadingFailed)
+    {
+        return reportConfigurationError();
+    }
+
 	if(pConfig->m_Config == NULL)
 	{
 		char *path;
@@ -764,8 +777,7 @@ CMyHttpModule::OnBeginRequest(
 
 		if ( FAILED( hr ) )
 		{
-			hr = E_UNEXPECTED;
-			goto Finished;
+            return reportConfigurationError();
 		}
 
 		pConfig->m_Config = modsecGetDefaultConfig();
@@ -779,8 +791,7 @@ CMyHttpModule::OnBeginRequest(
 		if ( FAILED( hr ) )
 		{
 			delete path;
-			hr = E_UNEXPECTED;
-			goto Finished;
+            return reportConfigurationError();
 		}
 
 		if(path[0] != 0)
@@ -792,7 +803,7 @@ CMyHttpModule::OnBeginRequest(
 				WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE);
 				delete apppath;
 				delete path;
-				goto Finished;
+                return reportConfigurationError();
 			}
 
 			modsecReportRemoteLoadedRules();
@@ -811,7 +822,6 @@ CMyHttpModule::OnBeginRequest(
 	request_rec *r;
 
 	c = modsecNewConnection();
-
 	modsecProcessConnection(c);
 
 	r = modsecNewRequest(c, (directory_config *)pConfig->m_Config);
diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c
index 17906fe355..9ed7cbbbcb 100644
--- a/nginx/modsecurity/apr_bucket_nginx.c
+++ b/nginx/modsecurity/apr_bucket_nginx.c
@@ -22,7 +22,6 @@ typedef struct apr_bucket_nginx {
 
 /* ngx_buf_t to apr_bucket */
 apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf,
-                                     apr_pool_t *p,
                                      apr_bucket_alloc_t *list)
 {
 
@@ -31,11 +30,10 @@ apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf,
     APR_BUCKET_INIT(b); /* link */
     b->free = apr_bucket_free;
     b->list = list;
-    return apr_bucket_nginx_make(b, buf, p);
+    return apr_bucket_nginx_make(b, buf);
 }
 
-apr_bucket * apr_bucket_nginx_make(apr_bucket *b, ngx_buf_t *buf,
-                                   apr_pool_t *pool)
+apr_bucket * apr_bucket_nginx_make(apr_bucket *b, ngx_buf_t *buf)
 {
     apr_bucket_nginx *n;
 
@@ -93,11 +91,11 @@ static void nginx_bucket_destroy(void *data)
 
 
 ngx_int_t
-copy_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) {
+copy_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_int_t last_buf) {
     apr_bucket         *e;
 
     while (chain) {
-        e = ngx_buf_to_apr_bucket(chain->buf, bb->p, bb->bucket_alloc);
+        e = ngx_buf_to_apr_bucket(chain->buf, bb->bucket_alloc);
         if (e == NULL) {
             return NGX_ERROR;
         }
diff --git a/nginx/modsecurity/apr_bucket_nginx.h b/nginx/modsecurity/apr_bucket_nginx.h
index a4ce631389..b6b5762770 100644
--- a/nginx/modsecurity/apr_bucket_nginx.h
+++ b/nginx/modsecurity/apr_bucket_nginx.h
@@ -2,13 +2,10 @@
 #include <ngx_core.h>
 #include "apr_buckets.h"
 
-apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf,
-                                     apr_pool_t *p,
-                                     apr_bucket_alloc_t *list);
+apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf, apr_bucket_alloc_t *list);
 
-apr_bucket * apr_bucket_nginx_make(apr_bucket *e, ngx_buf_t *buf,
-                                   apr_pool_t *pool);
+apr_bucket * apr_bucket_nginx_make(apr_bucket *e, ngx_buf_t *buf);
 
 #define ngx_buf_to_apr_bucket apr_bucket_nginx_create
 
-ngx_int_t copy_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf);
+ngx_int_t copy_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_int_t last_buf);
diff --git a/nginx/modsecurity/config b/nginx/modsecurity/config
deleted file mode 100644
index fe8471ba3d..0000000000
--- a/nginx/modsecurity/config
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-
-CFLAGS="$CFLAGS \
-     -I/usr/include/apr-1.0  \
-     -I/usr/include/apr-1.0 -I/usr/include \
-    -I/usr/include/apache2 \
-    -I/usr/include/libxml2 \
-    -DWITH_LUA -I/usr/include/lua5.1   \
-    -DWITH_PCRE_STUDY -DMODSEC_PCRE_MATCH_LIMIT=1500 -DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500  -DREQUEST_EARLY          \
-     \
-    -DWITH_YAJL -I/usr/include/yajl  "
-
-
-CORE_LIBS="$CORE_LIBS \
-     -L/usr/lib -lapr-1 \
-     -L/usr/lib -laprutil-1 \
-    -I/usr/include/apache2 \
-    -L/usr/lib/x86_64-linux-gnu -lcurl \
-    -lxml2 \
-    -llua5.1   \
-    -lpcre \
-    -L/usr/lib  -lcap \
-    -lyajl  "
-
-ngx_addon_name=ngx_http_modsecurity
-
-CORE_MODULES="$CORE_MODULES ngx_pool_context_module"
-
-HTTP_AUX_FILTER_MODULES="ngx_http_modsecurity $HTTP_AUX_FILTER_MODULES"
-
-NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
-		$ngx_addon_dir/ngx_http_modsecurity.c \
-		$ngx_addon_dir/apr_bucket_nginx.c \
-		$ngx_addon_dir/ngx_pool_context.c"
-
-NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
-		$ngx_addon_dir/apr_bucket_nginx.h \
-		$ngx_addon_dir/ngx_pool_context.h"
-
-CORE_LIBS="$ngx_addon_dir/../../standalone/.libs/standalone.a $CORE_LIBS"
-
-CORE_INCS="$CORE_INCS \
-	$ngx_addon_dir \
-	$ngx_addon_dir/../../standalone \
-	$ngx_addon_dir/../../apache2"
-
diff --git a/nginx/modsecurity/config.in b/nginx/modsecurity/config.in
index 64cf14b5b9..f77d4f7ca8 100644
--- a/nginx/modsecurity/config.in
+++ b/nginx/modsecurity/config.in
@@ -23,8 +23,7 @@ CORE_LIBS="$CORE_LIBS \
     @APXS_LIBS@ \
     @YAJL_LIBS@ \
     @SSDEEP_LDFLAGS@ \
-    -lstdc++ \
-    -lprotobuf"
+    -lstdc++"
 
 ngx_addon_name=ngx_http_modsecurity
 
@@ -34,9 +33,11 @@ HTTP_AUX_FILTER_MODULES="ngx_http_modsecurity $HTTP_AUX_FILTER_MODULES"
 
 NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
 		$ngx_addon_dir/ngx_http_modsecurity.c \
+		$ngx_addon_dir/ngx_http_modsecurity_config_cache.c \
 		$ngx_addon_dir/apr_bucket_nginx.c"
 
 NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
+		$ngx_addon_dir/ngx_http_modsecurity_config_cache.h \
 		$ngx_addon_dir/apr_bucket_nginx.h"
 
 CORE_LIBS="$ngx_addon_dir/../../standalone/.libs/standalone.a $CORE_LIBS"
diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c
index 2b5809fda6..33d11335d3 100644
--- a/nginx/modsecurity/ngx_http_modsecurity.c
+++ b/nginx/modsecurity/ngx_http_modsecurity.c
@@ -13,6 +13,7 @@
 */
 
 #include <ngx_http.h>
+#include <ngx_http_modsecurity_config_cache.h>
 #include <apr_bucket_nginx.h>
 
 #include <apr_base64.h>
@@ -22,7 +23,9 @@
 #undef CRLF
 
 #include "api.h"
-#include <waf_log_util_external.h>
+#ifdef WAF_JSON_LOGGING_ENABLE
+#include <waf_log_util.h>
+#endif
 
 #define NOTE_NGINX_REQUEST_CTX "nginx-ctx"
 
@@ -50,8 +53,14 @@ typedef struct {
 
 typedef struct {
     ngx_http_request_t *r;
-} ngx_http_modsecurity_thread_ctx_t;
+} ngx_http_modsecurity_prevention_thread_ctx_t;
+
+typedef struct {
+    conn_rec            *connection;
+    request_rec         *req;
 
+    apr_bucket_brigade  *brigade;
+} ngx_http_modsecurity_detection_thread_ctx_t;
 
 /*
 ** Module's registred function/handlers.
@@ -70,10 +79,6 @@ static int ngx_http_modsecurity_drop_action(request_rec *r);
 static void ngx_http_modsecurity_terminate(ngx_cycle_t *cycle);
 static void ngx_http_modsecurity_cleanup(void *data);
 
-static int ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const char *value);
-static int ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const char *value);
-
-
 static ngx_str_t thread_pool_name = ngx_string("default");
 
 ngx_thread_mutex_t mtx;
@@ -134,22 +139,6 @@ ngx_module_t ngx_http_modsecurity = {
 
 static ngx_http_upstream_t ngx_http_modsecurity_upstream;
 
-static struct {
-    char      *name;
-    ngx_str_t  variable_name;
-} special_headers_out[] = {
-    {"Content-Type",      ngx_string("sent_http_content_type")  },
-    {"Content-Length",    ngx_string("sent_http_content_length")},
-    {"Location",          ngx_string("sent_http_location")},
-    {"Last-Modified",     ngx_string("sent_http_last_modified")},
-    {"Connection",        ngx_string("sent_http_connection")},
-    {"Keep-Alive",        ngx_string("sent_http_keep_alive")},
-    {"Transfer-Encoding", ngx_string("sent_http_transfer_encoding")},
-    {"Cache-Control",     ngx_string("sent_http_cache_control")},
-    {NULL,                ngx_null_string}
-};
-
-
 static inline u_char *
 ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src)
 {
@@ -167,6 +156,34 @@ ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src)
 }
 
 
+static inline char *
+dup_ngx_str_to_apr(apr_pool_t *pool, ngx_str_t *src)
+{
+    return apr_pstrmemdup(pool, (char *)src->data, src->len);
+}
+
+/*
+ * Helper to allocate a thread task using an Apache pool.
+ * This is useful when the background task can outlive the Nginx request
+ * it is initiated for.
+ */
+ngx_thread_task_t *
+apr_thread_task_alloc(apr_pool_t *pool, size_t size)
+{
+    ngx_thread_task_t  *task;
+
+    // The logic of ngx_thread_task_post relies on the task to be zero-initialized.
+    // That is why we use apr_pcalloc here.
+    task = apr_pcalloc(pool, sizeof(ngx_thread_task_t) + size);
+    if (task == NULL) {
+        return NULL;
+    }
+
+    task->ctx = task + 1;
+
+    return task;
+}
+
 static inline int
 ngx_http_modsecurity_method_number(unsigned int nginx)
 {
@@ -211,6 +228,11 @@ ngx_http_modsecurity_method_number(unsigned int nginx)
     return MultiplyDeBruijnBitPosition[((uint32_t)((nginx & -nginx) * 0x077CB531U)) >> 27];
 }
 
+/*
+ * Use APR pool for all allocations because they should not depend on Nginx request pool.
+ * In case of detection mode, processing will take place entirely in background and may last
+ * longer than the original request lives.
+ */
 static ngx_inline ngx_int_t
 ngx_http_modsecurity_load_request(ngx_http_request_t *r)
 {
@@ -228,7 +250,7 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
     req = ctx->req;
 
     /* request line */
-    req->method = (char *)ngx_pstrdup0(r->pool, &r->method_name);
+    req->method = dup_ngx_str_to_apr(req->pool, &r->method_name);
 
     /* TODO: how to use ap_method_number_of ?
      * req->method_number = ap_method_number_of(req->method);
@@ -241,18 +263,18 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
         return NGX_ERROR;
     }
 
-    req->filename = (char *) path.data;
+    req->filename = dup_ngx_str_to_apr(req->pool, &path);
     req->path_info = req->filename;
 
-    req->args = (char *)ngx_pstrdup0(r->pool, &r->args);
+    req->args = dup_ngx_str_to_apr(req->pool, &r->args);
 
     req->proto_num = r->http_major *1000 + r->http_minor;
-    req->protocol = (char *)ngx_pstrdup0(r->pool, &r->http_protocol);
+    req->protocol = dup_ngx_str_to_apr(req->pool, &r->http_protocol);
     req->request_time = apr_time_make(r->start_sec, r->start_msec);
-    req->the_request = (char *)ngx_pstrdup0(r->pool, &r->request_line);
+    req->the_request = dup_ngx_str_to_apr(req->pool, &r->request_line);
 
-    req->unparsed_uri = (char *)ngx_pstrdup0(r->pool, &r->unparsed_uri);
-    req->uri = (char *)ngx_pstrdup0(r->pool, &r->uri);
+    req->unparsed_uri = dup_ngx_str_to_apr(req->pool, &r->unparsed_uri);
+    req->uri = dup_ngx_str_to_apr(req->pool, &r->uri);
 
     req->parsed_uri.scheme = "http";
 
@@ -262,7 +284,7 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
     }
 #endif
 
-    req->parsed_uri.path = (char *)ngx_pstrdup0(r->pool, &r->uri);
+    req->parsed_uri.path = dup_ngx_str_to_apr(req->pool, &r->uri);
     req->parsed_uri.is_initialized = 1;
 
     switch (r->connection->local_sockaddr->sa_family) {
@@ -287,18 +309,16 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
     }
 
     req->parsed_uri.port = port;
-    req->parsed_uri.port_str = ngx_pnalloc(r->pool, sizeof("65535"));
+    req->parsed_uri.port_str = apr_palloc(req->pool, sizeof("65535"));
     (void) ngx_sprintf((u_char *)req->parsed_uri.port_str, "%ui%c", port, '\0');
 
     req->parsed_uri.query = r->args.len ? req->args : NULL;
     req->parsed_uri.dns_looked_up = 0;
     req->parsed_uri.dns_resolved = 0;
 
-    // req->parsed_uri.password = (char *)ngx_pstrdup0(r->pool, &r->headers_in.passwd);
-    // req->parsed_uri.user = (char *)ngx_pstrdup0(r->pool, &r->headers_in.user);
-    req->parsed_uri.fragment = (char *)ngx_pstrdup0(r->pool, &r->exten);
+    req->parsed_uri.fragment = dup_ngx_str_to_apr(req->pool, &r->exten);
 
-    req->hostname = (char *)ngx_pstrdup0(r->pool, (ngx_str_t *)&ngx_cycle->hostname);
+    req->hostname = dup_ngx_str_to_apr(req->pool, (ngx_str_t *)&ngx_cycle->hostname);
 
     req->header_only = r->header_only ? r->header_only : (r->method == NGX_HTTP_HEAD);
 
@@ -337,7 +357,15 @@ ngx_http_modsecurity_load_headers_in(ngx_http_request_t *r)
             i = 0;
         }
 
-        apr_table_setn(req->headers_in, (char *)h[i].key.data, (char *)h[i].value.data);
+        const char *key = dup_ngx_str_to_apr(req->pool, &h[i].key);
+        if (key == NULL) {
+            return NGX_ERROR;
+        }
+        const char *value = dup_ngx_str_to_apr(req->pool, &h[i].value);
+        if (value == NULL) {
+            return NGX_ERROR;
+        }
+        apr_table_setn(req->headers_in, key, value);
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "ModSecurity: load headers in: \"%V: %V\"",
                        &h[i].key, &h[i].value);
@@ -349,18 +377,22 @@ ngx_http_modsecurity_load_headers_in(ngx_http_request_t *r)
     req->range = apr_table_get(req->headers_in, "Range");
     req->content_type = apr_table_get(req->headers_in, "Content-Type");
     req->content_encoding = apr_table_get(req->headers_in, "Content-Encoding");
+    req->hostname = apr_table_get(req->headers_in, "Host");
+    if (req->hostname == NULL) {
+        req->hostname = "<undefined>";
+    }
 
     lang = apr_table_get(ctx->req->headers_in, "Content-Languages");
     if(lang != NULL)
     {
-        ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *));
+        ctx->req->content_languages = apr_array_make(req->pool, 1, sizeof(const char *));
 
-        *(const char **)apr_array_push(ctx->req->content_languages) = lang;
+        *(const char **)apr_array_push(req->content_languages) = lang;
     }
 
     req->ap_auth_type = (char *)apr_table_get(req->headers_in, "Authorization");
 
-    req->user = (char *)ngx_pstrdup0(r->pool, &r->headers_in.user);
+    req->user = dup_ngx_str_to_apr(req->pool, &r->headers_in.user);
 
 
 
@@ -370,121 +402,6 @@ ngx_http_modsecurity_load_headers_in(ngx_http_request_t *r)
     return NGX_OK;
 }
 
-static ngx_inline ngx_int_t
-ngx_http_modsecurity_save_headers_in(ngx_http_request_t *r)
-{
-    ngx_http_modsecurity_ctx_t  *ctx;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
-
-    /* clean up headers_in */
-    ngx_memzero(&r->headers_in, sizeof(ngx_http_headers_in_t));
-
-    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
-                      sizeof(ngx_table_elt_t))
-            != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
-
-    if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
-                       sizeof(ngx_table_elt_t *))
-            != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
-    r->headers_in.content_length_n = -1;
-    r->headers_in.keep_alive_n = -1;
-
-    r->headers_in.headers.part.nelts = 0;
-    r->headers_in.headers.part.next = NULL;
-    r->headers_in.headers.last = &r->headers_in.headers.part;
-
-    /* shadow copy */
-    if (apr_table_do(ngx_http_modsecurity_save_headers_in_visitor,
-                     r, ctx->req->headers_in, NULL) == 0) {
-
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "ModSecurity: save headers in error");
-
-        return NGX_ERROR;
-    }
-
-    if (r->headers_in.content_length) {
-        r->headers_in.content_length_n =
-            ngx_atoof(r->headers_in.content_length->value.data,
-                      r->headers_in.content_length->value.len);
-
-        if (r->headers_in.content_length_n == NGX_ERROR) {
-            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                          "ModSecurity: invalid \"Content-Length\" header");
-            return NGX_ERROR;
-        }
-    }
-
-    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
-        if (r->headers_in.keep_alive) {
-            r->headers_in.keep_alive_n =
-                ngx_atotm(r->headers_in.keep_alive->value.data,
-                          r->headers_in.keep_alive->value.len);
-        }
-    }
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ModSecurity: save headers in done");
-
-    return NGX_OK;
-}
-
-
-static int
-ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const char *value)
-{
-    ngx_http_request_t         *r = data;
-    ngx_table_elt_t            *h;
-    ngx_http_header_t          *hh;
-    ngx_http_core_main_conf_t  *cmcf;
-
-    h = ngx_list_push(&r->headers_in.headers);
-    if (h == NULL) {
-        return 0;
-    }
-
-    h->key.data = (u_char *)key;
-    h->key.len = ngx_strlen(key);
-
-    h->value.data = (u_char *)value;
-    h->value.len = ngx_strlen(value);
-
-    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
-
-    if (h->lowcase_key == NULL) {
-        return 0;
-    }
-
-    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
-
-    h->hash = ngx_hash_key(h->lowcase_key, h->key.len);
-
-    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
-
-    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
-                       h->lowcase_key, h->key.len);
-
-    if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
-        return 0;
-    }
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ModSecurity: save headers in: \"%V: %V\"",
-                   &h->key, &h->value);
-
-    return 1;
-}
-
-
 static ngx_inline ngx_int_t
 ngx_http_modsecurity_load_request_body(ngx_http_request_t *r)
 {
@@ -496,327 +413,19 @@ ngx_http_modsecurity_load_request_body(ngx_http_request_t *r)
 
     if (r->request_body == NULL || r->request_body->bufs == NULL) {
 
-        return copy_chain_to_brigade(NULL, ctx->brigade, r->pool, 1);
-    }
-
-    if (copy_chain_to_brigade(r->request_body->bufs, ctx->brigade, r->pool, 1) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    return NGX_OK;
-}
-
-
-static ngx_inline ngx_int_t
-ngx_http_modsecurity_save_request_body(ngx_http_request_t *r)
-{
-    ngx_http_modsecurity_ctx_t    *ctx;
-    apr_off_t                      content_length;
-    ngx_buf_t                     *buf;
-    ngx_http_core_srv_conf_t      *cscf;
-    size_t                         size;
-    ngx_http_connection_t         *hc;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
-
-    apr_brigade_length(ctx->brigade, 0, &content_length);
-
-    if (r->header_in->end - r->header_in->last >= content_length) {
-        /* use r->header_in */
-
-        if (ngx_buf_size(r->header_in)) {
-            /* move to the end */
-            ngx_memmove(r->header_in->pos + content_length,
-                        r->header_in->pos,
-                        ngx_buf_size(r->header_in));
-        }
-
-        if (apr_brigade_flatten(ctx->brigade,
-                                (char *)r->header_in->pos,
-                                (apr_size_t *)&content_length) != APR_SUCCESS) {
-            return NGX_ERROR;
-        }
-
-        apr_brigade_cleanup(ctx->brigade);
-
-        r->header_in->last += content_length;
-
-        return NGX_OK;
-    }
-
-    if (ngx_buf_size(r->header_in)) {
-
-        /*
-         * ngx_http_set_keepalive will reuse r->header_in if
-         * (r->header_in != c->buffer && r->header_in.last != r->header_in.end),
-         * so we need this code block.
-         * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer
-         */
-        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
-        size = ngx_max(cscf->large_client_header_buffers.size,
-                       (size_t)content_length + ngx_buf_size(r->header_in));
-
-        hc = r->http_connection;
-
-#if defined(nginx_version) && nginx_version >= 1011011
-        if (hc->free && size == cscf->large_client_header_buffers.size) {
-
-            buf = hc->free->buf;
-#else
-        if (hc->nfree && size == cscf->large_client_header_buffers.size) {
-
-            buf = hc->free[--hc->nfree];
-#endif
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "ModSecurity: use http free large header buffer: %p %uz",
-                           buf->pos, buf->end - buf->last);
-
-        } else if (hc->nbusy < cscf->large_client_header_buffers.num) {
-
-            if (hc->busy == NULL) {
-                hc->busy = ngx_palloc(r->connection->pool,
-                                      cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
-            }
-
-            if (hc->busy == NULL) {
-                return NGX_ERROR;
-            } else {
-                buf = ngx_create_temp_buf(r->connection->pool, size);
-            }
-        } else {
-            /* TODO: how to deal this case ? */
-            return NGX_ERROR;
-        }
-
-    } else {
-
-        buf = ngx_create_temp_buf(r->pool, (size_t) content_length);
-    }
-
-    if (buf == NULL) {
-        return NGX_ERROR;
-    }
-
-    if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos,
-                            (apr_size_t *)&content_length) != APR_SUCCESS) {
-        return NGX_ERROR;
-    }
-
-    apr_brigade_cleanup(ctx->brigade);
-    buf->last += content_length;
-
-    ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in));
-    buf->last += ngx_buf_size(r->header_in);
-
-    r->header_in = buf;
-
-    return NGX_OK;
-}
-
-
-static ngx_inline ngx_int_t
-ngx_http_modsecurity_load_headers_out(ngx_http_request_t *r)
-{
-
-    ngx_http_modsecurity_ctx_t  *ctx;
-    char                        *data;
-    request_rec                 *req;
-    ngx_http_variable_value_t   *vv;
-    ngx_list_part_t             *part;
-    ngx_table_elt_t             *h;
-    ngx_uint_t                   i;
-    char                        *key, *value;
-    u_char                      *buf = NULL;
-    size_t                       size = 0;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
-    req = ctx->req;
-
-    req->status = r->headers_out.status;
-    req->status_line = (char *)ngx_pstrdup0(r->pool, &r->headers_out.status_line);
-
-    /* deep copy */
-    part = &r->headers_out.headers.part;
-    h = part->elts;
-
-    for (i = 0; ; i++) {
-        if (i >= part->nelts) {
-            if (part->next == NULL)
-                break;
-
-            part = part->next;
-            h = part->elts;
-            i = 0;
-        }
-        size += h[i].key.len + h[i].value.len + 2;
-
-        buf = ngx_palloc(r->pool, size);
-
-        if (buf == NULL) {
-            return NGX_ERROR;
-        }
-
-        key = (char *)buf;
-        buf = ngx_cpymem(buf, h[i].key.data, h[i].key.len);
-        *buf++ = '\0';
-
-        value = (char *)buf;
-        buf = ngx_cpymem(buf, h[i].value.data, h[i].value.len);
-        *buf++ = '\0';
-
-        apr_table_addn(req->headers_out, key, value);
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "ModSecurity: load headers out: \"%V: %V\"",
-                       &h[i].key, &h[i].value);
-
-    }
-
-    for (i = 0; special_headers_out[i].name; i++) {
-
-        vv = ngx_http_get_variable(r, &special_headers_out[i].variable_name,
-                                   ngx_hash_key(special_headers_out[i].variable_name.data,
-                                                special_headers_out[i].variable_name.len));
-
-        if (vv && !vv->not_found) {
-
-            data = ngx_palloc(r->pool, vv->len + 1);
-            if (data == NULL) {
-                return NGX_ERROR;
-            }
-
-            ngx_memcpy(data,vv->data, vv->len);
-            data[vv->len] = '\0';
-
-            apr_table_setn(req->headers_out, special_headers_out[i].name, data);
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "ModSecurity: load headers out: \"%s: %s\"",
-                           special_headers_out[i].name, data);
-        }
-    }
-
-    req->content_type = apr_table_get(ctx->req->headers_out, "Content-Type");
-    req->content_encoding = apr_table_get(ctx->req->headers_out, "Content-Encoding");
-
-    data = (char *)apr_table_get(ctx->req->headers_out, "Content-Languages");
-
-    if(data != NULL)
-    {
-        ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *));
-        *(const char **)apr_array_push(ctx->req->content_languages) = data;
-    }
-
-    /* req->chunked = r->chunked; may be useless */
-    req->clength = r->headers_out.content_length_n;
-    req->mtime = apr_time_make(r->headers_out.last_modified_time, 0);
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ModSecurity: load headers out done");
-
-    return NGX_OK;
-}
-
-
-static ngx_inline ngx_int_t
-ngx_http_modsecurity_save_headers_out(ngx_http_request_t *r)
-{
-    ngx_http_modsecurity_ctx_t      *ctx;
-    ngx_http_upstream_t             *upstream;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
-
-    /* r->chunked = ctx->req->chunked; */
-
-    ngx_http_clean_header(r);
-
-    upstream = r->upstream;
-    r->upstream = &ngx_http_modsecurity_upstream;
-
-    /* case SecServerSignature was used, the "Server: ..." header is added
-     * here, overwriting the default header supplied by nginx.
-     */
-    if (modsecIsServerSignatureAvailale() != NULL) {
-        apr_table_add(ctx->req->headers_out, "Server",
-                modsecIsServerSignatureAvailale());
+        return copy_chain_to_brigade(NULL, ctx->brigade, 1);
     }
 
-    if (apr_table_do(ngx_http_modsecurity_save_headers_out_visitor,
-                     r, ctx->req->headers_out, NULL) == 0) {
-
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "ModSecurity: save headers out error");
-
+    if (copy_chain_to_brigade(r->request_body->bufs, ctx->brigade, 1) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    r->upstream = upstream;
-
-    r->headers_out.status = ctx->req->status;
-    r->headers_out.status_line.data = (u_char *)ctx->req->status_line;
-    r->headers_out.status_line.len = ctx->req->status_line ?
-                                     ngx_strlen(ctx->req->status_line) : 0;
-
-    r->headers_out.content_length_n = ctx->req->clength;
-    r->headers_out.last_modified_time = apr_time_sec(ctx->req->mtime);
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ModSecurity: save headers out done");
-
     return NGX_OK;
 }
 
-
-static int
-ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const char *value)
-{
-    ngx_http_request_t             *r = data;
-    ngx_table_elt_t                *h, he;
-    ngx_http_upstream_header_t     *hh;
-    ngx_http_upstream_main_conf_t  *umcf;
-
-    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-
-    h = &he;
-
-    h->key.data = (u_char *)key;
-    h->key.len = ngx_strlen(key);
-
-    h->value.data = (u_char *)value;
-    h->value.len = ngx_strlen(value);
-
-    h->lowcase_key = ngx_palloc(r->pool, h->key.len);
-    if (h->lowcase_key == NULL) {
-        return 0;
-    }
-
-    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
-
-    h->hash = ngx_hash_key(h->lowcase_key, h->key.len);
-
-    hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
-                       h->lowcase_key, h->key.len);
-
-    if (hh) {
-        /* copy all */
-        if (hh->copy_handler(r, h, hh->conf) != NGX_OK) {
-            return 0;
-        }
-    }
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ModSecurity: save headers out: \"%V: %V\"",
-                   &h->key, &h->value);
-
-    return 1;
-}
-
-
 static ngx_inline ngx_int_t
-ngx_http_modsecurity_status(ngx_http_request_t *r, int status)
+ngx_http_modsecurity_status(int status)
 {
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status %d", status);
-
     if (status == DECLINED || status == APR_SUCCESS) {
         return NGX_DECLINED;
     }
@@ -907,6 +516,7 @@ modsec_pcre_free(void *ptr)
 }
 
 static server_rec *modsec_server = NULL;
+static ngx_http_modsecurity_config_cache_t *config_cache = NULL;
 
 static ngx_int_t
 ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf)
@@ -933,6 +543,12 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf)
     modsec_server->server_hostname[ ngx_cycle->hostname.len] = '\0';
 
     modsecStartConfig();
+
+    config_cache = ngx_http_modsecurity_config_cache_init(cf);
+    if (config_cache == NULL) {
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
@@ -944,6 +560,9 @@ ngx_http_modsecurity_terminate(ngx_cycle_t *cycle)
         modsecTerminate();
         modsec_server = NULL;
     }
+
+    /* Configuration cache is allocated from a pool and does not need explicit freeing */
+    config_cache = NULL;
 }
 
 
@@ -978,7 +597,12 @@ ngx_http_modsecurity_init(ngx_conf_t *cf)
     extern pthread_mutex_t msc_pregcomp_ex_mtx;
     pthread_mutex_init(&msc_pregcomp_ex_mtx, NULL);
 
-    init_appgw_rules_id_hash();
+#ifdef WAF_JSON_LOGGING_ENABLE
+    int result = init_appgw_rules_id_hash();
+    if (result) {
+        ngx_log_error(NGX_LOG_INFO, cf->log, 0, "ModSecurity: could not load application gateway rules IDs.");
+    }
+#endif    
 
     return NGX_OK;
 }
@@ -995,11 +619,11 @@ ngx_http_modsecurity_init_process(ngx_cycle_t *cycle)
 
 
 static void
-ngx_http_modsecurity_thread_func(void *data, ngx_log_t *log)
+ngx_http_modsecurity_prevention_thread_func(void *data, ngx_log_t *log)
 {
     // Executed in a separate thread
 
-    ngx_http_modsecurity_thread_ctx_t *thread_ctx = data;
+    ngx_http_modsecurity_prevention_thread_ctx_t *thread_ctx = data;
     ngx_http_request_t *r = thread_ctx->r;
 
     ngx_http_modsecurity_ctx_t* mod_ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
@@ -1013,7 +637,9 @@ ngx_http_modsecurity_thread_func(void *data, ngx_log_t *log)
     }
 
     // Processing request headers
-    ngx_int_t rc = ngx_http_modsecurity_status(r, modsecProcessRequestHeaders(mod_ctx->req));
+    ngx_int_t rc = modsecProcessRequestHeaders(mod_ctx->req);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity process headers: status %d", rc);
+    rc = ngx_http_modsecurity_status(rc);
 
     if (rc != NGX_DECLINED) {
         mod_ctx->status_code = rc;
@@ -1033,13 +659,14 @@ ngx_http_modsecurity_thread_func(void *data, ngx_log_t *log)
     }
 
     // The name of modsecProcessRequestBody is a bit misleading. This function call is needed even to just process GET args.
-    rc = ngx_http_modsecurity_status(r, modsecProcessRequestBody(mod_ctx->req));
-    mod_ctx->status_code = rc;
+    rc = modsecProcessRequestBody(mod_ctx->req);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity process body: status %d", rc);
+    mod_ctx->status_code = ngx_http_modsecurity_status(rc);
 }
 
 
 static void
-ngx_http_modsecurity_thread_completion(ngx_event_t *ev)
+ngx_http_modsecurity_prevention_thread_completion(ngx_event_t *ev)
 {
     // executed in nginx event loop after thread task is done, in order to pick up and continue processing request
 
@@ -1056,13 +683,52 @@ ngx_http_modsecurity_thread_completion(ngx_event_t *ev)
 }
 
 
+static void
+ngx_http_modsecurity_detection_thread_func(void *data, ngx_log_t *log)
+{
+    // Executed in a separate thread
+    ngx_http_modsecurity_detection_thread_ctx_t *thread_ctx = data;
+
+    // Processing request headers
+    ngx_int_t rc = ngx_http_modsecurity_status(modsecProcessRequestHeaders(thread_ctx->req));
+    if (rc != NGX_DECLINED) {
+        return;
+    }
+
+    if (modsecContextState(thread_ctx->req) == MODSEC_DISABLED) {
+        return;
+    }
+
+    // The name of modsecProcessRequestBody is a bit misleading. This function call is needed even to just process GET args.
+    modsecProcessRequestBody(thread_ctx->req);
+}
+
+
+static void
+ngx_http_modsecurity_detection_thread_completion(ngx_event_t *ev)
+{
+    // executed in nginx event loop after thread task is done, in order to pick up and continue processing request
+    ngx_http_modsecurity_detection_thread_ctx_t *ctx = ev->data;
+
+    request_rec *req = ctx->req;
+    conn_rec *connection = ctx->connection;
+
+    if (req != NULL) {
+        modsecFinishRequest(req);
+    }
+    if (connection != NULL) {
+        modsecFinishConnection(connection);
+    }
+}
+
+
 static ngx_int_t
-ngx_http_modsecurity_task_offload(ngx_http_request_t *r)
+ngx_http_modsecurity_prevention_task_offload(ngx_http_request_t *r)
 {
-    ngx_http_modsecurity_thread_ctx_t *thread_ctx;
+    ngx_http_modsecurity_prevention_thread_ctx_t *thread_ctx;
     ngx_thread_task_t *task;
 
-    task = ngx_thread_task_alloc(r->pool, sizeof(ngx_http_modsecurity_thread_ctx_t));
+    task = ngx_thread_task_alloc(r->pool, sizeof(ngx_http_modsecurity_prevention_thread_ctx_t));
     if (task == NULL) {
         return NGX_ERROR;
     }
@@ -1070,8 +736,8 @@ ngx_http_modsecurity_task_offload(ngx_http_request_t *r)
     thread_ctx = task->ctx;
     thread_ctx->r = r;
 
-    task->handler = ngx_http_modsecurity_thread_func;
-    task->event.handler = ngx_http_modsecurity_thread_completion;
+    task->handler = ngx_http_modsecurity_prevention_thread_func;
+    task->event.handler = ngx_http_modsecurity_prevention_thread_completion;
     task->event.data = r;
 
     ngx_thread_pool_t* thread_pool = ngx_thread_pool_get((ngx_cycle_t *)ngx_cycle, &thread_pool_name);
@@ -1085,6 +751,52 @@ ngx_http_modsecurity_task_offload(ngx_http_request_t *r)
     return NGX_OK;
 }
 
+static ngx_int_t
+ngx_http_modsecurity_detection_task_offload(ngx_http_request_t *r)
+{
+    ngx_http_modsecurity_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
+    request_rec *req = ctx->req;
+
+    // Load request to request rec
+    if (ngx_http_modsecurity_load_request(r) != NGX_OK ||
+            ngx_http_modsecurity_load_headers_in(r) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (modsecIsRequestBodyAccessEnabled(req) && (r->headers_in.content_length || r->headers_in.chunked)) {
+        if (ngx_http_modsecurity_load_request_body(r) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    ngx_thread_task_t *task = apr_thread_task_alloc(req->pool, sizeof(ngx_http_modsecurity_detection_thread_ctx_t));
+    if (task == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_modsecurity_detection_thread_ctx_t *thread_ctx = task->ctx;
+    thread_ctx->connection = ctx->connection;
+    thread_ctx->req = ctx->req;
+    thread_ctx->brigade = ctx->brigade;
+
+    task->handler = ngx_http_modsecurity_detection_thread_func;
+    task->event.handler = ngx_http_modsecurity_detection_thread_completion;
+    task->event.data = thread_ctx;
+
+    ngx_thread_pool_t* thread_pool = ngx_thread_pool_get((ngx_cycle_t *)ngx_cycle, &thread_pool_name);
+    if (ngx_thread_task_post(thread_pool, task) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    // We need to reset these so they don't get cleaned up when the request is complete.
+    // This is important as the background task can outlive the request and we need to
+    // make sure the memory it uses is still alive.
+    ctx->connection = NULL;
+    ctx->req = NULL;
+    ctx->brigade = NULL;
+
+    return NGX_OK;
+}
 
 static void
 ngx_http_modsecurity_body_handler(ngx_http_request_t *r)
@@ -1149,8 +861,17 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
         return NGX_DECLINED;
     }
 
+    /* When in detection mode, don't wait for background task to complete because it won't affect the request status */
+    if (cf->config->is_enabled == MODSEC_DETECTION_ONLY) {
+        if (ngx_http_modsecurity_detection_task_offload(r) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_DECLINED;
+    }
+
     ctx->thread_running = 1;
-    if (ngx_http_modsecurity_task_offload(r) != NGX_OK) {
+    if (ngx_http_modsecurity_prevention_task_offload(r) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -1197,9 +918,9 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
         ctx->connection = modsecNewConnection();
 
         /* fill apr_sockaddr_t */
-        asa = ngx_palloc(r->pool, sizeof(apr_sockaddr_t));
+        asa = apr_palloc(ctx->connection->pool, sizeof(apr_sockaddr_t));
         asa->pool = ctx->connection->pool;
-        asa->hostname = (char *)ngx_pstrdup0(r->pool, &r->connection->addr_text);
+        asa->hostname = dup_ngx_str_to_apr(asa->pool, &r->connection->addr_text);
         asa->servname = asa->hostname;
         asa->next = NULL;
         asa->salen = r->connection->socklen;
@@ -1243,7 +964,9 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
 
     ctx->req = modsecNewRequest(ctx->connection, cf->config);
 
-    apr_table_setn(ctx->req->notes, NOTE_NGINX_REQUEST_CTX, (const char *) ctx);
+    if (cf->config->is_enabled != MODSEC_DETECTION_ONLY) {
+        apr_table_setn(ctx->req->notes, NOTE_NGINX_REQUEST_CTX, (const char *)ctx);
+    }
     apr_generate_random_bytes(salt, TXID_SIZE);
 
     txid = apr_pcalloc (ctx->req->pool, TXID_SIZE);
@@ -1307,6 +1030,11 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         return NGX_CONF_ERROR;
     }
 
+    mscf->config = ngx_http_modsecurity_config_cache_lookup(config_cache, &value[1]);
+    if (mscf->config != NULL) {
+        return NGX_CONF_OK;
+    }
+
     mscf->config = modsecGetDefaultConfig();
 
     if (mscf->config == NULL) {
@@ -1320,6 +1048,12 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         return NGX_CONF_ERROR;
     }
 
+    ngx_int_t result = ngx_http_modsecurity_config_cache_insert(config_cache, cf, &value[1], mscf->config);
+    if (result != NGX_OK) {
+        ngx_log_error(NGX_LOG_WARN, cf->log, 0, "Couldn't cache config in %s:%ui: %s",
+                cf->conf_file->file.name.data, cf->conf_file->line, msg);
+    }
+
     return NGX_CONF_OK;
 }
 
diff --git a/nginx/modsecurity/ngx_http_modsecurity_config_cache.c b/nginx/modsecurity/ngx_http_modsecurity_config_cache.c
new file mode 100644
index 0000000000..040c85a3e5
--- /dev/null
+++ b/nginx/modsecurity/ngx_http_modsecurity_config_cache.c
@@ -0,0 +1,126 @@
+#include "ngx_http_modsecurity_config_cache.h"
+
+typedef struct {
+    // Must be the first member of the structure because of pointer casts 
+    // used by insert/lookup routines.
+    ngx_rbtree_node_t         rbnode;
+
+    ngx_str_t                 path;
+    directory_config          *config;
+} ngx_http_modsecurity_config_cache_node_t;
+
+/*
+ * Implemented similarly to ngx_string.c#ngx_str_rbtree_insert_value()
+ */
+static void
+ngx_http_modsecurity_config_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t  **p = NULL;
+    for ( ;; ) {
+
+        ngx_http_modsecurity_config_cache_node_t *n = (ngx_http_modsecurity_config_cache_node_t*) node;
+        ngx_http_modsecurity_config_cache_node_t *t = (ngx_http_modsecurity_config_cache_node_t*) temp;
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (n->path.len != t->path.len) {
+
+            p = (n->path.len < t->path.len) ? &temp->left : &temp->right;
+
+        } else {
+            p = (ngx_strncasecmp(n->path.data, t->path.data, n->path.len) < 0)
+                 ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+ngx_http_modsecurity_config_cache_t *ngx_http_modsecurity_config_cache_init(ngx_conf_t *cf)
+{
+    ngx_rbtree_node_t *sentinel = ngx_pcalloc(cf->pool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NULL;
+    }
+
+    ngx_http_modsecurity_config_cache_t *cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_modsecurity_config_cache_t));
+    if (cache == NULL) {
+        return NULL;
+    }
+
+    ngx_rbtree_init(&cache->rbtree, sentinel, ngx_http_modsecurity_config_cache_rbtree_insert_value);
+    return cache;
+}
+
+ngx_int_t ngx_http_modsecurity_config_cache_insert(
+    ngx_http_modsecurity_config_cache_t* cache, ngx_conf_t *cf,
+    ngx_str_t *path, directory_config *config)
+{
+    ngx_http_modsecurity_config_cache_node_t *node = ngx_pcalloc(cf->pool, sizeof(ngx_http_modsecurity_config_cache_node_t));
+    if (node == NULL) {
+        return NGX_ERROR;
+    }
+
+    node->path.data = ngx_pstrdup(cf->pool, path);
+    node->path.len = path->len;
+    node->rbnode.key = ngx_crc32_long(path->data, path->len);
+    node->config = config;
+
+    ngx_rbtree_insert(&cache->rbtree, &node->rbnode);
+    return NGX_OK;
+}
+
+/*
+ * Implemented similarly to ngx_string.c#ngx_str_rbtree_lookup()
+ */
+directory_config *ngx_http_modsecurity_config_cache_lookup(
+    ngx_http_modsecurity_config_cache_t *cache, ngx_str_t *path)
+{
+    ngx_uint_t hash = ngx_crc32_long(path->data, path->len);
+    ngx_rbtree_node_t *node = cache->rbtree.root;
+    ngx_rbtree_node_t *sentinel = cache->rbtree.sentinel;
+
+    while (node != sentinel) {
+        ngx_http_modsecurity_config_cache_node_t *n = (ngx_http_modsecurity_config_cache_node_t *) node;
+
+        if (hash != node->key) {
+            node = (hash < node->key) ? node->left : node->right;
+            continue;
+        }
+
+        if (path->len != n->path.len) {
+            node = (path->len < n->path.len) ? node->left : node->right;
+            continue;
+        }
+
+        ngx_int_t rc = ngx_memcmp(path->data, n->path.data, path->len);
+
+        if (rc < 0) {
+            node = node->left;
+            continue;
+        }
+
+        if (rc > 0) {
+            node = node->right;
+            continue;
+        }
+
+        return n->config;
+    }
+
+    return NULL;
+
+}
diff --git a/nginx/modsecurity/ngx_http_modsecurity_config_cache.h b/nginx/modsecurity/ngx_http_modsecurity_config_cache.h
new file mode 100644
index 0000000000..7102198914
--- /dev/null
+++ b/nginx/modsecurity/ngx_http_modsecurity_config_cache.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <ngx_core.h>
+#include <ngx_string.h>
+
+/*
+ * ModSecurity configuration cache is used to prevent loading configuration
+ * multiple times if one and the same configuration file is applied in multiple
+ * different contexts.
+ *
+ * The main problem with loading ModSecurity configuration independently for each 
+ * context is that the allocated memory block can be quite large (10-20 Mb) so if, 
+ * for example, the same configuration is used for 10-20 listeners it will cause 
+ * significant memory overuse for no good reason.
+ *
+ * The cache helps alleviate this by storing pointers to configuration objects created
+ * from ModSecurity configuration files and re-using existing objects when the same path
+ * occurs several times during Nginx configuration loading.
+ */
+
+typedef struct directory_config directory_config;
+
+typedef struct {
+    ngx_rbtree_t              rbtree;
+} ngx_http_modsecurity_config_cache_t;
+
+/* Create configuration cache */
+ngx_http_modsecurity_config_cache_t *ngx_http_modsecurity_config_cache_init(ngx_conf_t *cf);
+
+/* Add configuration block for the respective file path into configuration cache */
+ngx_int_t ngx_http_modsecurity_config_cache_insert(ngx_http_modsecurity_config_cache_t *cache,
+    ngx_conf_t *cf, ngx_str_t *path, directory_config *config);
+
+/* Look up configuration block for the respective file path in configuration cache */
+directory_config *ngx_http_modsecurity_config_cache_lookup(ngx_http_modsecurity_config_cache_t *cache,
+    ngx_str_t *path);
diff --git a/standalone/Makefile.am b/standalone/Makefile.am
index 33afd377e4..d3623db6ce 100644
--- a/standalone/Makefile.am
+++ b/standalone/Makefile.am
@@ -5,7 +5,6 @@ pkglib_LTLIBRARIES = standalone.la
 standalone_la_SOURCES = ../apache2/acmp.c \
     ../apache2/ag_mdb/murmur3.c \
     ../apache2/ag_mdb/ag_mdb.cpp \
-    ../apache2/waf_logging/waf_format.pb.cc \
     ../apache2/waf_logging/waf_log_util.cc \
     ../apache2/waf_lock/waf_lock.cpp \
     ../apache2/apache2_config.c \
diff --git a/tests/config.py b/tests/config.py
index be06412a0f..5b93eb99fa 100644
--- a/tests/config.py
+++ b/tests/config.py
@@ -23,4 +23,4 @@
 #
 # Reverse format: (example: see above)
 #log_date_format = "%Y-%m-%d %H:%M:%S.%f"
-failing_tests = ['920100-10', '920100-12', '920100-14', '920100-2', '920100-4', '920100-6', '920100-8', '920160-4', '920170-5', '920170-6', '920180-1', '920180-3', '920200-3', '920200-4', '920200-7', '920200-9', '920210-5', '920220-2', '920240-2', '920260-2', '920270-6', '920271-5', '920271-6', '920272-4', '920273-2', '920273-4', '920274-5', '920290-1', '920310-2', '920310-5', '920311-2', '920320-2', '920330-2', '920350-2', '920420-9', '920430-8', '920450-5', '920450-8', '920470-4', '920470-5', '932100-3', '933110-11', '933110-19', '933130-3', '933130-6', '933151-3', '933151-5', '933160-22', '933161-5', '941100-4', '941120-1', '941160-1FN', '941310-1', '942420-1', '943110-4', '944100-11', '944100-12', '944100-15', '944100-16', '944110-11', '944110-12', '944110-15', '944110-16', '944120-107', '944120-108', '944120-124', '944120-125', '944120-22', '944120-23', '944120-39', '944120-40', '944120-5', '944120-56', '944120-57', '944120-6', '944120-73', '944120-74', '944120-90', '944120-91', '944210-22', '944210-23', '944210-39', '944210-40', '944210-5', '944210-6', '920200-10', '920240-3', '920270-7', '920310-3', '920310-6', '920470-6', '942480-2', '920220-3', '941100-5FN', '941110-2', '933160-21', '920311-3']
+failing_tests = ['920100-10', '920100-12', '920100-14', '920100-2', '920100-4', '920100-6', '920100-8', '920160-4', '920170-5', '920170-6', '920180-1', '920180-3', '920200-3', '920200-4', '920200-7', '920200-9', '920210-5', '920220-2', '920240-2', '920260-2', '920270-6', '920271-5', '920271-6', '920272-4', '920273-2', '920273-4', '920274-5', '920290-1', '920310-2', '920310-5', '920311-2', '920320-2', '920330-2', '920350-2', '920420-9', '920430-8', '920450-5', '920450-8', '920470-4', '920470-5', '932100-3', '933110-11', '933110-19', '933130-3', '933130-6', '933151-3', '933151-5', '933160-22', '933161-5', '941100-4', '941120-1', '941160-1FN', '941310-1', '942420-1', '943110-4', '944100-11', '944100-12', '944100-15', '944100-16', '944110-11', '944110-12', '944110-15', '944110-16', '944120-107', '944120-108', '944120-124', '944120-125', '944120-22', '944120-23', '944120-39', '944120-40', '944120-5', '944120-56', '944120-57', '944120-6', '944120-73', '944120-74', '944120-90', '944120-91', '944210-22', '944210-23', '944210-39', '944210-40', '944210-5', '944210-6', '920200-10', '920240-3', '920270-7', '920310-3', '920310-6', '920470-6', '942480-2', '920220-3', '941100-5FN', '941110-2', '933160-21', '920311-3', '921110-5', '942500-1']
