How to Set Up Nginx as a Reverse Proxy for Couchbase

How to Set Up Nginx as a Reverse Proxy for Couchbase

How to Set Up Nginx as a Reverse Proxy for Couchbase

Couchbase is a distributed NoSQL database, and unlike a typical single-port web application, it spreads its functionality across a whole range of ports: the web admin console, the REST management API, the Data (KV) service, Query (N1QL), Search (FTS), Analytics, and Views, each with its own port and often its own TLS-enabled variant. That spread is exactly why reverse proxying Couchbase needs a more deliberate approach than a single proxy_pass block — deciding what gets proxied, and how, depends entirely on which services actually need external access.

This guide covers setting up Nginx in front of Couchbase’s web console and REST/query APIs, and explains why proxying the Data service works differently from the rest.

Understanding Couchbase’s Port Layout

Before writing any Nginx configuration, it helps to know what’s actually running where. A default Couchbase Server node exposes (non-TLS ports shown; TLS equivalents typically add 1 to the ten-thousands digit or a fixed offset depending on version — always confirm exact ports against the running cluster’s admin console):

That last one is the key distinction: the Data service, which is what SDKs actually use for the bulk of read/write operations, speaks a binary protocol over 11210/11207, not HTTP. It cannot be proxied with Nginx’s standard http module the same way the console and query APIs can — it would need the stream module for raw TCP forwarding, similar to how Redis is handled, and even then, Couchbase SDKs perform their own internal node discovery and connection management that doesn’t play well with a simple TCP proxy in front of it. For that reason, most real-world Couchbase reverse proxy setups focus on the HTTP-based services — the web console, REST management API, Query, Search, and Analytics — and leave direct SDK/Data-service traffic on the private network, unproxied.

Requirements

Install Nginx:

sudo apt update && sudo apt install nginx -y

Confirm Couchbase’s web console is reachable:

curl -u Administrator:password http://127.0.0.1:8091/pools/default

Step 1: Decide What to Expose

A reasonable default policy:

Step 2: Write the Nginx Configuration

Create the config file:

sudo nano /etc/nginx/sites-available/couchbase.conf

Example configuration proxying the web console and Query service, each under its own subdomain:

# Web Console / Cluster Manager
server {
    listen 80;
    server_name couchbase-admin.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name couchbase-admin.example.com;

    ssl_certificate     /etc/letsencrypt/live/couchbase-admin.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/couchbase-admin.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # Restrict to known internal/admin IP ranges
    allow 10.0.0.0/8;
    allow 203.0.113.10;
    deny all;

    location / {
        proxy_pass http://127.0.0.1:8091;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # The console UI uses long-polling for some cluster status views
        proxy_read_timeout 120s;
        proxy_send_timeout 120s;

        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

# Query (N1QL) service
server {
    listen 443 ssl;
    server_name couchbase-query.example.com;

    ssl_certificate     /etc/letsencrypt/live/couchbase-query.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/couchbase-query.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        auth_basic "Couchbase Query API";
        auth_basic_user_file /etc/nginx/.couchbase_htpasswd;

        proxy_pass http://127.0.0.1:8093;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

Generate the htpasswd file for the query endpoint if that layer of access control is desired in addition to Couchbase’s own RBAC:

sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.couchbase_htpasswd query_user

Enable and test:

sudo ln -s /etc/nginx/sites-available/couchbase.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 3: Testing the Setup

Test the console through the proxy (from an allowed IP):

curl -Ik https://couchbase-admin.example.com/

Log into the web console in a browser and confirm the cluster overview, bucket list, and node status pages all load correctly, since some of these views poll the REST API repeatedly and will surface any proxy timeout issues quickly.

Test the Query service:

curl -u query_user -k https://couchbase-query.example.com/query/service \
  -d 'statement=SELECT COUNT(*) FROM `travel-sample`'

A valid JSON response with query results confirms the Query service proxy is working end to end, including the basic auth layer and the underlying Couchbase RBAC check.

Troubleshooting

502 Bad Gateway on the console Confirm Couchbase is actually running and the web console is bound to the expected port:

sudo systemctl status couchbase-server
sudo ss -tlnp | grep 8091

Console loads but cluster status doesn’t update / pages hang The web console relies on repeated REST polling and occasionally long-held connections for certain views. Increase proxy_read_timeout and confirm proxy_http_version 1.1 with proxy_set_header Connection ""; is set, which allows keepalive connections to the backend rather than forcing a new connection per poll.

Query requests return 401 despite correct Couchbase credentials This usually means the Nginx auth_basic layer is being confused with Couchbase’s own authentication — these are two separate credential sets. The Nginx-level basic auth (query_user) gates access to the proxy itself; the actual N1QL query still needs to authenticate against Couchbase separately, either via the Authorization header passed through to Couchbase, or by including credentials in the query request as the Couchbase client library expects.

SDK connections fail when pointed at the proxy This is expected if the Data service (11210) traffic is being routed at an HTTP proxy — it won’t work, since that’s a binary protocol, not HTTP. Couchbase SDKs also perform cluster topology discovery that assumes direct node connectivity; pointing an SDK at an HTTP reverse proxy for anything beyond simple REST calls is not a supported pattern. Keep SDK connections on the private network.

Views or Analytics endpoints time out on large result sets Increase proxy_read_timeout for those specific service ports, and check proxy_buffer_size/proxy_buffers, since large aggregation or analytics results can exceed default buffer allocations.

Security Considerations

add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
limit_req_zone $binary_remote_addr zone=cb_query:10m rate=20r/s;

location / {
    limit_req zone=cb_query burst=40 nodelay;
    proxy_pass http://127.0.0.1:8093;
}

Performance Tips

upstream couchbase_query {
    server 127.0.0.1:8093;
    keepalive 32;
}
upstream couchbase_query_nodes {
    least_conn;
    server 10.0.1.21:8093;
    server 10.0.1.22:8093;
    server 10.0.1.23:8093;
}

Real-World Use Cases

Best Practices Checklist

Couchbase’s split between HTTP-based services and its binary Data service protocol is the single most important thing to internalize before setting up a reverse proxy in front of it. Once that boundary is clear — proxy the console and query-style APIs, leave the Data service alone — the rest of the setup follows the same TLS-termination-plus-access-control pattern used for any other HTTP service behind Nginx.

Exit mobile version