POST requests
URL encoding
Web development
HTTP communication
Data transmission

What does %5B and %5D in POST requests stand for?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

%5B and %5D are URL-encoded representations of [ (left bracket) and ] (right bracket). You will see them in POST request bodies, query strings, and browser DevTools whenever an application transmits array or nested data through HTTP. They exist because brackets are reserved characters that cannot appear raw in URLs or application/x-www-form-urlencoded payloads.

How URL Encoding Works

URL encoding (also called percent encoding) replaces every unsafe or reserved character with a % sign followed by two hexadecimal digits representing the character's ASCII code.

CharacterASCII (decimal)HexURL-Encoded
[915B%5B
]935D%5D
space3220%20 or +
@6440%40
#3523%23

Characters that are safe in URLs (letters, digits, -, _, .) pass through unchanged. Everything else gets encoded.

Encoding and decoding in code

JavaScript

javascript
1// Encoding
2const encoded = encodeURIComponent("fruits[]");
3console.log(encoded); // "fruits%5B%5D"
4
5// Decoding
6const decoded = decodeURIComponent("fruits%5B%5D");
7console.log(decoded); // "fruits[]"

Python

python
1from urllib.parse import quote, unquote
2
3encoded = quote("fruits[]")
4print(encoded)  # fruits%5B%5D
5
6decoded = unquote("fruits%5B%5D")
7print(decoded)  # fruits[]

PHP

php
echo urlencode("fruits[]");   // fruits%5B%5D
echo urldecode("fruits%5B%5D"); // fruits[]

Where %5B and %5D Appear in Practice

Query strings (GET requests)

When a URL needs to carry an array, the brackets get encoded:

 
https://api.example.com/search?colors%5B%5D=red&colors%5B%5D=blue

The server decodes this to:

 
colors[] = red
colors[] = blue

Form submissions (POST requests)

Consider an HTML form with checkbox inputs that share an array-style name:

html
1<form method="post" action="/order">
2  <input type="checkbox" name="items[]" value="widget" />
3  <input type="checkbox" name="items[]" value="gadget" />
4  <input type="checkbox" name="items[]" value="gizmo" />
5  <button type="submit">Order</button>
6</form>

When the user checks all three and submits, the browser sends this POST body:

 
items%5B%5D=widget&items%5B%5D=gadget&items%5B%5D=gizmo

Nested parameters

Frameworks like Rails and Laravel use bracket notation for nested objects:

 
user%5Bname%5D=Alice&user%5Baddress%5D%5Bcity%5D=Seattle

Decoded, the server interprets this as:

 
user[name] = Alice
user[address][city] = Seattle

Which maps to the object:

json
1{
2  "user": {
3    "name": "Alice",
4    "address": {
5      "city": "Seattle"
6    }
7  }
8}

How Different Frameworks Parse %5B%5D

The way a server treats bracket-encoded parameters depends on the backend framework:

FrameworkArray syntaxNested syntaxAuto-decode
PHPitems[]user[address][city]Yes
Rails (Ruby)items[]user[address][city]Yes
Express (Node)items[] (with qs)user[address][city] (with qs)Yes
Django (Python)items (repeated key)Not natively supportedPartial
Spring (Java)items (repeated key)user.address.city (dot notation)Partial

Django and Spring do not use bracket syntax by default. In Django, you retrieve repeated keys with request.GET.getlist("items"). In Spring, you use dot notation or bind to a POJO.

Sending Arrays with fetch and axios

When you build requests from JavaScript, the encoding usually happens automatically, but it helps to know how.

fetch with URLSearchParams

javascript
1const params = new URLSearchParams();
2params.append("tags[]", "javascript");
3params.append("tags[]", "python");
4
5fetch("/api/articles", {
6  method: "POST",
7  body: params,  // Content-Type is set automatically
8});
9// Body sent: tags%5B%5D=javascript&tags%5B%5D=python

axios with qs

javascript
1import axios from "axios";
2import qs from "qs";
3
4axios.post("/api/articles", qs.stringify({
5  tags: ["javascript", "python"]
6}, { arrayFormat: "brackets" }));
7// Body sent: tags%5B%5D=javascript&tags%5B%5D=python

Sending JSON instead

If your API accepts JSON, you can skip bracket encoding entirely:

javascript
1fetch("/api/articles", {
2  method: "POST",
3  headers: { "Content-Type": "application/json" },
4  body: JSON.stringify({ tags: ["javascript", "python"] }),
5});

JSON payloads are not URL-encoded, so brackets never appear as %5B%5D.

Common Pitfalls

  • Double encoding. If you manually encode a value that the HTTP client encodes again, you end up with %255B instead of %5B. Let your library handle encoding, or encode only once.
  • Missing brackets in Django. Django does not parse items[] into a list automatically. Use request.POST.getlist("items[]") or drop the brackets and call request.POST.getlist("items").
  • Framework mismatch. Sending items[0]=a&items[1]=b (indexed brackets) works in PHP/Rails but not in Express without the qs library. Match your client encoding to your server framework.
  • Forgetting to decode for logging. Raw %5B%5D in logs is hard to read. Run values through decodeURIComponent (JS) or unquote (Python) before logging.
  • Brackets in JSON bodies. If your Content-Type is application/json, do not URL-encode. Brackets inside JSON are literal array delimiters and must not be percent-encoded.

Summary

  • %5B is the URL-encoded form of [ and %5D is the URL-encoded form of ].
  • They appear whenever array or nested parameters are transmitted through URL-encoded HTTP requests (both GET and POST).
  • Bracket notation is the standard way PHP, Rails, and Express (with qs) handle array parameters.
  • Django and Spring use repeated keys or dot notation instead of brackets.
  • Sending data as JSON avoids URL encoding entirely and is generally simpler for complex structures.
  • Always let your HTTP client or framework handle encoding to avoid double-encoding bugs.

Course illustration
Course illustration

All Rights Reserved.