DevOps & Cloud

AWS S3: Object Storage

TOKEN

Tutorial lengkap AWS S3 — buckets, object lifecycle, IAM policies, presigned URLs, versioning, static website hosting, replication, dan best practices keamanan

1. Pengenalan AWS S3

Amazon Simple Storage Service (S3) adalah layanan object storage yang disediakan oleh Amazon Web Services (AWS). S3 dirancang untuk menyimpan dan mengambil data dalam jumlah besar — dari beberapa byte hingga 5 TB per object — dengan durability 99.999999999% (11 nines).

S3 menggunakan model key-value store dimana setiap object disimpan dengan key (nama file) unik di dalam sebuah bucket (container). S3 menawarkan virtually unlimited storage, high availability, dan berbagai fitur keamanan yang membuatnya menjadi standar industri untuk cloud storage.

Mengapa S3 Penting?

KeunggulanPenjelasan
Durability99.999999999% (11 nines) — data hampir tidak mungkin hilang
Availability99.99% — selalu bisa diakses kapan saja
ScalabilityTanpa batas — dari byte hingga exabytes
SecurityEnkripsi at-rest dan in-transit, fine-grained access control
PerformanceHigh throughput, low latency untuk semua storage class
Cost-EffectivePay-as-you-go, tiered pricing berdasarkan akses pattern
IntegrationTerintegrasi dengan seluruh layanan AWS (Lambda, CloudFront, dll)
Diagram: Arsitektur AWS S3
┌──────────────────────────────────────────────────────────────┐
│                     AWS S3 ARCHITECTURE                       │
│                                                              │
│  ┌──────────┐    ┌──────────────┐    ┌───────────────────┐  │
│  │ Client   │    │  S3 API      │    │  S3 Buckets       │  │
│  │ (App/    │───â–ē│  (REST)      │───â–ē│                   │  │
│  │  Browser)│    │              │    │  ┌──────────────┐ │  │
│  └──────────┘    └──────────────┘    │  │ Bucket A     │ │  │
│                                      │  │ /photos/     │ │  │
│  ┌──────────┐                       │  │ /documents/  │ │  │
│  │ AWS CLI  │───────────────────────â–ē│  └──────────────┘ │  │
│  └──────────┘                       │                   │  │
│                                      │  ┌──────────────┐ │  │
│  ┌──────────┐                       │  │ Bucket B     │ │  │
│  │ AWS SDK  │───────────────────────â–ē│  │ /backups/    │ │  │
│  │ (boto3)  │                       │  │ /logs/       │ │  │
│  └──────────┘                       │  └──────────────┘ │  │
│                                      └───────────────────┘  │
│                                                              │
│  Setiap object disimpan di minimal 3 AZ                     │
│  → Durability 99.999999999% (11 nines)                      │
└──────────────────────────────────────────────────────────────┘

Konsep dasar S3:

2. Membuat & Mengelola Bucket

Menggunakan AWS CLI

Bash
# Pastikan AWS CLI sudah dikonfigurasi
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: ap-southeast-1
# Default output format: json

# Buat bucket baru
aws s3api create-bucket \
  --bucket my-app-storage-2026 \
  --region ap-southeast-1 \
  --create-bucket-configuration LocationConstraint=ap-southeast-1

# List semua buckets
aws s3 ls

# Lihat detail bucket
aws s3api get-bucket-location --bucket my-app-storage-2026
aws s3api head-bucket --bucket my-app-storage-2026

# Hapus bucket (harus kosong dulu)
aws s3 rb s3://my-app-storage-2026

# Hapus bucket beserta semua isinya (FORCE)
aws s3 rb s3://my-app-storage-2026 --force

Menggunakan Python (boto3)

Python
import boto3

# Inisialisasi S3 client
s3 = boto3.client('s3', region_name='ap-southeast-1')

# Buat bucket
s3.create_bucket(
    Bucket='my-app-storage-2026',
    CreateBucketConfiguration={
        'LocationConstraint': 'ap-southeast-1'
    }
)

