nginx JSON hacks


At DuckDuckGo we use a lot of nginx (an awesome Web server) and a lot of JSON, both for our own API and for processing external APIs. Here are some hacks we've been using.

Proxy external JSON calls

You can take an external API and run it through your server instead of letting the client call it directly. 

location ^~ /ext_api2/ {

That means a request for

will turn into

Setting up a proxy can yield a number of benefits...

Proxy caching

proxy_cache_path  /tmp/nginx_cache levels=1:2 keys_zone=STATIC:64m inactive=60m max_size=128m;
proxy_cache STATIC;
proxy_cache_valid 200 204 302 1d;

Now it won't hit the external API if the same request is called by multiple clients. If it is a pay API this could save you money and it could also just speed up the responsiveness of your site.

Proxy timeouts

proxy_connect_timeout 5;
proxy_read_timeout 5;
proxy_send_timeout 5;

You can set the timeouts per proxy (or globally), thus controlling how long the client will wait for each request. With timeouts in place you can ensure the page doesn't hang on something, eventually loads, and gracefully degrades the way you want it to -- even for components you don't control.

Strip headers

proxy_hide_header Set-Cookie;

Some external APIs like to do things to clients (like set cookies). You can protect your users from that by stripping them (or other headers). 

Reset headers

proxy_set_header Referer;

Similarly, you can reset your headers. This can protect privacy by zeroing out search terms (in the case of the Referrer), but you can also set custom headers.

Hide private API keys

Many APIs require use of a key, which you generally don't want to expose client-side. You can still allow for client-side calls by proxying them and then having nginx add the key.

location ^~ /ext_api5/ {
  rewrite ^/ext_api5/(.*) /api/check/$1/key/e95fad09aa5091b7734d1a268b53cef5  break;

Now a request for

will turn into

Turn JSON into JSONP

JSONP is a slight modification of JSON where the object is wrapped in a callback function usually specified by you. For example, say you grab a JSON object from somewhere that looks like this:

{"Name": "Cheeso", "Id" : 1823, "Rank": 7}

With JSONP you specify a callback function like 'parseResponse' and then it looks like this:

parseResponse({"Name": "Cheeso", "Id" : 1823, "Rank": 7})

This is useful for two reasons. First, the function will be called automatically when it is done loading. Second, it allows you to get around cross-domain errors.

If the above API was yours it's easy to call within a client-side script. But if it isn't yours, i.e. on another domain, and you try to call it you'll often get lots of cross-domain errors. The way around this is to use JSONP. Then you can do something like this:

<script type="text/javascript"

You could also do that via JS, e.g.

function add_script(url) {
    var script,scripts;
    script = document.createElement('script');
    script.async = true;
    script.src = url;

    scripts = document.getElementsByTagName('script')[0];
    scripts.parentNode.insertBefore(script, scripts);

Now here's the problem. Most external APIs don't have JSONP capability. 

No bother, with nginx you can turn JSON into JSONP.

location ^~ /ext_api3/ {
  echo_before_body 'parseResponse(;
  echo_after_body ');';

This uses the HTTPEchoModule to wrap the JSON response in the callback for the external API.

Custom logs

The HTTPLogModule allows you to specify log formats within location blocks, which means you can write your API logs to a separate file. It also means if you proxy via a location block as in the examples above, you could give each proxy their own access and error logs with different parameters, e.g. error log level.

Update: good comments on HN.


If you have comments, hit me up on Twitter.
I'm the Founder & CEO of DuckDuckGo, the search engine that doesn't track you. I'm also the co-author of Traction, the book that helps you get customer growth. More about me.