Skip to main content

NGINX

NGINX Redis2 Module: Talk to Redis Directly from NGINX

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The NGINX redis2 module lets your web server talk directly to Redis using the native Redis protocol. Instead of routing every request through your application backend, NGINX handles the Redis communication itself. This approach eliminates an entire network hop and dramatically reduces response latency for Redis-backed operations.

This module implements the full Redis 2.0 unified protocol (RESP) with support for command pipelining, connection pooling via keepalive, and all Redis commands. Whether you need a simple key-value lookup or want to pipeline multiple commands in a single request, the NGINX redis2 module handles it natively at the web server level.

Why Use the NGINX Redis2 Module?

Traditional architectures require every Redis operation to pass through your application:

Client → NGINX → App Backend → Redis → App Backend → NGINX → Client

With the NGINX redis2 module, NGINX communicates with Redis directly:

Client → NGINX → Redis → NGINX → Client

This architecture is particularly effective for:

  • Session lookups where the application logic is simple
  • Cache serving where NGINX fetches cached content from Redis
  • Health checks that verify Redis availability
  • Rate limiting data read directly from Redis
  • API gateways that need fast key-value lookups

The NGINX redis2 module returns raw Redis Serialization Protocol (RESP) responses. Your client application parses these responses directly, or you can combine this module with lua-nginx-module for server-side processing.

For transparent caching with Redis, consider using the NGINX srcache module, which uses redis2 as its underlying protocol handler.

How the NGINX Redis2 Module Works

The NGINX redis2 module operates as an NGINX upstream module. When a request hits a location block configured with redis2_pass, NGINX opens a TCP connection to the specified Redis server, sends the configured query, and streams the raw RESP response back to the client.

Key architectural details:

  • Non-blocking I/O: Uses NGINX’s event-driven architecture, so Redis operations never block other connections
  • RESP protocol: Automatically converts redis2_query arguments into proper Redis wire protocol format
  • Streaming responses: Data flows through as raw bytes without intermediate buffering (beyond the configured buffer)
  • Keepalive support: Works with NGINX upstream keepalive for persistent TCP connections to Redis
  • Pipelining: Multiple redis2_query directives in the same location are concatenated into a single pipelined request

Installation

CentOS, RHEL, Rocky Linux, AlmaLinux, Fedora

First, enable the GetPageSpeed repository if you have not already done so:

sudo dnf install -y https://extras.getpagespeed.com/release-latest.rpm

Then install the NGINX redis2 module:

sudo dnf install -y nginx-module-redis2

After installation, load the module by adding the following line at the top of /etc/nginx/nginx.conf, before any http block:

load_module modules/ngx_http_redis2_module.so;

Debian, Ubuntu

On Debian and Ubuntu systems, install from the GetPageSpeed APT repository:

sudo apt-get update && sudo apt-get install -y nginx-module-redis2

On Debian and Ubuntu, modules are auto-enabled after installation. You do not need to add a load_module directive manually.

Verify Installation

Confirm that the module loaded correctly:

nginx -t

You should see:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Configuration

The NGINX redis2 module provides 11 directives. The two essential directives are redis2_query (to specify what Redis command to execute) and redis2_pass (to specify which Redis server to connect to).

redis2_query

Context: location, if in location
Arguments: command [arg1 arg2 …]

Specifies a Redis command with its arguments. NGINX automatically converts the arguments into the Redis RESP wire protocol format. You can use NGINX variables as arguments.

location = /redis/get {
    redis2_query get $arg_key;
    redis2_pass 127.0.0.1:6379;
}

Multiple redis2_query directives in the same location block are pipelined automatically. NGINX sends all commands in a single TCP write and reads all responses together:

location = /redis/pipeline {
    redis2_query set mykey "hello";
    redis2_query get mykey;
    redis2_query del mykey;
    redis2_pass 127.0.0.1:6379;
}

This is equivalent to running all three commands in a single Redis pipeline, which is significantly faster than three separate round trips.

redis2_pass