# List semua buckets
response = s3.list_buckets()
for bucket in response['Buckets']:
    print(f"Bucket: {bucket['Name']}, Created: {bucket['CreationDate']}")

# Cek apakah bucket ada
def bucket_exists(bucket_name):
    try:
        s3.head_bucket(Bucket=bucket_name)
        return True
    except:
        return False

print(bucket_exists('my-app-storage-2026'))  # True

# Hapus bucket
s3.delete_bucket(Bucket='my-app-storage-2026')

Pengaturan Bucket Penting

Bash
# Block public access (keamanan default)
aws s3api put-public-access-block \
  --bucket my-app-storage-2026 \
  --public-access-block-configuration \
    BlockPublicAcls=true,\
    IgnorePublicAcls=true,\
    BlockPublicPolicy=true,\
    RestrictPublicBuckets=true

# Enable default encryption (AES-256)
aws s3api put-bucket-encryption \
  --bucket my-app-storage-2026 \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "aws:kms"
        },
        "BucketKeyEnabled": true
      }
    ]
  }'

# Enable access logging
aws s3api put-bucket-logging \
  --bucket my-app-storage-2026 \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-s3-access-logs",
      "TargetPrefix": "my-app-storage-2026/"
    }
  }'

# Set CORS configuration
aws s3api put-bucket-cors \
  --bucket my-app-storage-2026 \
  --cors-configuration '{
    "CORSRules": [
      {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "PUT", "POST"],
        "AllowedOrigins": ["https://app.example.com"],
        "ExposeHeaders": ["ETag"],
        "MaxAgeSeconds": 3600
      }
    ]
  }'

3. Objects: Upload & Manage

Upload Files

Bash
# Upload file biasa
aws s3 cp ./photo.jpg s3://my-app-storage-2026/photos/photo.jpg

# Upload dengan metadata
aws s3 cp ./document.pdf s3://my-app-storage-2026/docs/ \
  --metadata '{"author":"BeebaneLabs","category":"tutorial"}'

# Upload dengan content-type
aws s3 cp ./style.css s3://my-app-storage-2026/css/style.css \
  --content-type "text/css"

# Upload seluruh direktori (recursive)
aws s3 sync ./public/ s3://my-app-storage-2026/website/ \
  --exclude "*.tmp" \
  --exclude ".git/*"

# Upload dengan progress
aws s3 cp ./large-file.zip s3://my-app-storage-2026/ \
  --only-show-errors

# Upload dengan storage class
aws s3 cp ./archive.tar.gz s3://my-app-storage-2026/archives/ \
  --storage-class GLACIER

# Multipart upload untuk file besar (>100MB)
aws s3 cp ./huge-file.zip s3://my-app-storage-2026/ \
  --expected-size 5368709120

Download & Manage Files

Bash
# Download file
aws s3 cp s3://my-app-storage-2026/photos/photo.jpg ./downloaded.jpg

# Download seluruh direktori
aws s3 sync s3://my-app-storage-2026/website/ ./website-backup/

# List objects di bucket
aws s3 ls s3://my-app-storage-2026/

# List objects dengan prefix
aws s3 ls s3://my-app-storage-2026/photos/ --recursive

# List objects dengan ukuran
aws s3api list-objects-v2 \
  --bucket my-app-storage-2026 \
  --query 'Contents[].{Key: Key, Size: Size, Modified: LastModified}' \
  --output table

# Hapus object
aws s3 rm s3://my-app-storage-2026/photos/old-photo.jpg

# Hapus semua objects dengan prefix
aws s3 rm s3://my-app-storage-2026/temp/ --recursive

# Pindahkan object (copy + delete)
aws s3 mv s3://my-app-storage-2026/temp/file.txt \
  s3://my-app-storage-2026/archive/file.txt

# Copy antar bucket
aws s3 sync s3://source-bucket/ s3://destination-bucket/

Menggunakan Python (boto3)

Python
import boto3
from botocore.exceptions import ClientError

