jvinhit//lab

Search posts

Type to search across journal entries.

↑ ↓ navigate ↵ open esc close

AWS cho Frontend Developer · Phần 16 — Lộ trình 8 tuần, dự án, từ điển, checklist và phỏng vấn

Bài khép series: lộ trình 8 tuần với tiêu chí hoàn thành từng tuần, năm dự án thực hành để bỏ vào CV, từ điển thuật ngữ, checklist trước khi lên production và mười câu hỏi phỏng vấn thường gặp kèm câu trả lời.

Mười lăm phần trước đi theo thứ tự của một request: từ tên miền qua CloudFront tới S3, ALB, backend, rồi tới cấu hình, pipeline và vận hành. Bài cuối này đổi góc nhìn sang thứ tự của người học: tuần nào đọc gì, làm gì, và làm xong thì phải chứng minh được gì trước khi sang tuần sau. Phần sau của bài là bốn bảng tra cứu bạn sẽ quay lại nhiều lần sau khi series đã đọc xong: năm dự án thực hành, từ điển thuật ngữ, checklist trước khi lên production, và câu hỏi phỏng vấn.

Nguyên tắc xuyên suốt vẫn là của Phần 1: đọc lý thuyết 30%, thực hành 70%. Kiến thức hạ tầng chỉ dính lại khi bạn tự tay gây ra lỗi và tự sửa — và tài khoản học tập với budget 5 USD là môi trường an toàn để phá.


Lộ trình 8 tuần

Mỗi tuần khoảng 6–8 giờ. Làm lần lượt, mỗi tuần có tiêu chí hoàn thành rõ ràng — đừng sang tuần sau khi chưa đạt. Cột “Đọc” trỏ tới phần tương ứng trong series.

Tuần 1 — Nền móng và mạng

  • Đọc: Phần 1, Phần 2, Phần 3.
  • Làm: Lab 1.1 (tạo tài khoản, budget, IAM user, CLI). Lab 2.1 (đọc header của một trang thật). Mua một tên miền rẻ (.io.vn, .xyz… khoảng 100–300 nghìn/năm) — có tên miền thật sẽ học nhanh hơn rất nhiều.
  • Xong khi: chạy được aws sts get-caller-identity; vẽ lại được sơ đồ vòng đời request mà không cần nhìn; giải thích được CORS preflight cho người khác.

Tuần 2 — S3 và file tĩnh

  • Đọc: Phần 4, Phần 5.
  • Làm: Lab 3.1. Viết một script bash deploy.sh tự động hoá hai bước sync. Thử đặt sai Content-Type cho file CSS rồi quan sát trang vỡ — tự tay gây lỗi là cách nhớ tốt nhất.
  • Xong khi: giải thích được vì sao asset và HTML cần Cache-Control khác nhau; biết bucket đang kín và vì sao truy cập trực tiếp bị 403.

Tuần 3 — CloudFront (tuần quan trọng nhất)

  • Đọc: Phần 6, Phần 7, Phần 8 — đọc kỹ, đọc hai lần.
  • Làm: Lab 4.1 toàn bộ. Gắn tên miền thật + chứng chỉ ACM ở us-east-1. Tạo bản ghi ALIAS trong Route 53. Thử cả hai cách SPA fallback (Custom Error Response và CloudFront Function) và so sánh.
  • Xong khi: https://tên-miền-của-bạn chạy, F5 ở route con không lỗi, X-Cache chuyển từ Miss sang Hit, và bạn hiểu rõ vì sao phải invalidate.

Tuần 4 — Backend, ALB và mạng

  • Đọc: Phần 9, Phần 10.
  • Làm: Lab 5.1. Viết API Node.js nhỏ, đóng gói Docker, đẩy lên ECR, chạy trên ECS Fargate sau ALB. Tự tạo cả ba lỗi 502/503/504.
  • Xong khi: nhìn mã lỗi là đoán được nguyên nhân; hiểu Security Group nào cho phép cái gì; biết vì sao deploy mất vài phút.

