# Reverse proxy - how to log http-content of response?

**URL:** <https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215>\
**Category:** NGINX\
**Tags:** reverse-proxy, config\
**Created:** [March 27, 2026, 11:20am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215 "2026-03-27T11:20:50Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 27, 2026, 11:20am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/1 "2026-03-27T11:20:50Z")

</div>

_After setting up the reverse proxy I’d like to log the response._

**My issue:**

I’m doing some reverse engineering of [radicale CalDAV](https://radicale.org). It operates within a docker-container and reverse proxy nginx is passing requests there. After [getting symlinks right](https://community.nginx.org/t/reverse-proxy-does-not-write-access-log-as-expected/8958/2) I get a nice access-log.

But now I’d like to see (read) radicale’s replies. Is there a way to make nginx log what the radicale-container replies?

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 28, 2026, 5:26pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/2 "2026-03-28T17:26:08Z")

</div>

You can do this with the JavaScript module (njs) which has a convenient [js\_body\_filter](https://nginx.org/en/docs/http/ngx_http_js_module.html#js_body_filter) directive that can be used to create a variable with the response as it is streamed back from the upstream server.

You could either dump the response directly into the error log with njs or create a variable that can be escaped and included in the access log.

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 28, 2026, 5:49pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/3 "2026-03-28T17:49:26Z")

</div>

Forgot I had written an entire blog about this. It’s overkill for what you want, but includes the response body logging.

> **[Diagnostic Logging with the NGINX JavaScript Module](https://www.f5.com/company/blog/nginx/diagnostic-logging-nginx-javascript-module)**
>
> We show how to use the NGINX JavaScript module to capture data about requests that cause errors, with enough details to be useful for debugging and troubleshooting, while not cluttering the log with this information about requests that didn't...

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 29, 2026, 5:44pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/4 "2026-03-29T17:44:39Z")

</div>

Thx, I had a go on your suggestion but ended up with some js-foo.

`root@pi5:/home/pi# nginx -t `  
`2026/03/29 19:45:18 [emerg] 1568759#1568759: unknown directive "js_import" in /etc/nginx/sites-enabled/default.conf:1`  
`nginx: configuration file /etc/nginx/nginx.conf test failed `  
`root@pi5:/home/pi# nginx -v `  
`nginx version: nginx/1.22.1`

An old-fashioned directive `js_include` was just the same.

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 29, 2026, 6:13pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/5 "2026-03-29T18:13:23Z")

</div>

That means you don’t have the njs module loaded. If your nginx is in a container based on the Docker Official Image then you need to add to your nginx.conf (top-level, main context)

```nginx
load_module modules/ngx_http_js_module.so;

```

If not, then see if you already have **ngx\_http\_js\_module.so** on your system.  
If not, you should be able to get it from your package manager.

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 29, 2026, 9:00pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/6 "2026-03-29T21:00:18Z")

</div>

I should have mentioned my architecture `binary-armhf` that stops me with … `Skipping acquire of configured file 'nginx/binary-armhf/Packages' as repository 'http://nginx.org/packages/debian bookworm InRelease' doesn’t support architecture ‘armhf’`

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 30, 2026, 8:08am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/7 "2026-03-30T08:08:05Z")

</div>

How do you feel about building the njs module from source then? 🙂

There’s a [script](https://github.com/nginx/pkg-oss/blob/159308edf6fe432f9a1758ab245713cd7870fb20/build_module.sh) for that

```shell
build_module.sh -v 1.22.1 https://github.com/nginx/njs.git
```

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 30, 2026, 9:31am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/8 "2026-03-30T09:31:16Z")

</div>

Thank you for your support. Script looked well until …

```auto
build_module.sh: INPUT: Enter module nickname [njs]:
build_module.sh: INFO: Creating /tmp/build_module.sh.1663126 build area
build_module.sh: INFO: Cloning module source
Cloning into ‘/tmp/build_module.sh.1663126/njs’…
remote: Enumerating objects: 19449, done.
remote: Counting objects: 100% (997/997), done.
remote: Compressing objects: 100% (481/481), done.
remote: Total 19449 (delta 728), reused 516 (delta 516), pack-reused 18452 (from 3)
Receiving objects: 100% (19449/19449), 22.20 MiB | 7.35 MiB/s, done.
Resolving deltas: 100% (15317/15317), done.
build_module.sh: ERROR: Cannot locate module config file - quitting

```

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 30, 2026, 10:24am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/9 "2026-03-30T10:24:23Z")