s3 = boto3.client('s3')
BUCKET = 'my-app-storage-2026'

# Upload file
def upload_file(file_path, s3_key):
    try:
        s3.upload_file(
            file_path, BUCKET, s3_key,
            ExtraArgs={
                'ContentType': 'image/jpeg',
                'Metadata': {
                    'uploaded-by': 'beebanelabs',
                    'purpose': 'tutorial'
                }
            }
        )
        print(f"Uploaded: {s3_key}")
        return True
    except ClientError as e:
        print(f"Error: {e}")
        return False

# Upload dari memory (tanpa file)
import io
def upload_from_memory(data, s3_key, content_type='text/plain'):
    s3.put_object(
        Bucket=BUCKET,
        Key=s3_key,
        Body=data,
        ContentType=content_type
    )

# Download file
def download_file(s3_key, local_path):
    s3.download_file(BUCKET, s3_key, local_path)

# List objects
def list_objects(prefix=''):
    response = s3.list_objects_v2(
        Bucket=BUCKET,
        Prefix=prefix
    )
    for obj in response.get('Contents', []):
        print(f"{obj['Key']} — {obj['Size']} bytes — {obj['LastModified']}")

# Generate presigned URL
def get_presigned_url(s3_key, expiration=3600):
    url = s3.generate_presigned_url(
        'get_object',
        Params={'Bucket': BUCKET, 'Key': s3_key},
        ExpiresIn=expiration
    )
    return url

# Multipart upload untuk file besar
from boto3.s3.transfer import TransferConfig

config = TransferConfig(
    multipart_threshold=1024 * 1024 * 100,  # 100MB
    max_concurrency=10,
    multipart_chunksize=1024 * 1024 * 100
)

s3.upload_file(
    'large-file.zip', BUCKET, 'uploads/large-file.zip',
    Config=config
)

# Contoh penggunaan
upload_file('./photo.jpg', 'photos/photo.jpg')
download_file('photos/photo.jpg', './downloaded.jpg')
list_objects('photos/')
print(get_presigned_url('photos/photo.jpg'))

4. Storage Classes

AWS S3 menyediakan beberapa storage class yang dioptimalkan untuk berbagai use case dan akses pattern. Memilih storage class yang tepat bisa menghemat biaya secara signifikan.

Perbandingan Storage Classes

Storage ClassAksesAvailabilityMin DurationHarga/GB/Bulan*Best For
S3 StandardFrequently99.99%-$0.023Hot data, web apps, content delivery
S3 Intelligent-TieringUnknown/Changing99.9%-$0.023 + monitoringUnknown access patterns
S3 Standard-IAInfrequent99.9%30 hari$0.0125Backups, disaster recovery
S3 One Zone-IAInfrequent99.5%30 hari$0.01Recreatable data, secondary backups
S3 Glacier InstantRare (ms retrieval)99.9%90 hari$0.004Archive yang perlu akses cepat
S3 Glacier FlexibleArchive (min 1min)99.99%90 hari$0.0036Long-term archive
S3 Glacier Deep ArchiveCold (12hr retrieval)99.99%180 hari$0.00099Compliance, long-term retention
S3 Express One ZoneFrequently (single AZ)99.95%-$0.16Ultra-low latency, ML training

* Harga untuk region US East, bisa berbeda di region lain

💡 Tips

Gunakan S3 Intelligent-Tiering jika Anda tidak yakin pola akses data. Layanan ini otomatis memindahkan data ke tier yang lebih murah jika tidak diakses selama 30 hari, tanpa biaya retrieval penalty.

Bash
# Upload dengan storage class tertentu
aws s3 cp ./archive.tar.gz s3://my-bucket/ --storage-class GLACIER

# Cek storage class object
aws s3api head-object --bucket my-bucket --key archive.tar.gz \
  --query 'StorageClass'

# Pindahkan object ke storage class berbeda (copy ke diri sendiri)
aws s3api copy-object \
  --bucket my-bucket \
  --key archive.tar.gz \
  --copy-source my-bucket/archive.tar.gz \
  --storage-class STANDARD_IA \
  --metadata-directive COPY