Tuần 5 — Ghép hai nửa lại

  • Đọc: xem lại mục CORS ở Phần 3 và behavior ở Phần 6.
  • Làm: thêm behavior /api/* vào CloudFront trỏ tới ALB, bật đủ 7 method, gắn Origin Request Policy AllViewer, Cache Policy CachingDisabled. Sửa frontend gọi /api/... thay vì URL tuyệt đối.
  • Xong khi: frontend và API cùng một tên miền, không còn dòng CORS nào trong code backend, POST/PUT/DELETE đều chạy.

Tuần 6 — Xác thực và cấu hình

  • Đọc: Phần 11, Phần 12.
  • Làm: dựng Cognito User Pool, làm luồng đăng ký/đăng nhập. Triển khai mẫu runtime config.json. Làm tính năng upload ảnh bằng presigned URL.
  • Xong khi: đăng nhập được, token tự refresh khi hết hạn, upload ảnh thành công, và bạn đổi được API URL mà không cần build lại.

Tuần 7 — Tự động hoá

  • Đọc: Phần 13.
  • Làm: Lab 9.1. Thiết lập OIDC, viết workflow hoàn chỉnh. Thêm môi trường staging (bucket + distribution riêng). Làm preview cho PR nếu còn thời gian.
  • Xong khi: push lên main là tự deploy; pipeline tự dừng khi test fail; không còn access key cố định nào trong GitHub Secrets.

Tuần 8 — Vận hành và hoàn thiện

  • Đọc: Phần 14, Phần 15.
  • Làm: gắn Response Headers Policy. Triển khai CSP ở chế độ Report-Only. Tạo CloudWatch Alarm cho 5xx và UnHealthyHostCount. Bật CloudFront logs. Chạy Lighthouse, tối ưu theo kết quả. Rà lại toàn bộ checklist bên dưới.
  • Xong khi: có cảnh báo tự động khi web lỗi; điểm Lighthouse Performance trên 90; hoá đơn AWS nằm trong budget đã đặt.

Nếu bạn đi song song hai series khác của blog, lộ trình tương ứng nằm ở Cloudflare phần 13 (4 tuần) và K8s phần 19 (6 tuần); ba lộ trình dùng chung Tuần 1 về DNS, TLS, HTTP header.


Năm dự án thực hành

#Dự ánKiến thức rèn luyệnĐộ khó
1Portfolio cá nhân, tên miền riêngS3 + CloudFront + Route 53 + ACM + GitHub Actions. Bài tập khởi động bắt buộcDễ
2Thư viện ảnh có uploadPresigned URL, CORS trên S3, Cognito, sinh thumbnail bằng Lambda kích hoạt từ sự kiện S3Trung bình
3Dashboard SPA + API thậtKiến trúc cùng origin qua CloudFront, ALB, ECS Fargate, cookie httpOnly, không CORSTrung bình
4Blog Next.js SSG + ISRAmplify Hosting hoặc OpenNext, SEO, sitemap, preview theo PR, tối ưu Core Web VitalsTrung bình
5Hệ thống nhiều môi trườngdev/staging/prod tách biệt, runtime config, deploy theo phiên bản, rollback tức thì, alarm, dashboardKhó

Làm theo thứ tự: dự án 1 là điều kiện cho mọi dự án sau (bạn cần một distribution và pipeline chạy được để xây thêm). Dự án 2 và 3 độc lập, chọn cái gần với công việc hơn. Dự án 5 là tổng hợp: nó không thêm dịch vụ mới nào mà nhân đôi mọi thứ đã có cho ba môi trường — chỗ khó là kỷ luật, không phải kỹ thuật.

Ghi chú: cách biến dự án thành điểm cộng khi xin việc. Với mỗi dự án, viết một file ARCHITECTURE.md trong repo: sơ đồ kiến trúc, lý do chọn từng dịch vụ, vấn đề gặp phải và cách giải quyết, ước tính chi phí. Người phỏng vấn đánh giá tài liệu này cao hơn nhiều so với bản thân dòng code — nó cho thấy bạn hiểu chứ không chỉ làm theo hướng dẫn.

Khung ARCHITECTURE.md đủ dùng, mỗi mục vài đoạn:

# Kiến trúc

## Sơ đồ
(ASCII hoặc ảnh: Route 53 → CloudFront → S3 / ALB → ECS → RDS)

## Vì sao chọn từng dịch vụ
- CloudFront + S3 thay vì Amplify: cần kiểm soát cache policy và behavior /api/*
- ECS Fargate thay vì Lambda: tải đều, backend có WebSocket

## Vấn đề đã gặp và cách giải quyết
- F5 ở /orders/123 bị 403 → CloudFront Function rewrite về /index.html
- Lỗi CORS lúc có lúc không → thiếu Origin trong cache key

## Chi phí ước tính
- ALB 18 USD, Fargate 2 task 15 USD, RDS t4g.micro 12 USD, còn lại dưới 3 USD

## Việc chưa làm
- WAF, Origin Shield, canary

Mục “Việc chưa làm” đáng giá ngang các mục khác: nó cho thấy bạn biết ranh giới của thứ mình đã xây.


Từ điển thuật ngữ

Thuật ngữGiải thích ngắn
ACMAWS Certificate Manager — cấp chứng chỉ TLS miễn phí, tự gia hạn
ALBApplication Load Balancer — chia tải và định tuyến HTTP theo path/host
ARNAmazon Resource Name — chuỗi định danh duy nhất của mọi tài nguyên AWS
AZAvailability Zone — trung tâm dữ liệu độc lập trong một region
BehaviorLuật định tuyến theo path trong CloudFront
Cache keyChuỗi CloudFront dùng để tra cứu cache; mặc định host + path
Cache PolicyCấu hình CloudFront quyết định thứ gì đưa vào cache key và TTL
Cold startĐộ trễ lần gọi đầu của Lambda khi phải khởi tạo môi trường
CloudFront FunctionHàm JavaScript nhỏ chạy ở edge tại Viewer Request/Response, dưới 1 ms, không gọi mạng
Custom Error ResponseCấu hình CloudFront đổi mã lỗi từ origin thành trang khác (SPA fallback 403/404 → /index.html 200)
Deregistration delayThời gian ALB chờ request đang chạy hoàn tất trước khi gỡ hẳn một target
DistributionMột cấu hình CloudFront
ECRElastic Container Registry — nơi lưu Docker image
ECS / FargateDịch vụ chạy container; Fargate là chế độ không cần quản lý máy chủ
Edge locationĐiểm hiện diện CloudFront, gần người dùng
Health checkKiểm tra định kỳ xem target còn sống không
Hosted UITrang đăng nhập dựng sẵn của Cognito
IaCInfrastructure as Code — khai báo hạ tầng bằng file (Terraform, CDK)
IAMHệ thống phân quyền của AWS
Identity PoolThành phần Cognito đổi JWT lấy thông tin xác thực AWS tạm thời cho trình duyệt
InvalidationLệnh xoá cache ở toàn bộ edge CloudFront
ISRIncremental Static Regeneration — Next.js sinh lại trang tĩnh nền
Lambda@EdgeHàm Lambda chạy ở edge tại cả bốn sự kiện, gọi mạng được, đắt hơn CloudFront Function
LCULoad Balancer Capacity Unit — đơn vị tính phí theo tải của ALB
Lifecycle ruleLuật S3 tự xoá hoặc chuyển tầng object theo tuổi (dọn noncurrent version, dọn releases/ cũ)
NAT GatewayCho tài nguyên trong private subnet ra Internet. Đắt, hay bị quên
OACOrigin Access Control — cách chuẩn để chỉ CloudFront đọc được S3
OIDCOpenID Connect — cơ chế GitHub Actions chứng minh danh tính với AWS để nhận quyền tạm thời, không cần access key
OriginNguồn gốc nội dung của CloudFront (S3, ALB…)
Origin Request PolicyCấu hình CloudFront quyết định header, cookie, query nào được chuyển tiếp xuống origin
Origin ShieldLớp cache trung gian của CloudFront trước origin, giảm số request tới origin
PreflightRequest OPTIONS trình duyệt tự gửi trước request cross-origin “không đơn giản”
Presigned URLURL có chữ ký, cho phép thao tác S3 trong thời gian giới hạn
RegionKhu vực địa lý chứa nhiều AZ
Response Headers PolicyCấu hình CloudFront tự gắn header (security, CORS) vào response
Route 53Dịch vụ DNS của AWS
S3Object storage — nơi chứa file build
Security GroupFirewall có trạng thái ở mức tài nguyên
SSEServer-Sent Events — server đẩy dữ liệu một chiều qua HTTP
SSM Parameter StoreNơi lưu cấu hình và secret đơn giản cho backend, miễn phí bậc standard
Target GroupNhóm đích mà ALB gửi request tới
TTLTime To Live — thời gian một giá trị được phép lưu cache
VPCMạng riêng ảo của bạn trên AWS
WAFWeb Application Firewall — lọc request độc hại

Checklist trước khi lên production

Rà từng dòng, tick bằng cách kiểm tra thật (lệnh curl, head-object, console), không tick theo trí nhớ.

Hạ tầng

  • Bucket S3 chặn toàn bộ public access, dùng OAC
  • Bucket policy có điều kiện SourceArn đúng distribution
  • Chứng chỉ ACM ở us-east-1 cho CloudFront, đã ở trạng thái Issued
  • Viewer protocol policy = Redirect HTTP to HTTPS
  • Bản ghi ALIAS trong Route 53 trỏ đúng distribution
  • S3 Versioning bật + lifecycle xoá bản cũ

Cache

  • Asset có hash: public, max-age=31536000, immutable
  • index.html: no-cache, must-revalidate
  • config.json và service-worker.js: no-store / no-cache
  • Behavior /assets/* dùng CachingOptimized
  • Bật Compress trên mọi behavior phục vụ text
  • Pipeline invalidate /index.html sau mỗi deploy

Routing

  • SPA fallback cho cả 403 và 404, trả mã 200, TTL 0
  • Behavior /api/* bật đủ 7 HTTP method
  • Behavior /api/* dùng CachingDisabled + AllViewer
  • Thứ tự behavior đúng: cụ thể trước, * sau cùng

Bảo mật

  • Không có AWS key nào trong code frontend hoặc Git history
  • Không có secret trong biến VITE_* / NEXT_PUBLIC_*
  • Response Headers Policy đã gắn (HSTS, nosniff, frame-options…)
  • CSP đã chạy Report-Only ít nhất một tuần trước khi bật chặn
  • Cookie có Secure, HttpOnly, SameSite phù hợp
  • CI/CD dùng OIDC role, không dùng access key cố định
  • Trust policy của role giới hạn đúng repo và nhánh

Backend (nếu có)

  • Health check path riêng, nhẹ, trả 200 (không redirect)
  • App lắng nghe 0.0.0.0 chứ không phải localhost
  • trust proxy đã bật để đọc X-Forwarded-*
  • Deregistration delay giảm xuống mức hợp lý
  • Security Group dùng SG khác làm nguồn, không mở 0.0.0.0/0

Vận hành

  • CloudWatch Alarm cho 5xx rate và UnHealthyHostCount
  • CloudWatch Logs có retention (không để vĩnh viễn)
  • AWS Budget + Cost Anomaly Detection đã bật
  • Có quy trình rollback đã thử ít nhất một lần
  • Kiểm tra sau deploy tự động trong pipeline
  • Tài nguyên có tag đầy đủ

Mỗi dòng ứng với một phần trong series; dòng nào bạn không giải thích được vì sao nó ở đó, hãy quay lại phần tương ứng — đó là cách tự kiểm tra rẻ nhất.


Câu hỏi phỏng vấn thường gặp

Mười câu dưới đây là những câu người phỏng vấn dùng để tách “đã deploy lên AWS” khỏi “hiểu cái mình deploy”. Câu trả lời mẫu cố ý ngắn: bạn nên nói được trong một phút rồi sẵn sàng đào sâu khi bị hỏi tiếp.

1. Mô tả cách bạn deploy một React SPA lên AWS. Build ra file tĩnh, upload lên S3 với bucket chặn public access. Đặt CloudFront phía trước dùng OAC để chỉ CloudFront đọc được bucket. Gắn tên miền qua Route 53 bản ghi ALIAS, chứng chỉ từ ACM ở us-east-1. Cấu hình Custom Error Response cho 403 và 404 trả /index.html mã 200 để SPA routing hoạt động khi refresh. Asset có hash cache một năm với immutable, index.html để no-cache. CI/CD dùng GitHub Actions với OIDC, upload asset trước rồi HTML sau, cuối cùng invalidate /index.html.

2. Vì sao refresh trang ở route con bị lỗi, còn click link thì bình thường? Click link là client-side routing — React Router đổi URL bằng history.pushState, không có request nào rời trình duyệt. Refresh thì trình duyệt gửi request thật lên server. S3 là object storage, chỉ khớp key chính xác, không có khái niệm thư mục, nên không tìm thấy object products/123 và trả 403 hoặc 404. Sửa bằng Custom Error Response hoặc CloudFront Function rewrite URI về /index.html.

3. Deploy xong nhưng người dùng vẫn thấy bản cũ. Bạn kiểm tra thế nào? Xem header X-Cache và Age của response. Kiểm tra Cache-Control của index.html bằng aws s3api head-object — nếu nó có max-age dài thì đó là nguyên nhân. Kiểm tra pipeline có bước invalidation không. Kiểm tra Service Worker trong DevTools → Application, vì SW có thể phục vụ bản cũ từ cache của nó. Giải pháp lâu dài: asset dùng hash trong tên nên không cần invalidate, chỉ index.html cần no-cache và được invalidate.

4. Giải thích CORS và cách khắc phục trên AWS. CORS là cơ chế của trình duyệt chặn JavaScript đọc response từ origin khác, trừ khi server đồng ý bằng header. Request vẫn được gửi và server vẫn xử lý — chỉ là JS không đọc được kết quả. Với request có header tuỳ chỉnh hoặc content-type JSON, trình duyệt gửi preflight OPTIONS trước. Trên AWS, sửa ở đâu tuỳ kiến trúc: S3 có cấu hình CORS riêng, API Gateway có mục CORS, sau ALB thì backend tự trả header. Nếu có CloudFront thì phải đảm bảo header Origin được forward và nằm trong cache key, nếu không sẽ trả nhầm header CORS giữa các origin. Cách tốt nhất là tránh CORS hoàn toàn: đặt /api/* làm một behavior trong cùng CloudFront distribution với frontend.

5. Phân biệt 502, 503, 504 từ ALB. 502 là ALB kết nối được target nhưng nhận response không hợp lệ — thường do lỗi trong code backend hoặc target chết giữa chừng. 503 là không có target nào khoẻ mạnh — health check đang fail, thường do Security Group, sai cổng, hoặc app không lắng nghe 0.0.0.0. 504 là target không trả lời trong thời gian idle timeout — backend xử lý quá lâu hoặc bị firewall chặn im lặng.

6. Làm sao cho user upload file lớn mà không làm nghẽn backend? Dùng presigned URL. Frontend gọi backend xin URL, backend kiểm tra quyền và loại file rồi ký một URL có thời hạn ngắn bằng IAM. Frontend PUT file thẳng lên S3, không đi qua backend. Sau đó báo backend key để lưu vào database. Cần cấu hình CORS trên bucket cho method PUT, và Content-Type lúc PUT phải khớp chính xác với lúc ký, nếu không sẽ lỗi SignatureDoesNotMatch.

7. Biến VITE_API_URL có an toàn để chứa API key không? Không. Các biến này được thay thế bằng giá trị thật ngay lúc build và nằm nguyên văn trong bundle JavaScript mà ai cũng tải về được. Chúng chỉ là cách cấu hình lúc build, không phải cơ chế bảo mật. Secret thật phải ở backend, lưu trong SSM Parameter Store hoặc Secrets Manager. Nếu frontend cần dịch vụ bên thứ ba có khoá bí mật thì làm endpoint proxy ở backend.

8. Vì sao không dùng access key cố định cho CI/CD? Access key tồn tại vĩnh viễn cho tới khi bị thu hồi thủ công, nên nếu lộ thì kẻ tấn công dùng được lâu dài. Cách đúng là OIDC: GitHub Actions tự chứng minh danh tính với AWS bằng token ngắn hạn, AWS trả về thông tin xác thực tạm thời sống vài phút. Không có gì bí mật lưu trữ lâu dài. Quan trọng là trust policy phải giới hạn đúng repo và nhánh — để repo:org/* nghĩa là mọi repo trong tổ chức đều deploy được lên production.

9. Cache Policy và Origin Request Policy khác nhau thế nào? Cache Policy quyết định những gì đưa vào cache key — ảnh hưởng tỷ lệ cache hit. Origin Request Policy quyết định những gì được chuyển tiếp xuống origin — ảnh hưởng thông tin backend nhận được. Một thứ có thể được forward mà không nằm trong cache key. Ví dụ với API: forward tất cả cookie xuống backend nhưng không cache gì cả. Ngược lại, thứ nằm trong cache key thì luôn được forward.

10. Chi phí lớn nhất khi host một SPA trên AWS là gì? Với SPA thuần tĩnh, chi phí gần như bằng 0 — S3 lưu vài chục MB không đáng kể, dữ liệu từ S3 sang CloudFront miễn phí, CloudFront có bậc miễn phí rộng, chỉ có Route 53 hosted zone khoảng 0,5 USD mỗi tháng. Chi phí thực sự xuất hiện khi thêm backend chạy liên tục: ALB tốn khoảng 16–20 USD mỗi tháng dù không ai truy cập, cộng Fargate và RDS. Nếu tải thất thường thì API Gateway + Lambda rẻ hơn nhiều vì không dùng thì không trả tiền.

Bốn câu hay gặp thêm

11. Build-time và runtime config khác nhau thế nào, chọn cái nào? Build-time nhúng giá trị vào bundle lúc build: nhanh, nhưng mỗi môi trường một bản build. Runtime tải config.json trước khi mount: thêm một request nhỏ, nhưng một bản build đi qua dev/staging/prod và staging giống hệt prod. Chọn runtime khi có nhiều hơn một môi trường; config.json phải no-store và được invalidate.

12. Health check nên viết thế nào? Endpoint riêng (/healthz), trả 200 trong vài mili giây, không truy vấn DB nặng, không đi qua auth hay redirect, tiến trình lắng nghe 0.0.0.0. Nó trả lời “tiến trình có nhận HTTP không”; tình trạng DB để cho alarm. Giảm healthy threshold và deregistration delay để deploy không mất bảy phút.

13. Vì sao nên tránh sticky session? Nó buộc user về một target nên tải lệch, deploy làm mất session nếu session ở bộ nhớ target, và thêm target không giúp user cũ. Làm backend stateless: session ở Redis hoặc dùng JWT, rồi tắt sticky session.

14. HTTP API và REST API của API Gateway chọn cái nào? HTTP API: rẻ hơn khoảng 70%, nhanh hơn, CORS và JWT authorizer với Cognito tích hợp sẵn — mặc định. REST API chỉ khi cần request validation theo schema, API key và usage plan, WAF tích hợp, hoặc response caching.


Lời kết

Kiến thức trong series này chỉ thực sự thuộc về bạn sau khi bạn tự tay gây ra lỗi và tự sửa. Đừng ngại làm hỏng — tài khoản học tập với budget 5 USD là môi trường an toàn để phá.

Hai nguồn nên đọc tiếp sau khi hoàn thành 8 tuần: AWS Well-Architected Framework (phần Performance Efficiency và Security) và tài liệu chính thức của CloudFront về Cache Policy. Nếu muốn có chứng chỉ, AWS Certified Cloud Practitioner là bước hợp lý — nhưng năm dự án ở trên có giá trị thực tế cao hơn nhiều khi đi phỏng vấn.

Ghi chú: ba việc nên làm ngay hôm nay:

  1. Tạo tài khoản AWS và đặt budget cảnh báo.
  2. Mua một tên miền rẻ — có tên miền thật làm việc học nhanh hơn gấp đôi.
  3. Mở DevTools trên trang web bạn hay dùng, tìm header X-Cache và X-Amz-Cf-Pop. Bạn vừa nhìn thấy CloudFront đang làm việc.

Tóm tắt

  • Lộ trình 8 tuần đi theo thứ tự của series: nền móng và mạng → S3 → CloudFront (tuần quan trọng nhất) → ALB và backend → ghép cùng origin → auth và config → CI/CD → vận hành. Mỗi tuần có tiêu chí “xong khi” phải đạt trước khi sang tuần sau.
  • Năm dự án tăng dần: portfolio → thư viện ảnh có upload → dashboard cùng origin → blog Next.js SSG/ISR → hệ thống nhiều môi trường. Mỗi dự án kèm ARCHITECTURE.md.
  • Từ điển 44 thuật ngữ là ngôn ngữ chung để nói chuyện với DevOps; checklist production 34 dòng là thứ để rà trước mỗi lần go-live, tick bằng kiểm tra thật.
  • Mười câu phỏng vấn xoay quanh bốn chủ đề: SPA trên S3/CloudFront, cache và invalidation, CORS và kiến trúc cùng origin, mã lỗi ALB và bảo mật pipeline.
  • Ba việc hôm nay: budget, tên miền, mở DevTools tìm X-Cache.

Đọc thêm