π S3 Encryption β Simplified
Amazon S3 keeps your data safe through encryption, and there are two ways it can happen:
π Server-Side Encryption (SSE) β S3 encrypts your data after you upload it
π» Client-Side Encryption (CSE) β you encrypt the data before uploading it
π Server-Side Encryption (SSE)
S3 handles the encryption for you, automatically:
π€ You upload a file
π S3 encrypts it before storing it
πΎ The encrypted version sits securely in your bucket
<div class="infographic-sse">
<style>
.infographic-sse {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f6f4ef;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--bucket: #1f7a4d;
--lock: #c0392b;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 860px;
margin: 0 auto;
padding: 1.5rem 1.25rem 2rem;
box-sizing: border-box;
}
.infographic-sse[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #6f9bff;
--accent-soft:#1c2740;
--line: #2c313d;
--bucket: #4fbf85;
--lock: #e57373;
}
.infographic-sse * { box-sizing: border-box; }
.sse-scroller { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.sse-canvas { width: 100%; height: auto; display: block; min-width: 560px; }
.sse-title { font-family: Georgia, serif; font-weight: 700; font-size: 30px; text-anchor: middle; fill: var(--ink); }
.sse-person { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }
.sse-label { font-size: 17px; fill: var(--ink); text-anchor: middle; }
.sse-sub-label { font-size: 15px; fill: var(--ink-soft); text-anchor: middle; }
.sse-doc-static { fill: none; stroke: var(--ink); stroke-width: 2.2; stroke-linejoin: round; }
.sse-doc-lines { stroke: var(--ink-soft); stroke-width: 1.3; }
.sse-arrow { stroke: var(--ink); stroke-width: 2.2; fill: none; }
.sse-arrowhead { fill: var(--ink); }
.sse-box { fill: none; stroke: var(--bucket); stroke-width: 2.5; stroke-dasharray: 6 5; }
.sse-badge { fill: var(--bucket); }
.sse-badge-bucket { fill: none; stroke: #fff; stroke-width: 2.2; stroke-linejoin: round; }
.sse-bucket { fill: none; stroke: var(--bucket); stroke-width: 3; stroke-linejoin: round; }
.sse-annotation { font-size: 16px; fill: var(--ink); }
.sse-annotation-group { opacity: 0; }
.sse-doc-traveler { }
.sse-lock-group { opacity: 0; }
@keyframes sse-travel {
0% { transform: translate(220px, 300px) scale(1); opacity: 0; }
6% { opacity: 1; }
42% { transform: translate(655px, 300px) scale(1); opacity: 1; }
52% { transform: translate(748px, 390px) scale(0.8); opacity: 1; }
92% { transform: translate(748px, 390px) scale(0.8); opacity: 1; }
97% { opacity: 0; }
100% { transform: translate(220px, 300px) scale(1); opacity: 0; }
}
@keyframes sse-lock-fade {
0%, 55% { opacity: 0; }
65%, 90% { opacity: 1; }
97%, 100% { opacity: 0; }
}
@keyframes sse-note-fade {
0%, 58% { opacity: 0; }
68%, 90% { opacity: 1; }
97%, 100% { opacity: 0; }
}
.infographic-sse .sse-doc-traveler {
animation: sse-travel 6s ease-in-out infinite;
animation-play-state: paused;
}
.infographic-sse .sse-lock-group {
animation: sse-lock-fade 6s ease-in-out infinite;
animation-play-state: paused;
}
.infographic-sse .sse-annotation-group {
animation: sse-note-fade 6s ease-in-out infinite;
animation-play-state: paused;
}
.infographic-sse.is-playing .sse-doc-traveler,
.infographic-sse.is-playing .sse-lock-group,
.infographic-sse.is-playing .sse-annotation-group {
animation-play-state: running;
}
.sse-controls { display: flex; justify-content: center; gap: 0.6rem; margin-top: 1rem; }
.sse-btn {
font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--ink);
background: var(--paper-alt); border: 1.5px solid var(--line); border-radius: 999px;
padding: 0.5rem 1.15rem; cursor: pointer; transition: border-color 0.15s, background 0.15s;
}
.sse-btn:hover { border-color: var(--accent); }
.sse-btn.primary { color: #fff; border-color: var(--accent); background: var(--accent); }
.sse-btn.secondary { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) {
.infographic-sse .sse-doc-traveler,
.infographic-sse .sse-lock-group,
.infographic-sse .sse-annotation-group {
animation: none !important;
}
.infographic-sse.is-playing .sse-doc-traveler { transform: translate(748px,390px) scale(0.8); opacity: 1; }
.infographic-sse.is-playing .sse-lock-group,
.infographic-sse.is-playing .sse-annotation-group { opacity: 1; }
}
</style>
<div class="sse-scroller">
<svg class="sse-canvas" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="sse-head" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
<path d="M0,0 L8,4 L0,8 Z" class="sse-arrowhead"/>
</marker>
</defs>
<text class="sse-title" x="480" y="44">π Server-Side Encryption (Default)</text>
<!-- user -->
<g transform="translate(220,110)">
<circle class="sse-person" cx="0" cy="0" r="16"/>
<path class="sse-person" d="M-24,46 A24,28 0 0 1 24,46"/>
</g>
<text class="sse-label" x="270" y="118">User</text>
<!-- static "upload" doc icon (source) -->
<g transform="translate(220,200)">
<path class="sse-doc-static" d="M-16,-20 L8,-20 L20,-8 L20,20 L-16,20 Z"/>
<path class="sse-doc-lines" d="M-9,-8 H12 M-9,2 H12 M-9,10 H4"/>
</g>
<text class="sse-sub-label" x="220" y="240">Upload</text>
<!-- arrow from user/upload toward bucket -->
<line class="sse-arrow" x1="220" y1="260" x2="220" y2="300" marker-end="url(#sse-head)"/>
<line class="sse-arrow" x1="220" y1="300" x2="655" y2="300" marker-end="url(#sse-head)"/>
<!-- Amazon S3 box -->
<rect class="sse-box" x="560" y="90" width="370" height="410" rx="4"/>
<rect class="sse-badge" x="580" y="112" width="60" height="60" rx="8"/>
<g class="sse-badge-bucket" transform="translate(596,128)">
<ellipse cx="14" cy="0" rx="14" ry="4"/>
<path d="M1,1 L5,26 L23,26 L27,1"/>
</g>
<text class="sse-label" x="712" y="150">Amazon S3</text>
<!-- bucket icon -->
<g transform="translate(690,320)">
<ellipse class="sse-bucket" cx="65" cy="0" rx="65" ry="18"/>
<path class="sse-bucket" d="M4,3 L20,130 Q65,146 110,130 L126,3"/>
<path class="sse-bucket" d="M110,55 Q148,55 148,76 Q148,94 114,88"/>
</g>
<text class="sse-label" x="755" y="478">Bucket</text>
<!-- annotation, fades in once encrypted -->
<g class="sse-annotation-group">
<text class="sse-annotation" x="60" y="360">Server encrypts</text>
<text class="sse-annotation" x="60" y="384">the file after</text>
<text class="sse-annotation" x="60" y="408">receiving it</text>
</g>
<!-- traveling document -->
<g class="sse-doc-traveler">
<g transform="translate(-16,-20)">
<path class="sse-doc-static" d="M0,0 L24,0 L36,12 L36,40 L0,40 Z"/>
<path class="sse-doc-lines" d="M8,12 H29 M8,22 H29 M8,32 H21"/>
</g>
<g class="sse-lock-group" transform="translate(8,14)">
<rect width="20" height="16" rx="3" fill="var(--lock)"/>
<path d="M3,0 V-6 a7,7 0 0 1 14,0 V0" fill="none" stroke="var(--lock)" stroke-width="3"/>
<circle cx="10" cy="8" r="2" fill="#fff"/>
</g>
</g>
</svg>
</div>
<div class="sse-controls">
<button class="sse-btn primary" id="sse-toggle">βΆ Play</button>
<button class="sse-btn secondary" id="sse-restart">βΊ Restart</button>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-sse')
|| document.querySelector('.infographic-sse');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const toggleBtn = wrapper.querySelector('#sse-toggle');
const restartBtn = wrapper.querySelector('#sse-restart');
const animated = wrapper.querySelectorAll('.sse-doc-traveler, .sse-lock-group, .sse-annotation-group');
let playing = false;
function setPlaying(next) {
playing = next;
wrapper.classList.toggle('is-playing', playing);
toggleBtn.textContent = playing ? 'βΈ Pause' : 'βΆ Play';
}
toggleBtn.addEventListener('click', function () { setPlaying(!playing); });
restartBtn.addEventListener('click', function () {
wrapper.classList.remove('is-playing');
animated.forEach(function (el) {
el.style.animation = 'none';
});
void wrapper.offsetWidth; // force reflow to restart animation from 0%
animated.forEach(function (el) {
el.style.animation = '';
});
setPlaying(true);
});
})();
</script>
</div>Since S3 (the server) does the work, it's called server-side. β Because it's on by default, your data is protected even if you never touch an encryption setting.
π» Client-Side Encryption (CSE)
Here, you do the encrypting first:
π You encrypt the file yourself β locally, or in your app
π€ You upload the already-encrypted file
πΎ S3 stores it exactly as received, no changes made
<div class="infographic-cse">
<style>
.infographic-cse {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f6f4ef;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--bucket: #1f7a4d;
--lock: #c0392b;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 860px;
margin: 0 auto;
padding: 1.5rem 1.25rem 2rem;
box-sizing: border-box;
}
.infographic-cse[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #6f9bff;
--accent-soft:#1c2740;
--line: #2c313d;
--bucket: #4fbf85;
--lock: #e57373;
}
.infographic-cse * { box-sizing: border-box; }
.cse-scroller { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cse-canvas { width: 100%; height: auto; display: block; min-width: 560px; }
.cse-title { font-family: Georgia, serif; font-weight: 700; font-size: 30px; text-anchor: middle; fill: var(--ink); }
.cse-person { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }
.cse-label { font-size: 17px; fill: var(--ink); text-anchor: middle; }
.cse-sub-label { font-size: 15px; fill: var(--ink-soft); text-anchor: middle; }
.cse-doc-static { fill: none; stroke: var(--ink); stroke-width: 2.2; stroke-linejoin: round; }
.cse-doc-lines { stroke: var(--ink-soft); stroke-width: 1.3; }
.cse-lock-badge rect { fill: var(--lock); }
.cse-lock-badge path { fill: none; stroke: var(--lock); }
.cse-lock-badge circle { fill: #fff; }
.cse-arrow { stroke: var(--ink); stroke-width: 2.2; fill: none; }
.cse-arrowhead { fill: var(--ink); }
.cse-box { fill: none; stroke: var(--bucket); stroke-width: 2.5; stroke-dasharray: 6 5; }
.cse-badge { fill: var(--bucket); }
.cse-badge-bucket { fill: none; stroke: #fff; stroke-width: 2.2; stroke-linejoin: round; }
.cse-bucket { fill: none; stroke: var(--bucket); stroke-width: 3; stroke-linejoin: round; }
.cse-annotation { font-size: 16px; font-weight: 600; fill: var(--ink); }
@keyframes cse-travel {
0% { transform: translate(220px, 300px) scale(1); opacity: 0; }
6% { opacity: 1; }
42% { transform: translate(655px, 300px) scale(1); opacity: 1; }
52% { transform: translate(748px, 390px) scale(0.8); opacity: 1; }
92% { transform: translate(748px, 390px) scale(0.8); opacity: 1; }
97% { opacity: 0; }
100% { transform: translate(220px, 300px) scale(1); opacity: 0; }
}
.infographic-cse .cse-doc-traveler {
animation: cse-travel 6s ease-in-out infinite;
animation-play-state: paused;
}
.infographic-cse.is-playing .cse-doc-traveler {
animation-play-state: running;
}
.cse-controls { display: flex; justify-content: center; gap: 0.6rem; margin-top: 1rem; }
.cse-btn {
font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--ink);
background: var(--paper-alt); border: 1.5px solid var(--line); border-radius: 999px;
padding: 0.5rem 1.15rem; cursor: pointer; transition: border-color 0.15s, background 0.15s;
}
.cse-btn:hover { border-color: var(--accent); }
.cse-btn.primary { color: #fff; border-color: var(--accent); background: var(--accent); }
.cse-btn.secondary { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) {
.infographic-cse .cse-doc-traveler { animation: none !important; }
.infographic-cse.is-playing .cse-doc-traveler { transform: translate(748px,390px) scale(0.8); opacity: 1; }
}
</style>
<div class="cse-scroller">
<svg class="cse-canvas" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="cse-head" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
<path d="M0,0 L8,4 L0,8 Z" class="cse-arrowhead"/>
</marker>
</defs>
<text class="cse-title" x="480" y="44">π Client-Side Encryption</text>
<!-- user -->
<g transform="translate(220,110)">
<circle class="cse-person" cx="0" cy="0" r="16"/>
<path class="cse-person" d="M-24,46 A24,28 0 0 1 24,46"/>
</g>
<text class="cse-label" x="270" y="118">User</text>
<!-- static "upload" doc icon, already shown encrypted -->
<g transform="translate(220,200)">
<path class="cse-doc-static" d="M-16,-20 L8,-20 L20,-8 L20,20 L-16,20 Z"/>
<path class="cse-doc-lines" d="M-9,-8 H12 M-9,2 H12 M-9,10 H4"/>
<g class="cse-lock-badge" transform="translate(6,10)">
<rect width="18" height="15" rx="3"/>
<path d="M3,0 V-5 a6,6 0 0 1 12,0 V0" stroke-width="2.6"/>
<circle cx="9" cy="7" r="1.8"/>
</g>
</g>
<text class="cse-sub-label" x="220" y="242">Upload</text>
<!-- annotation -->
<text class="cse-annotation" x="330" y="210">Encrypts the file</text>
<text class="cse-annotation" x="330" y="234">before uploading it</text>
<!-- arrow from user/upload toward bucket -->
<line class="cse-arrow" x1="220" y1="260" x2="220" y2="300" marker-end="url(#cse-head)"/>
<line class="cse-arrow" x1="220" y1="300" x2="655" y2="300" marker-end="url(#cse-head)"/>
<!-- Amazon S3 box -->
<rect class="cse-box" x="560" y="90" width="370" height="410" rx="4"/>
<rect class="cse-badge" x="580" y="112" width="60" height="60" rx="8"/>
<g class="cse-badge-bucket" transform="translate(596,128)">
<ellipse cx="14" cy="0" rx="14" ry="4"/>
<path d="M1,1 L5,26 L23,26 L27,1"/>
</g>
<text class="cse-label" x="712" y="150">Amazon S3</text>
<!-- bucket icon -->
<g transform="translate(690,320)">
<ellipse class="cse-bucket" cx="65" cy="0" rx="65" ry="18"/>
<path class="cse-bucket" d="M4,3 L20,130 Q65,146 110,130 L126,3"/>
<path class="cse-bucket" d="M110,55 Q148,55 148,76 Q148,94 114,88"/>
</g>
<text class="cse-label" x="755" y="478">Bucket</text>
<!-- traveling document, already encrypted -->
<g class="cse-doc-traveler">
<g transform="translate(-16,-20)">
<path class="cse-doc-static" d="M0,0 L24,0 L36,12 L36,40 L0,40 Z"/>
<path class="cse-doc-lines" d="M8,12 H29 M8,22 H29 M8,32 H21"/>
</g>
<g class="cse-lock-badge" transform="translate(8,14)">
<rect width="20" height="16" rx="3"/>
<path d="M3,0 V-6 a7,7 0 0 1 14,0 V0" stroke-width="3"/>
<circle cx="10" cy="8" r="2"/>
</g>
</g>
</svg>
</div>
<div class="cse-controls">
<button class="cse-btn primary" id="cse-toggle">βΆ Play</button>
<button class="cse-btn secondary" id="cse-restart">βΊ Restart</button>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-cse')
|| document.querySelector('.infographic-cse');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const toggleBtn = wrapper.querySelector('#cse-toggle');
const restartBtn = wrapper.querySelector('#cse-restart');
const traveler = wrapper.querySelector('.cse-doc-traveler');
let playing = false;
function setPlaying(next) {
playing = next;
wrapper.classList.toggle('is-playing', playing);
toggleBtn.textContent = playing ? 'βΈ Pause' : 'βΆ Play';
}
toggleBtn.addEventListener('click', function () { setPlaying(!playing); });
restartBtn.addEventListener('click', function () {
wrapper.classList.remove('is-playing');
traveler.style.animation = 'none';
void wrapper.offsetWidth; // force reflow to restart from 0%
traveler.style.animation = '';
setPlaying(true);
});
})();
</script>
</div>Since you (the client) handle it, it's called client-side.
βοΈ Quick Comparison
π Server-Side | π» Client-Side |
|---|---|
S3 does the encrypting | You do the encrypting |
Happens after upload | Happens before upload |
On by default | You have to set it up |
Both protect your data β the only real difference is who does the encrypting and when. SSE is automatic and default; CSE puts you in control, but you have to do the work. π