5. Versioning

Versioning memungkinkan Anda menyimpan multiple versi dari setiap object. Ini sangat penting untuk melindungi data dari penghapusan atau overwrite yang tidak disengaja.

Bash
# Enable versioning
aws s3api put-bucket-versioning \
  --bucket my-app-storage-2026 \
  --versioning-configuration Status=Enabled

# Cek status versioning
aws s3api get-bucket-versioning --bucket my-app-storage-2026
# {
#     "Status": "Enabled",
#     "MFADelete": "Disabled"
# }

# List semua versi object
aws s3api list-object-versions \
  --bucket my-app-storage-2026 \
  --prefix "photos/photo.jpg"
# {
#     "Versions": [
#         {"Key": "photos/photo.jpg", "VersionId": "v3_abc123", ...},
#         {"Key": "photos/photo.jpg", "VersionId": "v2_def456", ...},
#         {"Key": "photos/photo.jpg", "VersionId": "v1_ghi789", ...}
#     ]
# }

# Download versi spesifik
aws s3api get-object \
  --bucket my-app-storage-2026 \
  --key photos/photo.jpg \
  --version-id v2_def456 \
  ./photo-v2.jpg

# Hapus versi spesifik (permanent delete)
aws s3api delete-object \
  --bucket my-app-storage-2026 \
  --key photos/photo.jpg \
  --version-id v3_abc123

# Suspend versioning (tidak menghapus versi lama)
aws s3api put-bucket-versioning \
  --bucket my-app-storage-2026 \
  --versioning-configuration Status=Suspended

MFA Delete

Bash
# Enable MFA Delete (membutuhkan root account)
# Memerlukan MFA token untuk menghapus versi atau mengubah versioning
aws s3api put-bucket-versioning \
  --bucket my-app-storage-2026 \
  --versioning-configuration Status=Enabled,MFADelete=Enabled \
  --mfa "arn:aws:iam::123456789012:mfa/root-account-mfa-device 123456"
Diagram: S3 Versioning
┌──────────────────────────────────────────────────────────────┐
│                    S3 VERSIONING                              │
│                                                              │
│  Bucket: my-app-storage-2026                                │
│  Versioning: Enabled                                         │
│                                                              │
│  Key: photos/photo.jpg                                      │
│  ┌────────────────────────────────────────────┐             │
│  │  v3 (latest)  │ content_v3.jpg │ 2026-06-26│             │
│  │  v2           │ content_v2.jpg │ 2026-06-20│             │
│  │  v1           │ content_v1.jpg │ 2026-06-15│             │
│  └────────────────────────────────────────────┘             │
│                                                              │
│  Key: documents/report.pdf (DELETED)                        │
│  ┌────────────────────────────────────────────┐             │
│  │  v3 (delete   │ [delete marker]│ 2026-06-26│             │
│  │   marker)     │               │            │             │
│  │  v2           │ report_v2.pdf │ 2026-06-20│             │
│  │  v1           │ report_v1.pdf │ 2026-06-15│             │
│  └────────────────────────────────────────────┘             │
│                                                              │
│  Delete marker bisa dihapus untuk "restore" object           │
└──────────────────────────────────────────────────────────────┘

6. Lifecycle Management

Lifecycle rules mengotomatiskan transisi object antar storage class dan penghapusan object yang sudah tidak diperlukan. Ini sangat penting untuk mengelola biaya storage.

