Cập Nhật Nội Dung CloudFront: Tạo Invalidation Để Xóa Cache Edge Ngay Lập Tức

Bạn vừa upload file mới lên S3, reload trang và vẫn thấy phiên bản cũ — đây là tình huống kinh điển khi làm việc với CloudFront. Cache tại edge location chưa hết TTL, nên CloudFront tiếp tục phục vụ object cũ từ bộ nhớ đệm mà không hề biết S3 đã thay đổi. Giải pháp trực tiếp nhất là tạo một CloudFront Invalidation để buộc các edge node xóa bản cache và fetch lại từ origin.

TL;DR — Cập Nhật Nội Dung CloudFront Nhanh

BướcHành độngKết quả
1Upload file mới lên S3Origin đã có nội dung mới
2Tạo Invalidation trên distributionEdge cache bị xóa
3Chờ invalidation hoàn tất (~1-5 phút)Request tiếp theo fetch từ S3
4Xác minh bằng curl hoặc browserNội dung mới được phục vụ

CloudFront Cache Hoạt Động Như Thế Nào

CloudFront duy trì một mạng lưới edge location toàn cầu. Khi người dùng request một object, CloudFront kiểm tra cache tại edge gần nhất. Nếu object đã được cache và chưa hết TTL, CloudFront trả về bản cache mà không liên hệ S3. Đây là lý do tại sao việc thay file trên S3 không tự động cập nhật những gì người dùng nhìn thấy.

TTL được kiểm soát bởi header Cache-Control trên object S3, hoặc bởi cài đặt cache behavior trong CloudFront distribution. Nếu bạn set TTL là 86400 giây (1 ngày), edge location sẽ giữ bản cũ tối đa 24 giờ trừ khi bị invalidate.

graph LR User["Người dùng"] --> Edge["CloudFront Edge Location"] Edge -->|"Cache Hit (TTL còn hạn)"| User Edge -->|"Cache Miss / Invalidated"| S3["S3 Origin"] S3 -->|"Object mới"| Edge Edge -->|"Trả về + cache lại"| User Inv["CloudFront Invalidation"] -->|"Đánh dấu expired"| Edge style Inv fill:#ff9900,color:#fff style S3 fill:#3f8624,color:#fff style Edge fill:#1a73e8,color:#fff
  1. User Request: Trình duyệt gửi request đến CloudFront edge location gần nhất.
  2. Cache Hit: Nếu object còn trong cache và TTL chưa hết, edge trả về ngay — S3 không được hỏi.
  3. Cache Miss / Invalidated: Edge fetch từ S3 origin, cache lại, rồi trả về cho user.
  4. Invalidation: Khi bạn tạo invalidation, CloudFront đánh dấu object là expired tại tất cả edge location, buộc request tiếp theo phải fetch từ origin.

Tạo CloudFront Invalidation — Từng Bước Cụ Thể

Có hai cách chính: AWS Management Console và AWS CLI. CLI phù hợp hơn cho automation và CI/CD pipeline.

Cách 1: Dùng AWS CLI (Khuyến nghị cho Automation)

Trước tiên, xác định Distribution ID của bạn. Nếu chưa biết, lấy danh sách distributions:

aws cloudfront list-distributions \
  --query 'DistributionList.Items[*].{ID:Id,Domain:DomainName,Status:Status}' \
  --output table

Sau khi có Distribution ID, tạo invalidation cho một file cụ thể:

aws cloudfront create-invalidation \
  --distribution-id E1EXAMPLE12345 \
  --paths '/images/banner.jpg'

Để invalidate nhiều path cùng lúc:

aws cloudfront create-invalidation \
  --distribution-id E1EXAMPLE12345 \
  --paths '/images/banner.jpg' '/css/style.css' '/js/app.js'

Để invalidate toàn bộ cache (dùng cẩn thận — xem phần lưu ý về chi phí bên dưới):

aws cloudfront create-invalidation \
  --distribution-id E1EXAMPLE12345 \
  --paths '/*'

Output của lệnh trên sẽ trả về một Invalidation object với IdStatus: InProgress. Lưu lại Invalidation ID để theo dõi trạng thái.

Theo Dõi Trạng Thái Invalidation

Invalidation không hoàn tất ngay lập tức. Dùng lệnh sau để kiểm tra khi nào nó chuyển sang Completed:

aws cloudfront get-invalidation \
  --distribution-id E1EXAMPLE12345 \
  --id IEXAMPLE123456