</div>

OK! Let’s do it the hard way 🙂

Install the build tools

```shell
# apt-get install wget git gcc make libpcre2-dev zlib1g-dev libssl-dev libxslt1-dev

```

Clone the njs repo

```shell
$ git clone https://github.com/nginx/njs

```

Get the nginx sources that match the version you have installed. This way we don’t modify your existing nginx install in any way.

```shell
$ wget -O - http://nginx.org/download/`nginx -v 2>&1 | tr / - | cut -f3 -d' '`.tar.gz | tar xfz -

```

Build the njs module

```shell
$ cd nginx-*
$ ./configure --with-compat --add-dynamic-module=../njs/nginx
$ make modules

```

You’ll find two modules (.so files for http\_js and stream\_js) in the **objs** directory. Copy them to **/etc/nginx/modules**

Don’t forget to add the `load_module` directive to point at the http\_js\_module file.

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 30, 2026, 11:31am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/10 "2026-03-30T11:31:34Z")

</div>

Uh, always coming back with pointless errors such as

```auto
root@pi5:~/njs-repo# wget -O - http://nginx.org/download/`nginx -v 2>&1 | tr / - | cut -f3 -d' '`.tar.gz | tar xfz
tar: Old option 'f' requires an argument.
Try 'tar --help' or 'tar --usage' for more information.
--2026-03-30 13:28:29-- http://nginx.org/download/nginx-1.28.3.tar.gz
Resolving nginx.org (nginx.org)... 3.125.197.172, 2a05:d014:5c0:2600::6, 2a05:d014:5c0:2601::6
Connecting to nginx.org (nginx.org)|3.125.197.172|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1284562 (1.2M) [application/octet-stream]
Saving to: ‘STDOUT’
- 0%[] 0 --.-KB/s in 0s      
Cannot write to ‘-’ (Broken pipe).

```

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 30, 2026, 11:47am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/11 "2026-03-30T11:47:53Z")

</div>

Oops - copy/paste error on my end. Needs a trailing `-` on the tar command to read from stdin. I’ve edited the post with the fix.

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 30, 2026, 12:08pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/12 "2026-03-30T12:08:28Z")

</div>

Wow, thanks. It worked but just wrote `ngx_http_js_module.so` into `objs`.

But my brave little `nginx` is fine.

```auto
root@pi5:/etc/nginx# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@pi5:/etc/nginx# service nginx restart
root@pi5:/etc/nginx# service nginx status
● nginx.service - nginx - high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-03-30 14:11:05 CEST; 4s ago
       Docs: https://nginx.org/en/docs/
    Process: 1685167 ExecStart=/usr/sbin/nginx -c ${CONFFILE} (code=exited, status=0/SUCCESS)
   Main PID: 1685168 (nginx)
      Tasks: 5 (limit: 19359)
        CPU: 39ms
     CGroup: /system.slice/nginx.service
             ├─1685168 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ├─1685169 "nginx: worker process"
             ├─1685170 "nginx: worker process"
             ├─1685171 "nginx: worker process"
             └─1685172 "nginx: worker process"

Mar 30 14:11:05 pi5 systemd[1]: Starting nginx.service - nginx - high performance web server...
Mar 30 14:11:05 pi5 systemd[1]: Started nginx.service - nginx - high performance web server.

```

Thank you!

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 30, 2026, 12:53pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/13 "2026-03-30T12:53:37Z")

</div>

Yay! That’s awesome - I guessed it was a Raspberry Pi 🙂

Good luck with the body read…

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 30, 2026, 2:05pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/14 "2026-03-30T14:05:29Z")

</div>

Hey Lian,