JSON
{
  "Rules": [
    {
      "ID": "TransitionToIA",
      "Status": "Enabled",
      "Filter": {
        "Prefix": "logs/"
      },
      "Transitions": [
        {
          "Days": 30,
          "StorageClass": "STANDARD_IA"
        },
        {
          "Days": 90,
          "StorageClass": "GLACIER"
        },
        {
          "Days": 365,
          "StorageClass": "DEEP_ARCHIVE"
        }
      ],
      "Expiration": {
        "Days": 730
      }
    },
    {
      "ID": "CleanupTempFiles",
      "Status": "Enabled",
      "Filter": {
        "Prefix": "temp/"
      },
      "Expiration": {
        "Days": 7
      }
    },
    {
      "ID": "ManageOldVersions",
      "Status": "Enabled",
      "Filter": {},
      "NoncurrentVersionTransitions": [
        {
          "NoncurrentDays": 30,
          "StorageClass": "STANDARD_IA"
        },
        {
          "NoncurrentDays": 90,
          "StorageClass": "GLACIER"
        }
      ],
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 365
      }
    },
    {
      "ID": "RemoveIncompleteMultipart",
      "Status": "Enabled",
      "Filter": {},
      "AbortIncompleteMultipartUpload": {
        "DaysAfterInitiation": 7
      }
    }
  ]
}
Bash
# Terapkan lifecycle rules
aws s3api put-bucket-lifecycle-configuration \
  --bucket my-app-storage-2026 \
  --lifecycle-configuration file://lifecycle.json

# Lihat lifecycle rules
aws s3api get-bucket-lifecycle-configuration \
  --bucket my-app-storage-2026

# Hapus lifecycle rules
aws s3api delete-bucket-lifecycle \
  --bucket my-app-storage-2026

7. IAM Policies & Bucket Policies

Keamanan S3 diatur melalui dua jenis policy: IAM Policy (attached ke user/role) dan Bucket Policy (attached ke bucket). Keduanya bekerja bersama untuk menentukan siapa yang bisa mengakses apa.

Bucket Policy — Contoh Umum

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAppReadAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/app-role"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-storage-2026",
        "arn:aws:s3:::my-app-storage-2026/*"
      ]
    },
    {
      "Sid": "DenyPublicAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-app-storage-2026",
        "arn:aws:s3:::my-app-storage-2026/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    },
    {
      "Sid": "AllowCloudFrontAccess",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-app-storage-2026/public/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234567890"
        }
      }
    }
  ]
}
Bash
# Terapkan bucket policy
aws s3api put-bucket-policy \
  --bucket my-app-storage-2026 \
  --policy file://bucket-policy.json

# Lihat bucket policy
aws s3api get-bucket-policy --bucket my-app-storage-2026

# Hapus bucket policy
aws s3api delete-bucket-policy --bucket my-app-storage-2026