Nếu muốn chờ cho đến khi invalidation hoàn tất trong script:

aws cloudfront wait invalidation-completed \
  --distribution-id E1EXAMPLE12345 \
  --id IEXAMPLE123456

Cách 2: Dùng AWS Management Console

  1. Mở CloudFront console → chọn distribution của bạn.
  2. Chọn tab Invalidations → nhấn Create invalidation.
  3. Nhập object path (ví dụ: /images/banner.jpg hoặc /*).
  4. Nhấn Create invalidation và chờ status chuyển sang Completed.

IAM Permission Cần Thiết

Nếu bạn đang chạy lệnh CLI từ một IAM role hoặc user, cần đảm bảo có permission sau. Thiếu permission này sẽ gặp lỗi AccessDenied mà không có thông báo rõ ràng về nguyên nhân.

🔽 Xem IAM Policy mẫu cho CloudFront Invalidation
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontInvalidation",
      "Effect": "Allow",
      "Action": [
        "cloudfront:CreateInvalidation",
        "cloudfront:GetInvalidation",
        "cloudfront:ListInvalidations"
      ],
      "Resource": "arn:aws:cloudfront::123456789012:distribution/E1EXAMPLE12345"
    },
    {
      "Sid": "AllowListDistributions",
      "Effect": "Allow",
      "Action": "cloudfront:ListDistributions",
      "Resource": "*"
    }
  ]
}

Lưu ý: cloudfront:ListDistributions yêu cầu "Resource": "*" vì đây là list action không hỗ trợ resource-level restriction. Các action còn lại có thể scope xuống ARN của distribution cụ thể.

Cập Nhật Nội Dung CloudFront — Chiến Lược Dài Hạn Tốt Hơn

Invalidation giải quyết vấn đề ngay lập tức, nhưng nếu bạn deploy thường xuyên, có một cách tiếp cận tốt hơn về mặt kiến trúc: cache busting bằng versioned filename.

graph TD Deploy["Deploy mới"] --> Q{"Chiến lược cache update"} Q -->|"Reactive"| Inv["Invalidation /index.html, /app.js"] Q -->|"Proactive"| VF["Versioned Filename app.v2.js, style.v3.css"] Q -->|"Kết hợp"| Combo["Invalidation cho HTML Versioned cho Assets"] Inv --> Cost["Phát sinh chi phí nếu vượt 1000 paths/tháng"] VF --> NoCost["Không cần invalidation Không phát sinh chi phí"] Combo --> Best["Tối ưu chi phí và độ phức tạp"] style Best fill:#3f8624,color:#fff style Cost fill:#d13212,color:#fff style NoCost fill:#1a73e8,color:#fff
  1. Invalidation (reactive): File giữ nguyên tên, bạn phải tạo invalidation sau mỗi lần thay đổi. Phát sinh chi phí nếu vượt quota miễn phí.
  2. Versioned Filename (proactive): Mỗi lần deploy, file có tên mới (ví dụ: app.v2.js thay vì app.js). CloudFront tự động fetch file mới vì cache key khác. Không cần invalidation.
  3. Kết hợp: Dùng versioned filename cho static assets (JS, CSS, images), dùng invalidation cho HTML và các file không thể đổi tên.
Nghĩ về invalidation như việc gọi điện cho từng bưu cục để thu hồi thư đã gửi. Versioned filename giống như gửi thư mới với địa chỉ khác — không cần thu hồi gì cả.

Lưu Ý Về Chi Phí Invalidation

AWS cung cấp 1.000 invalidation path miễn phí mỗi tháng cho mỗi distribution. Sau đó, mỗi path tính phí riêng. Wildcard path như /* được tính là một path duy nhất, không phải theo số lượng file thực tế bị invalidate.

Pricing và quota cụ thể thay đổi theo thời gian — luôn kiểm tra trang pricing chính thức của AWS CloudFront.

Kinh Nghiệm Thực Tế: Khi Invalidation Chạy Xong Nhưng Vẫn Thấy Nội Dung Cũ

Đây là tình huống hay gặp nhất và thường bị chẩn đoán sai. Invalidation đã Completed, nhưng reload trang vẫn thấy file cũ. Phản xạ đầu tiên thường là 'CloudFront chưa propagate xong' và tiếp tục chờ. Sai.

Nguyên nhân thực tế thường là browser cache hoặc cache của ISP/proxy trung gian, không phải CloudFront. CloudFront đã xóa cache rồi, nhưng trình duyệt vẫn đang dùng bản local của nó.

Cách xác minh nhanh: dùng curl với header để bypass browser cache và kiểm tra response header từ CloudFront:

curl -I -H 'Cache-Control: no-cache' \
  'https://d1example.cloudfront.net/images/banner.jpg'

Kiểm tra header X-Cache trong response:

  • X-Cache: Miss from cloudfront — CloudFront đã fetch từ origin (invalidation hoạt động đúng).
  • X-Cache: Hit from cloudfront — CloudFront vẫn đang serve từ cache (invalidation chưa propagate đến edge này, hoặc path không khớp).

Nếu curl trả về nội dung mới nhưng browser vẫn hiển thị cũ, vấn đề nằm ở browser cache — không phải CloudFront. Hard refresh (Ctrl+Shift+R hoặc Cmd+Shift+R) hoặc mở incognito window để xác nhận.

Một điểm tinh tế hơn: path trong invalidation phải khớp chính xác với cache key. Nếu distribution của bạn có cấu hình forward query string, thì /images/banner.jpg/images/banner.jpg?v=1 là hai cache entry khác nhau. Invalidate /images/banner.jpg sẽ không xóa entry có query string.

Tích Hợp Invalidation Vào CI/CD Pipeline

Trong môi trường production, invalidation nên chạy tự động sau mỗi lần deploy. Ví dụ đơn giản cho script deploy:

🔽 Xem script deploy mẫu với S3 sync và CloudFront invalidation
#!/bin/bash
set -e

DISTRIBUTION_ID="E1EXAMPLE12345"
S3_BUCKET="s3://my-website-bucket"
BUILD_DIR="./dist"

# Sync build artifacts lên S3
aws s3 sync "$BUILD_DIR" "$S3_BUCKET" \
  --delete \
  --cache-control 'max-age=31536000' \
  --exclude 'index.html'

# HTML không cache lâu
aws s3 cp "$BUILD_DIR/index.html" "$S3_BUCKET/index.html" \
  --cache-control 'no-cache, no-store, must-revalidate'

# Invalidate HTML files (assets dùng versioned filename nên không cần)
INVALIDATION_ID=$(aws cloudfront create-invalidation \
  --distribution-id "$DISTRIBUTION_ID" \
  --paths '/index.html' \
  --query 'Invalidation.Id' \
  --output text)

echo "Invalidation created: $INVALIDATION_ID"

# Chờ invalidation hoàn tất
aws cloudfront wait invalidation-completed \
  --distribution-id "$DISTRIBUTION_ID" \
  --id "$INVALIDATION_ID"

echo "Deploy hoàn tất. Nội dung mới đã được phục vụ."

Wrap-Up: Cập Nhật Nội Dung CloudFront Hiệu Quả

Khi cần xóa cache CloudFront ngay lập tức, aws cloudfront create-invalidation là công cụ trực tiếp nhất. Nhưng nếu bạn deploy thường xuyên, kết hợp versioned filename cho static assets với invalidation chỉ cho HTML sẽ giảm chi phí và phức tạp vận hành đáng kể. Luôn xác minh bằng curl -I và kiểm tra header X-Cache trước khi kết luận invalidation không hoạt động — browser cache là thủ phạm phổ biến hơn bạn nghĩ.

Tham khảo thêm: AWS CloudFront Developer Guide — Invalidating Files.

Glossary — Thuật Ngữ Chính

Thuật ngữGiải thích
Edge LocationĐiểm hiện diện (PoP) của CloudFront trên toàn cầu, nơi cache được lưu trữ gần người dùng.
InvalidationYêu cầu gửi đến CloudFront để đánh dấu một hoặc nhiều object là hết hạn tại tất cả edge location.
TTL (Time To Live)Thời gian tối đa một object được giữ trong cache trước khi CloudFront fetch lại từ origin.
Cache KeyĐịnh danh duy nhất cho một cached object, thường là URL path và có thể bao gồm query string hoặc header tùy cấu hình.
Cache BustingKỹ thuật thêm version vào tên file hoặc URL để buộc browser và CDN fetch phiên bản mới mà không cần invalidation.

Nhận xét

Bài đăng phổ biến từ blog này

EC2 Không Có Internet Trong Custom VPC: Cách Gắn Internet Gateway và Cập Nhật Route Table

RDS Multi-AZ: Lợi Ích Thực Sự Là Gì và Khi Nào Nên Bật?

Lỗi CORS trên API Gateway: Cách bật CORS và Lambda phải trả về header gì