Context: location, if in location
Arguments: upstream_name | host:port

Specifies the Redis backend server. You can use a direct host:port address or reference an upstream block. For production deployments, always use an upstream block to enable connection pooling:

# Direct connection
location = /redis {
    redis2_query ping;
    redis2_pass 127.0.0.1:6379;
}
# Using an upstream block (recommended for connection pooling)
upstream redis_backend {
    server 127.0.0.1:6379;
    keepalive 32;
}

server {
    location = /redis {
        redis2_query ping;
        redis2_pass redis_backend;
    }
}

The redis2_pass directive also supports NGINX variables for dynamic backend routing:

location = /redis {
    redis2_query get $arg_key;
    redis2_pass $arg_backend;
}

redis2_raw_query

Context: location, if in location
Arguments: query_string

Sends a raw Redis protocol string. NGINX variables are interpolated. Use this when you need to construct queries dynamically:

location = /redis/raw {
    redis2_raw_query "ping\r\n";
    redis2_pass 127.0.0.1:6379;
}

Important: The redis2_raw_query directive is mutually exclusive with redis2_query and redis2_literal_raw_query. You can only use one query style per location block.

redis2_raw_queries

Context: location, if in location
Arguments: count query_string

Sends multiple raw pipelined queries. The first argument specifies how many commands are in the query string. Both arguments support NGINX variables:

location = /redis/multi-raw {
    redis2_raw_queries 2 "*1\r\n$4\r\nping\r\n*1\r\n$4\r\nping\r\n";
    redis2_pass 127.0.0.1:6379;
}

redis2_literal_raw_query

Context: location, if in location
Arguments: query_string

Similar to redis2_raw_query, but NGINX variables are not interpolated. Dollar signs are treated as literal characters. Use this when your Redis commands contain dollar signs that should not be interpreted as NGINX variables:

location = /redis/literal {
    redis2_literal_raw_query "*1\r\n$4\r\nping\r\n";
    redis2_pass 127.0.0.1:6379;
}

Timeout Directives

These directives control TCP-level timeouts for the Redis connection. All three accept time values (e.g., 5s, 500ms, 1m) and can be set in http, server, or location context.

Directive Default Description
redis2_connect_timeout 60s Maximum time to wait for a TCP connection to Redis
redis2_send_timeout 60s Maximum time to wait while sending the query to Redis
redis2_read_timeout 60s Maximum time to wait for Redis to respond

For most production deployments, the default 60-second timeouts are too generous. Set them to lower values to fail fast:

location = /redis {
    redis2_connect_timeout 200ms;
    redis2_send_timeout 200ms;
    redis2_read_timeout 200ms;

    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

redis2_buffer_size

Context: http, server, location
Default: one memory page (typically 4 KB or 8 KB)

Sets the buffer size for reading Redis responses. This is conceptually similar to proxy_buffer_size but for Redis upstream connections. Increase this for commands that return large values:

redis2_buffer_size 16k;

redis2_next_upstream

Context: http, server, location
Default: error timeout

Controls when NGINX should try the next upstream server (if multiple servers are defined in the upstream block). Valid values:

Value Description
error An error occurred while connecting, sending, or reading
timeout A timeout occurred during any phase
invalid_response Redis returned an invalid or unexpected response
off Disable failover entirely
upstream redis_cluster {
    server 127.0.0.1:6379;
    server 127.0.0.1:6380;
}

server {
    location = /redis {
        redis2_next_upstream error timeout invalid_response;
        redis2_query get $arg_key;
        redis2_pass redis_cluster;
    }
}

redis2_bind

Context: http, server, location

Binds outgoing connections to Redis to a specific local IP address. This is useful on servers with multiple network interfaces:

redis2_bind 10.0.0.1;

Practical Examples

Key-Value Store API

Create a simple REST-like API backed by Redis:

upstream redis_backend {
    server 127.0.0.1:6379;
    keepalive 32;
}

server {
    listen 80;

    location = /cache/get {
        redis2_query get $arg_key;
        redis2_pass redis_backend;
    }

    location = /cache/set {
        redis2_query set $arg_key $arg_val;
        redis2_pass redis_backend;
    }

    location = /cache/del {
        redis2_query del $arg_key;
        redis2_pass redis_backend;
    }

    location = /cache/exists {
        redis2_query exists $arg_key;
        redis2_pass redis_backend;
    }
}

Test it:

# Set a value
curl "http://localhost/cache/set?key=greeting&val=hello"
# +OK

# Get the value
curl "http://localhost/cache/get?key=greeting"
# $5
# hello

# Check existence
curl "http://localhost/cache/exists?key=greeting"
# :1

Redis Database Selection

Use Redis SELECT command as a regular redis2_query to work with different Redis databases:

location = /db1/get {
    redis2_query select 1;
    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

location = /db2/get {
    redis2_query select 2;
    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

List Operations

location = /list/push {
    redis2_query lpush $arg_list $arg_val;
    redis2_pass redis_backend;
}

location = /list/range {
    redis2_query lrange $arg_list 0 -1;
    redis2_pass redis_backend;
}

Test it:

# Push items
curl "http://localhost/list/push?list=mylist&val=first"
curl "http://localhost/list/push?list=mylist&val=second"
curl "http://localhost/list/push?list=mylist&val=third"

# Get all items
curl "http://localhost/list/range?list=mylist"
# *3
# $5
# third
# $6
# second
# $5
# first

Redis Health Check Endpoint

Create a simple health check that verifies Redis connectivity:

location = /health/redis {
    redis2_query ping;
    redis2_pass redis_backend;
}

A healthy Redis server responds with +PONG. Monitoring tools can hit this endpoint and alert when the response changes.

Redis Authentication

If your Redis server requires authentication, send the AUTH command before your data queries:

location = /redis/secure {
    redis2_query auth your_redis_password;
    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

For Redis 6+ with ACL usernames:

location = /redis/acl {
    redis2_query auth myuser mypassword;
    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

Connection Pooling with Keepalive

Without connection pooling, NGINX opens a new TCP connection to Redis for every request. This adds connection setup overhead and can exhaust file descriptors under load. Always use an upstream block with keepalive in production:

upstream redis_backend {
    server 127.0.0.1:6379;

    # Keep up to 32 idle connections alive
    keepalive 32;
}

server {
    location = /redis {
        redis2_query get $arg_key;
        redis2_pass redis_backend;
    }
}

The keepalive value should match your expected concurrency. For most deployments, 32-128 is a good starting range. Monitor your Redis connection count with INFO clients to tune this value. Read our NGINX upstream keepalive guide for a deeper dive into connection pooling.

Understanding RESP Responses

The NGINX redis2 module returns raw RESP (Redis Serialization Protocol) responses. Understanding this format is essential for parsing results:

Prefix Type Example
+ Simple string +OK\r\n
- Error -ERR unknown command\r\n
: Integer :1\r\n
$ Bulk string $5\r\nhello\r\n
* Array *2\r\n$5\r\nhello\r\n$5\r\nworld\r\n

A $-1 response means the key does not exist (null bulk string). A *0 response means an empty array.

If you need structured JSON output instead of raw RESP, consider combining this module with lua-nginx-module and lua-resty-redis-parser.

SELinux Considerations

On systems with SELinux enabled (default on RHEL, CentOS, Rocky Linux, and AlmaLinux), NGINX cannot connect to Redis by default. You must explicitly allow NGINX to make network connections:

sudo setsebool -P httpd_can_network_connect 1

Without this setting, you will see “Permission denied” errors in the NGINX error log:

connect() to 127.0.0.1:6379 failed (13: Permission denied) while connecting to upstream

The -P flag makes the change persist across reboots.

Performance Considerations

The NGINX redis2 module adds minimal overhead because it operates at the TCP level within NGINX’s event loop. However, keep these points in mind:

Use Pipelining

Pipelining multiple commands in a single location block reduces round trips. A single pipelined request with 4 commands is significantly faster than 4 separate HTTP requests:

# One HTTP request, four Redis commands
location = /batch {
    redis2_query set key1 val1;
    redis2_query set key2 val2;
    redis2_query get key1;
    redis2_query get key2;
    redis2_pass redis_backend;
}

Tune Timeouts for Your Workload

The default 60-second timeouts are designed for reliability, not latency. For latency-sensitive workloads, set aggressive timeouts:

redis2_connect_timeout 100ms;
redis2_send_timeout 100ms;
redis2_read_timeout 500ms;

Adjust Buffer Size for Large Values

If you store large values in Redis (e.g., cached HTML pages), increase the buffer size:

redis2_buffer_size 64k;

The default buffer size equals one memory page (typically 4 KB on most systems). If your Redis values exceed this size, NGINX will need multiple read cycles.

Security Best Practices

Restrict Access

Never expose Redis endpoints to the public internet. Use NGINX access controls:

location /redis/ {
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;

    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

Use Internal Locations

Mark Redis locations as internal when they should only be accessed via NGINX subrequests (e.g., from lua-nginx-module):

location = /redis {
    internal;
    redis2_query $arg_cmd $arg_key;
    redis2_pass redis_backend;
}

Enable Redis Authentication

Always use Redis AUTH in production. Never run Redis without a password on a network-accessible interface.

Validate Input

Since redis2_query accepts NGINX variables, be cautious with user-supplied input. The NGINX redis2 module handles RESP encoding safely (arguments are length-prefixed, preventing injection), but limit which Redis commands are available through your API design:

# Good: Fixed command, only the key varies
location = /cache/get {
    redis2_query get $arg_key;
    redis2_pass redis_backend;
}

# Dangerous: User controls the entire command
location = /redis/exec {
    redis2_query $arg_cmd $arg_key $arg_val;
    redis2_pass redis_backend;
}

Troubleshooting

502 Bad Gateway

This typically means NGINX cannot connect to Redis. Common causes:

  1. Redis is not running: Check with redis-cli ping or valkey-cli ping
  2. Wrong host/port: Verify the redis2_pass address matches your Redis configuration
  3. SELinux blocking: Run setsebool -P httpd_can_network_connect 1
  4. Firewall rules: Ensure port 6379 is accessible from NGINX

Check the NGINX error log for specific error messages:

tail -f /var/log/nginx/error.log

Empty or Unexpected Responses

If Redis returns $-1, the key does not exist. This is normal RESP behavior, not an error.

Module Not Found

If nginx -t shows unknown directive "redis2_query", the module is not loaded. On RHEL-based systems, add to the top of nginx.conf:

load_module modules/ngx_http_redis2_module.so;

On Debian/Ubuntu, reinstall the package — modules are auto-enabled.

Connection Timeouts

If queries time out despite Redis being responsive via redis-cli, check:

  1. SELinux: The most common cause on RHEL-based systems
  2. Firewall between NGINX and Redis: Test with curl telnet://127.0.0.1:6379
  3. Redis max connections: Check INFO clients for connected_clients near maxclients

Directive Conflicts

The redis2_query, redis2_raw_query, redis2_raw_queries, and redis2_literal_raw_query directives are mutually exclusive. Using more than one style in the same location block produces a configuration error:

"redis2_query" directive conflicts with "redis2_raw_query"

Choose one query style per location block.

Conclusion

The NGINX redis2 module provides a fast, lightweight path between your web server and Redis. By handling Redis communication at the NGINX level, you eliminate application backend overhead for simple Redis operations.

For best results, use upstream blocks with keepalive connections, pipeline commands wherever possible, and set appropriate timeouts for your workload. Restrict access to Redis-backed endpoints and always enable Redis authentication in production.

The module works with both Redis and compatible servers like Valkey. It is production-ready at version 0.15 and is part of the OpenResty ecosystem fully provided by GetPageSpeed packages.

The redis2-nginx-module source code is available on GitHub.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.