IAM Policy untuk User/Role

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3Access",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-storage-2026",
        "arn:aws:s3:::my-app-storage-2026/uploads/*",
        "arn:aws:s3:::my-app-storage-2026/public/*"
      ]
    },
    {
      "Sid": "DenyDeleteProduction",
      "Effect": "Deny",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::my-app-storage-2026/production/*"
    }
  ]
}
âš ī¸ Peringatan

Jangan pernah membuat bucket S3 public kecuali Anda benar-benar tahu apa yang Anda lakukan. Banyak kebocoran data terjadi karena bucket S3 yang tidak sengaja di-public-kan. Selalu gunakan BlockPublicAcls dan akses melalui CloudFront atau presigned URLs.

8. Presigned URLs

Presigned URLs memungkinkan Anda memberikan akses sementara ke object S3 tanpa perlu membuat object tersebut public. URL ini memiliki waktu kedaluwarsa dan bisa digunakan untuk upload maupun download.

Menggunakan AWS CLI

Bash
# Generate presigned URL untuk download (berlaku 1 jam)
aws s3 presign s3://my-app-storage-2026/photos/photo.jpg \
  --expires-in 3600
# https://my-app-storage-2026.s3.ap-southeast-1.amazonaws.com/photos/photo.jpg?...

# Generate presigned URL untuk download (berlaku 7 hari)
aws s3 presign s3://my-app-storage-2026/documents/report.pdf \
  --expires-in 604800

# Generate presigned URL untuk upload
aws s3 presign s3://my-app-storage-2026/uploads/new-file.jpg \
  --expires-in 3600

# Gunakan URL untuk upload via curl
curl -X PUT \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg \
  "https://presigned-url-here"

Menggunakan Python (boto3)

Python
import boto3
from botocore.config import Config

s3 = boto3.client('s3',
    region_name='ap-southeast-1',
    config=Config(signature_version='s3v4')
)

BUCKET = 'my-app-storage-2026'

# Generate presigned URL untuk GET (download)
def generate_download_url(key, expires_in=3600):
    url = s3.generate_presigned_url(
        'get_object',
        Params={
            'Bucket': BUCKET,
            'Key': key,
            'ResponseContentDisposition': f'attachment; filename="{key.split("/")[-1]}"'
        },
        ExpiresIn=expires_in
    )
    return url

# Generate presigned URL untuk PUT (upload)
def generate_upload_url(key, content_type='application/octet-stream', expires_in=3600):
    url = s3.generate_presigned_url(
        'put_object',
        Params={
            'Bucket': BUCKET,
            'Key': key,
            'ContentType': content_type
        },
        ExpiresIn=expires_in
    )
    return url

# Generate presigned POST (untuk browser upload — lebih fleksibel)
def generate_presigned_post(key, content_type='image/jpeg', max_size=10*1024*1024):
    conditions = [
        ['content-length-range', 1, max_size],
        ['starts-with', '$Content-Type', content_type.split('/')[0]]
    ]

    fields = {
        'Content-Type': content_type,
        'x-amz-meta-uploaded-by': 'beebanelabs'
    }

    response = s3.generate_presigned_post(
        Bucket=BUCKET,
        Key=key,
        Fields=fields,
        Conditions=conditions,
        ExpiresIn=3600
    )
    return response
    # Returns: {'url': 'https://...', 'fields': {...}}

# Contoh penggunaan
download_url = generate_download_url('photos/photo.jpg', expires_in=7200)
upload_url = generate_upload_url('uploads/new-photo.jpg')
post_data = generate_presigned_post('uploads/user-avatar.jpg')

print(f"Download URL: {download_url}")
print(f"Upload URL: {upload_url}")
print(f"POST URL: {post_data['url']}")
print(f"POST Fields: {post_data['fields']}")

Browser Upload dengan Presigned POST

JavaScript
// Upload file langsung dari browser ke S3
async function uploadToS3(file) {
  // 1. Dapatkan presigned POST dari backend
  const response = await fetch('/api/get-upload-url', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      filename: file.name,
      contentType: file.type
    })
  });

  const { url, fields } = await response.json();

  // 2. Upload langsung ke S3
  const formData = new FormData();
  Object.entries(fields).forEach(([key, value]) => {
    formData.append(key, value);
  });
  formData.append('file', file);

  const uploadResponse = await fetch(url, {
    method: 'POST',
    body: formData
  });

  if (uploadResponse.ok) {
    console.log('Upload berhasil!');
  } else {
    console.error('Upload gagal');
  }
}

// Gunakan
document.getElementById('fileInput').addEventListener('change', (e) => {
  uploadToS3(e.target.files[0]);
});

9. Static Website Hosting

S3 bisa digunakan untuk meng-host static website (HTML, CSS, JavaScript) tanpa perlu server. Dipadukan dengan CloudFront untuk CDN global dan custom domain.

Bash
# Enable static website hosting
aws s3 website s3://my-website-bucket \
  --index-document index.html \
  --error-document error.html

# Upload website files
aws s3 sync ./public/ s3://my-website-bucket/ \
  --acl public-read

# Set content types
aws s3 cp ./public/index.html s3://my-website-bucket/ \
  --content-type "text/html" --acl public-read

aws s3 cp ./public/css/style.css s3://my-website-bucket/css/ \
  --content-type "text/css" --acl public-read

aws s3 cp ./public/js/app.js s3://my-website-bucket/js/ \
  --content-type "application/javascript" --acl public-read

# Dapatkan website URL
echo "Website URL: http://my-website-bucket.s3-website-ap-southeast-1.amazonaws.com"

Redirect Rules

XML
<!-- website-redirect-rules.xml -->
<RoutingRules>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals>old-page/</KeyPrefixEquals>
    </Condition>
    <Redirect>
      <ReplaceKeyPrefixWith>new-page/</ReplaceKeyPrefixWith>
      <HttpRedirectCode>301</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
  <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <HostName>app.example.com</HostName>
      <ReplaceKeyWith>index.html</ReplaceKeyWith>
    </Redirect>
  </RoutingRule>
</RoutingRules>

10. Encryption & Keamanan

Enkripsi di S3

EnkripsiPenjelasanKapan Digunakan
SSE-S3S3 manage encryption keys (AES-256)Kebanyakan use case standar
SSE-KMSAWS KMS manage keys (audit trail)Compliance, audit requirement
SSE-CCustomer provide encryption keysKontrol penuh atas keys
Client-SideEnkripsi sebelum upload ke S3Zero-trust, end-to-end encryption
Bash
# Set default encryption (SSE-S3)
aws s3api put-bucket-encryption \
  --bucket my-app-storage-2026 \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "AES256"
        }
      }
    ]
  }'

# Upload dengan KMS encryption
aws s3 cp ./sensitive-data.csv s3://my-app-storage-2026/secure/ \
  --sse aws:kms \
  --sse-kms-key-id alias/my-s3-key

# Upload dengan AES-256 encryption
aws s3 cp ./data.csv s3://my-app-storage-2026/ \
  --sse AES256

# Enable Object Lock (WORM — Write Once Read Many)
# Berguna untuk compliance
aws s3api put-object-lock-configuration \
  --bucket my-app-storage-2026 \
  --object-lock-configuration '{
    "ObjectLockEnabled": true,
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",
        "Days": 365
      }
    }
  }'

# Enable Transfer Acceleration (upload cepat via edge locations)
aws s3api put-bucket-accelerate-configuration \
  --bucket my-app-storage-2026 \
  --accelerate-configuration Status=Enabled

# Enable server access logging
aws s3api put-bucket-logging \
  --bucket my-app-storage-2026 \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-s3-logs",
      "TargetPrefix": "access-logs/"
    }
  }'

11. Best Practices

Keamanan

PraktikRekomendasi
Block Public AccessSelalu aktifkan Block Public Access di level bucket
Enkripsi DefaultAktifkan SSE-S3 atau SSE-KMS sebagai default encryption
Enforce HTTPSTambahkan bucket policy yang menolak request non-HTTPS
VersioningSelalu aktifkan versioning untuk bucket production
MFA DeleteAktifkan MFA Delete untuk bucket kritis
Access LoggingAktifkan server access logging untuk audit
Least PrivilegeBerikan permission seminimal mungkin sesuai kebutuhan
Object LockGunakan untuk data compliance (WORM)

Biaya & Performance

Naming Conventions

Bash
# Prefix yang baik (mudah di-manage)
photos/2026/06/26/photo.jpg
documents/reports/quarterly-report-q2-2026.pdf
users/{user_id}/avatars/avatar.jpg
backups/database/2026-06-26-dump.sql.gz
logs/app/2026/06/26/app-log-001.gz

# Hindari
PHOTO.JPG                    # Tidak ada hierarchy
a/b/c/d/e/f/file.txt         # Terlalu dalam
file with spaces.txt          # Spasi menyulitkan
â„šī¸ Catatan

S3 dirancang untuk durability 99.999999999% (11 nines) yang berarti jika Anda menyimpan 10 juta object, statistiknya hanya 1 object yang hilang per 10.000 tahun. Tapi ini bukan backup — tetap gunakan cross-region replication untuk disaster recovery.

12. Quiz Pemahaman

1. Berapa durability S3 Standard?

2. Apa itu Presigned URL?

3. Apa fungsi S3 Versioning?

4. Storage class mana yang paling murah untuk data yang sangat jarang diakses?

5. Mengapa bucket S3 sebaiknya tidak di-public-kan?

🔍 Zoom
100%
🎨 Tema