How Does AES Encryption Actually Work Inside Your Browser?

AES browser encryption — Chunky Munster

AES browser encryption is your browser turning readable text into ciphertext on your device, using a secret key and a standardized encryption engine. Nothing magical is happening in the cloud; the browser takes bytes in, mixes them with a key and an initialization vector, and spits out output that should look like static. If you want to watch that flow without shipping data anywhere, give the AES cipher a spin.

What AES is actually doing

AES stands for Advanced Encryption Standard. It is a symmetric block cipher, which means the same secret key is used for both encryption and decryption, unlike public-key systems such as RSA or ECC.

AES works on fixed-size blocks of data: 128 bits at a time. Your text is not encrypted directly as characters; the browser first encodes it into bytes, then the crypto layer processes those bytes in blocks and mode-dependent chunks.

That distinction matters. If you encrypt the exact same text twice with the same key but different IVs, the ciphertext should still differ. That is the whole point of using a proper mode like GCM or CBC instead of pretending the raw AES primitive is enough.

How the browser gets from text to ciphertext

In a browser app, AES usually runs through the Web Crypto API. JavaScript prepares the input, hands it to the browser’s crypto subsystem, and gets back an encrypted buffer. The browser is doing the actual cryptographic work, not some hand-rolled JavaScript math loop.

A typical flow looks like this:

  1. Take a string like Hello.
  2. Convert it into bytes with something like TextEncoder.
  3. Import or derive a key.
  4. Generate or supply an IV.
  5. Encrypt the bytes using crypto.subtle.encrypt().
  6. Encode the result as Base64 or hex if you want to display it.

In code, that usually looks more like plumbing than wizardry:

const encoder = new TextEncoder();
const plaintext = encoder.encode('backup code: 483921');

const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
  { name: 'AES-GCM', iv },
  key,
  plaintext
);

The browser keeps the crypto primitives close to the platform, which is good for consistency and usually better for security than rolling your own encryption library in plain JavaScript. The important part is that the data stays local unless your app sends it somewhere else after the fact.

Key, IV, mode: the three pieces people mix up

The key is the secret. If an attacker gets it, they can decrypt the ciphertext. If you reuse weak keys, guessable passwords, or hard-coded values in client-side code, the encryption is mostly decorative.

The IV — initialization vector — is not a secret. It is there to make repeated encryptions of the same plaintext produce different ciphertext. For modes like GCM, the IV should be unique for every encryption under the same key.

The mode determines how AES handles blocks. Common browser-friendly choices are:

If you are building a browser tool or client-side feature, AES-GCM is usually the sensible starting point. CBC without authentication is a footgun with a nice UI.

Why ciphertext looks random, and why that matters

Good ciphertext should not leak patterns from the original message. Repeated words, repeated lines, and obvious structure should disappear into output that looks like noise. That is why encrypted data often needs an encoding step like Base64 before it is shown or copied.

Example: the string AAAAAA is visually obvious to a human. After encryption, you should not be able to infer that it contained repeated letters, a JSON object, or a password reset token. If you can, something is wrong with the implementation, the mode, or the key handling.

There is also an integrity side to this. With authenticated encryption like GCM, decryption should fail if the ciphertext was altered. That is not just a nice bonus; it prevents silent corruption and some active attacks.

If you want the broader crypto basics behind browser-side secrecy, our guide on why Base64 shows up everywhere is a good companion piece. Base64 is not encryption, but it is often the last wrapper around encrypted bytes before they hit a screen or an API.

What AES browser encryption is good for

Browser-side AES is useful when you need local protection before data leaves the page, or when you want to encrypt content for storage on the client. Think notes apps, draft secrets, password vaults, redacted form fields, and files that should only be readable after the user enters a passphrase.

It is also handy for prototyping. You can test payloads, inspect ciphertext formats, and verify key handling without setting up a backend. For developers, that is often enough to catch bad assumptions before they turn into production problems.

Here are a few common patterns:

What it is not good for: making client-side secrets impossible to steal. If the browser can decrypt it, the user’s machine can usually get at the key somehow. Browser encryption is a layer, not a force field.

A Worked Example

Say you want to encrypt a short note in the browser before saving it. The plaintext is readable, the ciphertext is not, and the IV changes every time so identical notes do not produce identical output.

Plaintext:
meet at dock 7

Key:
6f3b1a9c0d4e8f12... (16/24/32 bytes, depending on AES variant)

IV:
9c 2a 7f 44 11 88 0f 3b 6e 54 20 a1

Ciphertext (Base64):
M0Z8Y6mM8iFQmYVx9s8qv4y6hYBq1p3W5w==

The browser does not just scramble letters. It encodes meet at dock 7 into bytes, uses the key plus IV, encrypts the bytes under AES-GCM, and returns binary ciphertext. The Base64 string is only there so you can store or copy the result without binary garbage in the middle.

Now decrypt it with the same key and IV, and you should get the original note back exactly. If even one byte of the ciphertext changes, GCM should reject it. That is a very useful property when you are storing or transmitting sensitive data.

Decrypt with the same key + IV:
meet at dock 7

In practice, the code flow is close to this:

const iv = crypto.getRandomValues(new Uint8Array(12));
const plaintextBytes = new TextEncoder().encode('meet at dock 7');

const encrypted = await crypto.subtle.encrypt(
  { name: 'AES-GCM', iv },
  key,
  plaintextBytes
);

const b64 = btoa(String.fromCharCode(...new Uint8Array(encrypted)));

If you are testing this kind of flow manually, it helps to keep the plaintext, IV, and output side by side. That makes it obvious whether the crypto layer is working or whether the bug lives in encoding, storage, or copy/paste handling.

Frequently Asked Questions

Is AES browser encryption safe?

It can be, if you use strong keys, a modern mode like GCM, and a unique IV for every encryption. The weak point is usually not AES itself, but how the app handles keys, passwords, and ciphertext encoding. If you hard-code secrets in client-side code, the game is already over.

Can a browser encrypt data without sending it to a server?

Yes. With the Web Crypto API, the encryption can happen entirely on the client device. That is useful for local-first tools, offline apps, and pre-upload protection. The browser still runs code, of course, so you should trust the page you are using.

What is the difference between AES and Base64?

AES encrypts data. Base64 only converts binary data into text-safe characters so it can be displayed or stored more easily. Base64 does not hide content by itself, and anyone can decode it.

Why do I need an IV with AES?

The IV prevents the same plaintext from producing the same ciphertext every time under the same key. Without it, patterns leak and repeated messages become easier to spot. In AES-GCM, the IV also has to be unique for each encryption with a given key.

The Bottom Line

AES in the browser is straightforward once you separate the moving parts: bytes, key, IV, mode, and output encoding. The browser is not inventing a new cipher on the fly; it is running a standard primitive through a platform API and giving you encrypted bytes back.

If you are building something real, focus on the boring details: choose a modern mode, generate IVs correctly, keep keys out of source code, and make sure you understand whether you need encryption, integrity, or both. If you want to test the flow end to end, open the AES cipher tool and inspect what changes when you tweak the key, IV, or plaintext.

That is usually where the lightbulb goes on. Encryption stops being abstract and starts looking like the data plumbing it always was.

// try the tool
give the AES cipher a spin →
// related reading
← all posts