Value Inspector for Redis, Valkey and Memcached

Paste a value from Redis, Valkey, Memcached or any other key-value store and see what format it’s in and what it says. The inspector reads JSON, MessagePack, CBOR, BSON, Protocol Buffers, PHP sessions, igbinary, Java serialization, Python pickles and Ruby Marshal, and it unwraps base64, hex, gzip, zlib, LZ4 and Snappy on the way in. It all runs in your browser.

Nothing you paste leaves this page, and nothing in it runs. The tool doesn’t send anything anywhere, and the page’s security policy stops it from fetching or loading anything from another site. Pickles, Java objects and Marshal data are only read, so a value can’t make the page do anything.

On a wide screen you can open it full screen.

How to Use It

  1. Get the value with its bytes intact. Ask redis-cli or valkey-cli for the quoted form, which writes every byte that isn’t plain text as a \x escape:
redis-cli --no-raw GET session:42
valkey-cli --no-raw HGET user:42 profile
  1. Paste it. Paste what it printed, quotes and all. Hex, base64 and plain text work too, and so does loading a file that holds the value. Each example on the page was written by the real library.
  2. Read the layers. If the value was wrapped, the page shows each layer from the outside in, such as base64, then gzip with its checksum, then JSON.
  3. Read the value. It’s shown indented, with notes such as the Java class of a number or the time zone of a date. Show as JSON turns it into plain JSON.
  4. Take it with you. Copy the value, or download the bytes from inside the last layer.

Why Stored Values Are Hard to Read

A key-value store keeps whatever bytes it’s given and never looks inside them, so every application picks its own way to turn objects into bytes. Spring Data Redis and Spring Session store values with Java serialization by default. Django’s Redis cache pickles every value apart from plain integers. PHP keeps sessions in its own serialize() format or in igbinary, and Ruby has Marshal. Everyone else tends to use JSON, often gzipped, or a compact binary format such as MessagePack.

Then redis-cli adds its escapes on top, so a value read by hand looks like "\xac\xed\x00\x05sr\x00\x11java.util.HashMap...". Making sense of it means spotting the format from a few bytes, undoing each layer in the right order and knowing the format’s layout. The inspector works from the outside in, the way a person would, up to 8 layers deep.

Formats with a signature are the easy part. Binary with no signature gets tried as MessagePack, CBOR and Protocol Buffers, and a reading wins only when it holds real text, floating-point numbers, booleans or nulls, since random bytes often parse as one of these by chance. Weaker readings are listed as possibilities.

What It Reads

  • Text formats. JSON, with every number exactly as written, JWTs with their dates (the signature isn’t checked), PHP serialize() and sessions, and pickle’s old text protocols.
  • Language formats. Java serialization, Python pickle protocols 0 to 5, Ruby Marshal and igbinary, with class names shown and back-references resolved.
  • Binary formats. MessagePack, CBOR, BSON and Protocol Buffers. Protocol Buffers show field numbers only, since the names live in the .proto schema.
  • Wrappers. base64, hex, gzip, zlib, LZ4 and Snappy, with their checksums checked.
  • Also recognized. Images, with a preview, and Redis DUMP payloads, which the Snapshot Viewer decodes. Zstandard, bzip2, xz, ZIP, PDF, .NET, Avro and Parquet are named but not opened.

Nothing in a Value Runs

Some of these formats carry instructions along with the data. A pickle can tell Python to call any function while it loads, which is how a crafted pickle runs commands on a server. Java runs the readObject code of the classes a stream names, and Ruby’s Marshal calls _load and marshal_load.

The inspector never calls anything. It reads the format’s records and describes what they would build. A pickle that would run os.system('echo hi') shows up as a call to posix.system with the argument "echo hi", and that’s all that happens. Decompression stops at 256 MB, so a small value built to expand without end can’t take over the page.

How It Was Tested

Every test value was written by the real thing: Python 3.13 with its own pickle module, msgpack, cbor2, pymongo’s BSON, protobuf, lz4, python-snappy and Pillow; Django 6.1’s Redis cache; PHP 8.3 with igbinary 3.2; Ruby 3.3; and Java 21’s ObjectOutputStream. Valkey 9.1.2 and Redis 8.10.2 supplied DUMP payloads and printed stored values with --no-raw.

  • All 53 values decoded to exactly what their own language reads back, compared value by value.
  • All 10 values that redis-cli and valkey-cli printed read back to the exact bytes stored.
  • The DEFLATE decoder behind gzip and zlib matched Node’s zlib on 7 inputs at 4 compression levels and 5 strategies.
  • Of 50,000 random base64 tokens and 20,000 random hex strings, none was taken for anything but text. Of 20,000 random binary values, only 21 short ones that happened to be valid text came out as anything but binary data.
  • Thousands of random and damaged values decoded or failed cleanly, with no crash.

The Code

The inspector is one JavaScript file with no dependencies, open source under the Apache License 2.0. It runs as this page, as a command line in Node.js and inside your own code:

node inspect/cli.js value.bin
redis-cli --no-raw GET session:42 | node inspect/cli.js -
node inspect/cli.js value.bin --json > value.json

The full manual, the tests and the values they replay are in the inspect folder on GitHub. The other tools work the same way.