you’re right, I’m trying to pimp my Raspberry. Though accessing replies’ content is almost done there is just the next JavaScript-related issue. I modified [your example](https://www.f5.com/company/blog/nginx/harnessing-power-convenience-of-javascript-for-each-request-with-nginx-javascript-module) to

```auto
function kvAccess(r) {
    var log = `${r.variables.time_iso8601} client=${r.remoteAddress} method=${r.method} uri=${r.uri} status=${r.status}`;
    // r.rawHeadersIn.forEach(h => log += ` in.${h[0]}=${h[1]}`);
    // r.rawHeadersOut.forEach(h => log += ` out.${h[0]}=${h[1]}`);
    // log += r.responseText;
    log += ` Response body=${r.responseBody}`;
    log += ` Response buffer=${r.responseBuffer}`;
    log += ` Response text=${r.responseText}`;
    return log;
}

export default { kvAccess }

```

but get this log

```auto
2026-03-30T15:52:03+02:00 client=192.168.178.40 method=GET uri=/robots.txt status=404 Response body=undefined Response buffer=undefined Response text=undefined
2026-03-30T15:52:06+02:00 client=192.168.178.40 method=GET uri=/ status=302 Response body=undefined Response buffer=undefined Response text=undefined
2026-03-30T15:52:06+02:00 client=192.168.178.40 method=GET uri=/.web status=302 Response body=undefined Response buffer=undefined Response text=undefined
2026-03-30T15:52:06+02:00 client=192.168.178.40 method=GET uri=/.web/ status=200 Response body=undefined Response buffer=undefined Response text=undefined
2026-03-30T15:52:11+02:00 client=192.168.178.40 method=GET uri=/.web/fn.js status=200 Response body=undefined Response buffer=undefined Response text=undefined

```

It looks like `njs` does _not_ find [`responseText`](https://nginx.org/en/docs/njs/reference.html#r_response_body). Do I have to apply further configuration while loading `ngx_http_js_module.so`?

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 31, 2026, 9:11am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/15 "2026-03-31T09:11:38Z")

</div>

`responseText` is only populated when there has been a `r.subrequest()` call.

I apologise that the blog post wasn’t as complete as I remembered. We’ll need to extend the code/config a little.

In the location with the `proxy_pass` directive, add the following to intercept the response body and pass it to a JS function.

```nginx
js_body_filter main.getResponseBody; # Change the module name from 'main' if different

```

Extend the .js file to include a new function that appends a new variable `res` as the response is streamed back to the client.

```js
var res = '';
function getResponseBody(r, data, flags) {
    res += data;
    r.sendBuffer(data, flags);
}

function kvAccess(r) {
    var log = `${r.variables.time_iso8601} client=${r.remoteAddress} method=${r.method} uri=${r.uri} status=${r.status}`;
    // r.rawHeadersIn.forEach(h => log += ` in.${h[0]}=${h[1]}`);
    // r.rawHeadersOut.forEach(h => log += ` out.${h[0]}=${h[1]}`);
    log += ` response=${res}`; // Now we can log the response
    return log;
}

export default { getResponseBody, kvAccess }

```

Not tested -but should get you pretty close.

---

<div class="post-metadata">

**Author:** ![ChrisCross](https://avatars.discourse-cdn.com/v4/letter/c/91b2a8/32.png) [@ChrisCross](https://community.nginx.org/u/ChrisCross)\
**Post date:** [March 31, 2026, 11:24am UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/16 "2026-03-31T11:24:51Z")

</div>

Too bad I’m lacking another solution-button. It works perfectly fine now. I get responses such as `2026-03-31T13:16:29+02:00 client=192.168.178.40 method=PROPFIND uri=/user/data/ status=207 response=\x1F\xEF\xBF\xBD\x08\x00\x00\x00\x00\x00\x00\x03\xEF\xBF\xBDT[o\xEF\xBF` which needs a bit of decoding.

Thank you!

---

<div class="post-metadata">

**Author:** ![liam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/liam/32/99_2.png) [@liam](https://community.nginx.org/u/liam)\
**Post date:** [March 31, 2026, 4:44pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/17 "2026-03-31T16:44:02Z")

</div>

Glad you got something! The `PROPFIND` method might also help you, as an extension of WebDAV. Good luck!

---

<div class="post-metadata">

**Author:** ![system](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/system/32/19_2.png) [@system](https://community.nginx.org/u/system)\
**Post date:** [April 7, 2026, 4:44pm UTC](https://community.nginx.org/t/reverse-proxy-how-to-log-http-content-of-response/9215/18 "2026-04-07T16:44:54Z")

</div>

Closed on April 7, 2026, 4:44pm UTC

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.
