10.3
Static Website Hosting আর CORS
bucket থেকে ওয়েবসাইট চালানো, 403-এর ফাঁদ, আর preflight সহ cross-origin request।
18 মিনিট পড়া + Animation
টিপস: ডটেড-আন্ডারলাইন করা যেকোনো শব্দে ট্যাপ বা Hover করলে তার অর্থ দেখবে।
এই লেসন শেষে তুমি পারবে
- Static website কী, কোনো server ছাড়াই S3 কেন সেটা চালাতে পারে, আর কখন এটাই সবচেয়ে সস্তা সঠিক উত্তর, সেটা বোঝাতে।
- একটা bucket-কে website বানাতে: hosting চালু করা, index আর error document ঠিক করা, public access খুলে দেওয়া, আর সবার জন্য read-এর bucket policy লেখা।
- Website endpoint-এর দুই রকম format চিনতে, আর REST endpoint-এর সাথে website endpoint-এর তফাত বলতে: শুধু HTTP, শুধু public read, HTML error, index document আর redirect।
- শুধু status code দেখেই 403-এর ফাঁদ আর বাকি চেনা error (404, 302, 400, HTTPS-এর ব্যর্থতা, SSE-KMS, Requester Pays) ধরতে।
- তিন রকম redirect, Route 53 দিয়ে নিজের domain, আর CloudFront বা Amplify Hosting দিয়ে HTTPS সাজাতে।
- Origin, same-origin policy আর CORS বোঝাতে, browser কখন preflight পাঠাবে আগেই বলতে, আর এমন CORS configuration লিখতে যেটা page-এর যা লাগে ঠিক ততটুকুই অনুমতি দেয়।
#Server ছাড়া একটা website
#“Static” মানে কী
Static website হলো কিছু file-এর সমষ্টি, যেগুলো যেমন রাখা আছে ঠিক তেমনই browser-এ পাঠানো হয়: HTML page, CSS style sheet, JavaScript file, ছবি, font, .pdf download। প্রত্যেক ক্রেতার জন্য server-এ আলাদা করে কিছু হিসাব করা হয় না। Dynamic site-এ প্রতিটা request-এ server-এ code চলে (PHP, Node.js, Python, Java…): page বানাতে, database পড়তে বা login যাচাই করতে।
“Static” মানে কিন্তু “একঘেয়ে” না। React, Angular বা Vue দিয়ে বানানো আধুনিক single-page application (SPA) compile হয়ে static file-ই হয়ে যায়। সব interactivity চলে JavaScript হিসেবে browser-এর ভেতরে, আর dynamic data-র জন্য সে অন্য জায়গার API-কে call করতে পারে (API Gateway, Lambda, একটা ALB)। File-গুলো নিজেরা static, তাই S3 সেগুলো দিতে পারে।
S3 যেটা পারে না, সেটা হলো server-এর দিকে code চালানো: PHP না, Node.js না, Python না, server-side rendering না, database query না, form handler না। কোনো page যদি প্রতি request-এ বানাতে হয়, সেই অংশের জন্য লাগবে EC2, container বা Lambda (chapter ১৫)।
#খরচ কত
Website hosting একটা setting মাত্র, আলাদা কোনো product না, তাই এর নিজের কোনো fee নেই। তুমি S3-এর স্বাভাবিক দামই দাও: প্রতি GB-month storage, প্রতিটা GET request-এর জন্য সামান্য কিছু, আর internet-এ data transfer out-এর জন্য প্রতি GB (খরচের ভাগগুলো লেসন ১০.১-এ আছে)। ভিড়ওয়ালা site-এ সামনে CloudFront বসালে সাধারণত খরচ কমে, গতিও বাড়ে, কারণ S3 থেকে CloudFront-এ transfer বিনা পয়সায়, আর বারবার আসা ক্রেতাদের edge cache থেকেই দেওয়া হয়।
#একটা bucket-কে website বানানো
Static website hosting চালু করো
Bucket-এর Properties-এ Static website hosting-এর নিচে Enable আর Host a static website বেছে নাও। (অন্য বিকল্প “Redirect requests for an object” পুরো bucket-টাকেই একটা redirect বানিয়ে দেয়; নিচে “Redirect” অংশ দেখো।)
Index document-এর নাম দাও, চাইলে error document-এরও
Index document (সাধারণত
index.html) বাধ্যতামূলক: root আর slash দিয়ে শেষ হওয়া যেকোনো path-এর জন্য S3 এটাই দেয়। Error document (যেমন404.html) ঐচ্ছিক। দুটো নামেই বড়-ছোট হাতের অক্ষর আলাদা ধরা হয়, আর upload করা object-এর নামের সাথে হুবহু মিলতে হবে। এরপর site-এর file-গুলো upload করো।Block Public Access বন্ধ করো
নতুন bucket-এ Block Public Access-এর চারটা setting-ই চালু থাকে (লেসন ১০.২), তোমার account-এও থাকতে পারে। এই bucket-এর জন্য সেগুলো তুলে দাও, আর account স্তরে চালু থাকলে সেখানেও: S3 দুটোর মধ্যে সবচেয়ে কড়া মিলটাই মানে।
সবার জন্য read-এর একটা bucket policy দাও
Bucket-এর object-গুলোতে সবাইকে
s3:GetObjectদাও (শেষের/*খেয়াল করো):{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::mishti-menu-example/*" } ] }
Policy-টা কী দেয় না, সেটাও দেখো: s3:ListBucket। ক্রেতা নাম জানা যেকোনো object পড়তে পারে, কিন্তু bucket-এর list দেখতে পারে না। ঠিক এটাই চাই।
Notes-এ শর্তটা এক লাইনে বলা: bucket-এ bucket policy দিয়ে public read access চালু থাকতে হবে। আজকের দিনে আরও দুটো কথা জানা দরকার। এপ্রিল ২০২৩ থেকে নতুন bucket-এ ACL বন্ধ থাকে (Object Ownership = bucket owner enforced), তাই object public করার একমাত্র উপায় bucket policy, আর সেটা যে-ই upload করুক, সব object-এর ওপর খাটে। কিন্তু পুরোনো কোনো bucket-এ ACL চালু থাকলে, অন্য account-এর upload করা object-এর মালিক ওই account, তাদের ওপর bucket policy খাটে না; তাদের public-read ACL লাগবে, অথবা আরও ভালো হয় bucket-টাকে bucket owner enforced-এ নিয়ে গেলে।
#Website endpoint
Website bucket উত্তর দেয় একটা website endpoint-এ, যেটা S3-এর সাধারণ API (REST) endpoint থেকে আলাদা। Region অনুযায়ী এর format দুই রকম:
| Format | উদাহরণ | কোন Region |
|---|---|---|
s3-website- dash Region | http://mishti-menu-example.s3-website-us-east-1.amazonaws.com | পুরোনো Region-গুলো: us-east-1, us-west-1, us-west-2, eu-west-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, sa-east-1 (আর GovCloud US-West) |
s3-website dot Region | http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com | বাকি সব Region, নতুনগুলো সহ (ap-south-1, eu-central-1, us-east-2…) |
দুটোই শুরু হয় http:// দিয়ে, কখনো https:// না। Bucket-এর নাম আসে সবার আগে, তাই bucket-এর নামের মতোই endpoint-ও সারা পৃথিবীতে একটাই। কোন Region কোন চিহ্ন ব্যবহার করে মুখস্থ করার দরকার নেই; পরীক্ষা শুধু চায় তুমি দুটো চেহারাকেই S3 website endpoint হিসেবে চিনতে পারো, আর console-এ Static website hosting-এর নিচে ঠিকটাই দেখায়।
Website endpoint-এ একটা request
একজন ক্রেতা মিষ্টি ঘরের menu site খুলল। Browser সাধারণ HTTP দিয়ে bucket-এর website endpoint-এর সাথে কথা বলে; endpoint path-টাকে object key বানায়, object-টা public কি না দেখে, তারপর HTML দিয়ে উত্তর দেয়।
পরিস্থিতি
Hosting চালু, Block Public Access বন্ধ, সবার জন্য read policy: স্বাভাবিক অবস্থা।
Bucket-এর setting
| Block Public Access | Bucket policy | Index document | Error document |
|---|---|---|---|
| Off | সবার জন্য read, /* | index.html | 404.html |
ক্রেতা website endpoint লিখল: mishti-menu-example.s3-website.ap-south-1.amazonaws.com, কোনো path ছাড়া। Browser port 80-এ সাধারণ HTTP দিয়ে GET / পাঠাল।
#Index document আর folder
S3-এ আসল folder নেই (লেসন ১০.১), কিন্তু website endpoint slash-ওয়ালা key-গুলোকে folder-এর মতো আচরণ করায়:
/আর/menu/(শেষে slash) ফেরত দেয় ওই “folder”-এর index document:index.htmlআরmenu/index.html। প্রতিটা folder-এ আলাদা index document লাগে, একই নামে।/menu(শেষে slash নেই): S3 আগে খোঁজে ঠিকmenuনামের key-র কোনো object আছে কি না। না থাকলে, কিন্তুmenu/index.htmlথাকলে, সে 302 Found দিয়ে/menu/-এ পাঠিয়ে দেয়। দুটোর কোনোটাই না থাকলে 404।- Versioning চালু bucket-এ
index.html-এর শুধু সবচেয়ে নতুন version-টাই দেওয়া হয়।
#Error document
কিছু ভুল হলে website endpoint একটা HTML page ফেরত দেয় (REST endpoint দেয় XML)। Error document সেট করা থাকলে প্রতিটা 4xx error-এ S3 তোমার page পাঠায়, আসল status code সহ; 5xx error-এ সবসময় S3-এর নিজের page যায়। Error document-কেও সবার পড়ার মতো হতে হবে, নইলে ক্রেতা S3-এর সাদামাটা page-ই দেখবে।
#403-এর ফাঁদ, আর বাকি error
#403 Forbidden কেন আসে
Slide-এর চেনা পরামর্শ: 403 Forbidden পেলে দেখো bucket policy public read-এর অনুমতি দিচ্ছে কি না। বাস্তবে কারণ আরও কয়েকটা আছে। সবগুলোর গোড়ায় একটাই কথা: website-এর ক্রেতা anonymous, তাই একটা object তখনই দেওয়া হয়, যখন anonymous read অনুমোদিত আর পথে আর কিছু আটকায় না।
| কারণ | কেন ব্যর্থ হয় | সমাধান |
|---|---|---|
| Block Public Access চালু (bucket বা account-এ) | Public policy save করা যায় না, আর আগে save করা থাকলেও উপেক্ষা হয় | দুই স্তরেই এই bucket-এর দরকার মতো বন্ধ করো |
| কোনো bucket policy নেই | Object default-এ private; শুধু Block Public Access বন্ধ করলে কিছুই public হয় না | সবার জন্য read-এর policy দাও |
Policy-র Resource হলো /* ছাড়া arn:aws:s3:::bucket | এটা bucket-কে বোঝায়, তার object-গুলোকে না | arn:aws:s3:::bucket/* লেখো |
| Object-এর মালিক অন্য account (ACL চালু পুরোনো bucket) | Bucket policy শুধু bucket owner-এর নিজের object-এ খাটে | Bucket owner enforced, বা ওই object-গুলোতে public-read ACL |
| Object SSE-KMS দিয়ে encrypt করা | Decrypt করতে অনুমোদিত কোনো পরিচয় থেকে KMS-কে call করতে হয়; anonymous ক্রেতার তেমন কিছু নেই | Public content-এ SSE-S3, অথবা OAC আর key policy সহ CloudFront (লেসন ১০.৮) |
| Requester Pays চালু | Anonymous ক্রেতাকে বিল করা যায় না; website endpoint এর সাথে কখনোই চলে না | বন্ধ করো (লেসন ১০.৭) |
Bucket policy-তে স্পষ্ট একটা Deny (যেমন aws:SourceIp দিয়ে) | Deny সবসময় জেতে (লেসন ২.২) | শর্তটা ঠিক করো |
#404 আর বাকি status code
| Status | Website endpoint কখন দেয় |
|---|---|
| 200 OK | Object (বা index document) আছে, আর সবাই পড়তে পারে |
| 301 Moved Permanently | একটা redirect rule, “সব request redirect” করা bucket, বা redirect metadata-ওয়ালা object |
| 302 Found | Folder চাওয়া হয়েছে শেষের slash ছাড়া |
| 304 Not Modified | Browser-এর cache-এ রাখা কপিটাই এখনো ঠিক আছে (conditional request) |
| 400 Bad Request | Request এই bucket-এর ভুল Region-এর website endpoint-এ গেছে |
| 403 Forbidden | Object আছে, কিন্তু সবার পড়ার মতো না (ওপরের table দেখো) |
| 404 Not Found | Key নেই, index document নেই, bucket নেই, বা bucket website হিসেবে configure করা নেই |
| 405 Method Not Allowed | GET বা HEAD ছাড়া অন্য যেকোনো কিছু |
| 500 / 503 | ভেতরের কোনো error, অথবা “slow down”: request-এর হার কমাও (লেসন ১০.৬) |
একটা পার্শ্বপ্রতিক্রিয়া জেনে রাখো: public website নেই এমন key-তে 404 দেয় আর private key-তে 403, ফলে যে কেউ বুঝে ফেলতে পারে কোন key আছে। এটা সমস্যা হলে website hosting চালুই কোরো না; bucket-কে CloudFront-এর পেছনে private রাখো।
প্রতিটা কারণ নিজে ঘটিয়ে দেখো। নতুন bucket যেমন setting নিয়ে আসে, সেখান থেকে শুরু করো, তারপর ধাপে ধাপে 200 পর্যন্ত পৌঁছাও:
Website tester: 403 কেন?
Bucket সাজাও, একটা endpoint আর একটা path বাছো, তারপর একটা anonymous request পাঠাও, ঠিক যেভাবে ক্রেতার browser পাঠায়। এই লেসনের প্রতিটা 403 আর 404 নিজে ঘটিয়ে দেখার চেষ্টা করো।
Bucket mishti-menu-example
Object: index.html, about.html, 404.html, menu/index.html
Bucket policy
Object-এর encryption
Path
উত্তর দেখতে “Request পাঠাও” চাপো।
#Website endpoint না REST endpoint
Website bucket-এর সাধারণ REST endpoint-ও থেকে যায়, https://mishti-menu-example.s3.ap-south-1.amazonaws.com, আর দুটোর আচরণ একদম আলাদা:
| REST endpoint | Website endpoint | |
|---|---|---|
| কার জন্য | Program: S3 API, SDK, CLI | Browser |
| Access control | Public আর private দুই রকম content (signed request, IAM) | শুধু সবার পড়ার মতো content |
| Request | Bucket আর object-এর সব operation | Object-এ শুধু GET আর HEAD |
GET / | Bucket-এর list দেয় (অনুমতি থাকলে) | Index document দেয় |
Folder (/menu/) | শুধুই একটা key; index document নেই | menu/index.html |
| Error | XML | HTML, চাইলে তোমার error document |
| Redirect | নেই (redirect metadata-ওয়ালা object যেমন আছে তেমন ফেরত আসে) | Bucket আর object দুই স্তরের redirect |
| HTTPS | আছে | নেই, শুধু HTTP |
| Access point, Requester Pays | চলে | চলে না |
একই bucket, দুটো endpoint
Website hosting চালু করা bucket দুটো আলাদা hostname-এ উত্তর দেয়। REST endpoint হলো সাধারণ S3 API; website endpoint হলো browser-এর জন্য একটা ছোট web server। Object একই, আচরণ আলাদা।
পরিস্থিতি
Website endpoint-এ root মানে index document।
নিজেকে যাচাই করোপ্রশ্ন
একজন developer browser-এ https://mishti-menu-example.s3.ap-south-1.amazonaws.com/ খুলল, home page দেখবে ভেবে। Bucket-এ website hosting আর সবার জন্য read policy আছে। Browser কী দেখাবে?
#Redirect
Website endpoint তিনভাবে ক্রেতাকে অন্য জায়গায় পাঠাতে পারে:
- সব request অন্য host-এ redirect। পুরো একটা bucket-এর hosting option “Redirect requests for an object” করে একটা host name দাও (চাইলে http বা https-ও)। চেনা ব্যবহার হলো
www.example.com→example.com: দুই নামের দুটো bucket, content একটায়, অন্যটা ফাঁকা আর redirect করে, path অপরিবর্তিত রেখে। - Routing rule, website configuration-এর ভেতরে (console-এ JSON, সর্বোচ্চ ৫০টা rule)। প্রতিটা rule-এর একটা শর্ত থাকে, key prefix (
KeyPrefixEquals) আর/অথবা error code (HttpErrorCodeReturnedEquals), আর একটা redirect: নতুন host, protocol, বদলি prefix (ReplaceKeyPrefixWith) বা বদলি key (ReplaceKeyWith), আর redirect code। Folder-এর নাম বদলালে, কোনো অংশ তুলে দিলে, বা 404-গুলো এমন server-এ পাঠাতে চাইলে যে সেগুলো সামলাতে পারে, তখন এটা কাজে লাগে। - Object redirect: object-এর
x-amz-website-redirect-locationmetadata-তে আরেকটা key (শুরুতে/সহ) বা পুরো একটা URL দাও। Website endpoint 301 দেয়; REST endpoint এটা পাত্তা দেয় না, object-টাই ফেরত দেয়। ৫০ rule-এর কোনো সীমা নেই, তাই আলাদা আলাদা অনেক page সরালে এটাই মানায়।
[
{
"Condition": { "KeyPrefixEquals": "docs/" },
"Redirect": { "ReplaceKeyPrefixWith": "documents/" }
},
{
"Condition": { "HttpErrorCodeReturnedEquals": "404" },
"Redirect": { "HostName": "app.example.com", "ReplaceKeyPrefixWith": "report-404/" }
}
]
তিন রকম redirect
Website bucket সবকিছু অন্য host-এ পাঠাতে পারে, নিয়ম ধরে পাঠাতে পারে (key prefix বা error code দেখে), অথবা metadata দিয়ে একটা object-কে পাঠাতে পারে।
পরিস্থিতি
example.com আর www.example.com দুটোই চালাতে হলে ঠিক ওই দুই নামে দুটো bucket বানাতে হয়। Content থাকে example.com-এ; www bucket ফাঁকা, আর তাকে সেট করা হয় host example.com-এ “redirect requests for an object” দিয়ে।
#নিজের domain, আর HTTPS
#Route 53 দিয়ে নিজের domain
ক্রেতারা লিখবে example.com, লম্বা S3 hostname না। Notes বলছে নিজের domain-এর জন্য CloudFront লাগে; কথাটা অর্ধেক সত্যি। শুধু নিজের domain-এর জন্য CloudFront লাগে না:
- Bucket-এর নাম রাখো domain-এর হুবহু নামে:
example.com(আর www redirect bucket-এর জন্যwww.example.com)। - Route 53-এ
example.com-এর জন্য একটা Alias A record বানাও, target হবে S3 website endpoint (লেসন ৮.২)। Subdomain হলে যেকোনো DNS provider-এ website endpoint-এর দিকে একটা CNAME-ও চলে।
এটা কাজ করে কারণ S3 HTTP request-এর Host header দেখে bucket বেছে নেয়। এর মানে এটাও: example.com নামের bucket অন্য কারো হলে এই পদ্ধতি অসম্ভব, আর নাম না মিললে bucket-টা Alias target হিসেবে দেখাই যায় না।
#HTTPS-এর জন্য CloudFront (বা Amplify Hosting)
Website endpoint-এ HTTPS একেবারেই নেই, আর এতে certificate লাগানোও যায় না। আজকাল browser HTTP site-কে “Not secure” দেখায়, আর অনেক সুবিধা (service worker, geolocation, clipboard) HTTPS ছাড়া চলে না। বিকল্পগুলো:
- Website endpoint-কে custom origin বানিয়ে CloudFront। CloudFront
example.com-এর জন্য বিনা পয়সার একটা ACM certificate (CloudFront-এর জন্য us-east-1-এ চাওয়া) দিয়ে HTTPS চালায়; Route 53 Alias দেখায় distribution-কে। প্রতিটা folder-এর index document আর redirect থেকে যায়, কিন্তু bucket public-ই থাকে, আর CloudFront থেকে S3-এর ধাপটা HTTP। - REST endpoint আর Origin Access Control (OAC) দিয়ে CloudFront। Website hosting বন্ধ, Block Public Access চালু, bucket policy শুধু ওই distribution-কে অনুমতি দেয়। শুরু থেকে শেষ HTTPS, CloudFront এড়িয়ে কেউ ঢুকতে পারে না, আর SSE-KMS-ও চলে।
/-এর জন্য একটা default root object সেট করতে হয়, আর sub-folder-এর index page আর redirect সামলাতে হয় CloudFront Functions দিয়ে। বেশিরভাগ নতুন site-এর জন্য AWS এটাই বলে, আর chapter ১১-এ এর বিস্তারিত আছে (CloudFront basics)। - AWS Amplify Hosting (পাশের কথা)। S3-এর documentation এখন S3-এ রাখা site-এ HTTPS দেওয়ার সবচেয়ে সহজ উপায় হিসেবে এটাকেই বলে: তুমি bucket আর prefix বেছে দাও, Amplify file-গুলো CloudFront-চালিত একটা CDN-এ বসিয়ে দেয়, HTTPS URL আর নিজের domain সহ। SAA-C03 পরীক্ষায় এটা নেই; সেখানে “S3 static website-এ HTTPS”-এর উত্তর CloudFront।
নিজের domain, আর HTTPS
Route 53 সরাসরি example.com-কে HTTP-তে একটা website bucket-এর দিকে দেখাতে পারে। HTTPS চাইলে, বা bucket private রাখতে চাইলে, সামনে বসে CloudFront।
পরিস্থিতি
ঠিক example.com নামের একটা bucket-এ website hosting চালু। Route 53-এ example.com-এর একটা Alias A record সেই bucket-এর website endpoint-কে দেখায় (লেসন ৮.২)। নিজের domain-এর জন্য CloudFront লাগেই না।
#CORS: page আর file যখন আলাদা জায়গায়
#Origin আর same-origin policy
একটা URL-এর origin হলো তিনটা জিনিস একসাথে: scheme + host + port:
https://www.example.com-এর schemehttps, hostwww.example.com, আর port ধরে নেওয়া 443 (httpহলে 80)।https://www.example.com/app1আরhttps://www.example.com/app2একই origin: path গোনায় পড়ে না।https://www.example.comআরhttps://other.example.comআলাদা origin (host আলাদা); তেমনইhttp://www.example.com(scheme আলাদা) আরhttps://www.example.com:8443(port আলাদা)।
Browser same-origin policy মেনে চলে: এক origin থেকে আসা script অন্য origin-এ request পাঠাতে পারে, কিন্তু অন্য origin স্পষ্ট করে অনুমতি না দিলে তার উত্তর পড়তে পারে না। এই নিয়ম না থাকলে তুমি যে site-এই ঢুকতে, সেটা তোমার login করা session ব্যবহার করে তোমার ব্যাংকের page পড়ে ফেলতে পারত। CORS (Cross-Origin Resource Sharing) হলো অন্য origin-এর বলার উপায় যে “এই origin আমাকে পড়তে পারে”: Access-Control-Allow-Origin-এর মতো response header দিয়ে।
তিনটা কথা অনেকেই খেয়াল করে না:
- CORS মানায় browser, S3 না। একই file নতুন tab-এ, curl দিয়ে বা কোনো server থেকে দিব্যি খোলে। S3 শুধু browser-এর প্রশ্নের উত্তর দেয়।
- এটা খাটে script-এর (
fetch,XMLHttpRequest) cross-origin পড়ায়, CSS-এর load করা web font-এ, আরcrossoriginattribute দিয়ে ব্যবহার করা ছবি বা video-তে (যেমন canvas-এ আঁকা)। অন্য origin-এর ছবি সাধারণ<img src>দিয়ে দেখাতে কোনো CORS লাগে না। - CORS কখনো permission দেয় না। S3 object দেবে কি না, সেটা তবুও ঠিক করে bucket policy, IAM বা signature; CORS শুধু ঠিক করে browser script-কে উত্তরটা দেখতে দেবে কি না।
#Simple request আর preflight
Browser cross-origin request-কে দুই ভাগে ভাগ করে:
- Simple request: GET, HEAD বা POST, শুধু “নিরাপদ” header আর সাধারণ একটা
Content-Type(form data বা text) সহ। Browser সাথে সাথে request পাঠায়, একটাOriginheader জুড়ে, তারপর response-এAccess-Control-Allow-Originখোঁজে। না থাকলে বা অন্য origin-এর নাম থাকলে, S3 data পাঠালেও script পায় CORS error। - Preflight করা request: অন্য যেকোনো method (PUT, DELETE), একটা custom header (
Authorization,x-amz-*), বাapplication/json-এর মতোContent-Type। আসল request-এর আগে browser একটা OPTIONS request পাঠায়, এটাই preflight, এতে থাকেOrigin,Access-Control-Request-MethodআরAccess-Control-Request-Headers। উত্তর যদি origin, method আর প্রতিটা header-এর অনুমতি দেয়, তবেই আসল request যায়।
Slide-এ preflight-এর ছবিটা এমন: browser cross-origin server-কে জিজ্ঞেস করে “https://www.example.com কি GET, PUT, DELETE পাঠাতে পারবে?”; উত্তরে আসে Access-Control-Allow-Origin: https://www.example.com আর Access-Control-Allow-Methods: GET, PUT, DELETE; তারপর আসল request যায়। Access-Control-Max-Age header থাকলে browser এই উত্তর cache-এ রেখে পরের preflight বাদ দিতে পারে (browser একটা সীমা টানে; Chrome-এ ২ ঘণ্টা)।
একটা site আর একটা bucket-এর মধ্যে CORS
Page আসে https://www.example.com থেকে। তার JavaScript data চায় assets-example bucket থেকে, S3-এর REST endpoint দিয়ে, যেটা আলাদা origin। নিয়ম মানায় browser; S3 শুধু header পাঠিয়ে জানায় সে কী অনুমতি দেয়।
পরিস্থিতি
Browser দোকানের page আনল। তার origin হলো scheme + host + port: https://www.example.com (port 443 ধরা আছে)।
#Bucket-এ CORS configuration
Configuration দিতে হয় সেই bucket-এ, যে resource দিচ্ছে, মানে cross-origin target-এ; যে site request করছে তাতে কখনো না। S3 console-এ এটা JSON (Permissions → Cross-origin resource sharing); API আর CLI-ও এটা নেয় (put-bucket-cors), আর পুরোনো document-এ একই নিয়ম XML-এ দেখা যায়। একটা configuration-এ সর্বোচ্চ ১০০টা rule, প্রত্যেকটায়:
| Element | মানে |
|---|---|
AllowedOrigins (বাধ্যতামূলক) | কোন origin অনুমোদিত, যেমন https://www.example.com। প্রতিটা string-এ একটা * wildcard থাকতে পারে (https://*.example.com), আর শুধু * দিলে সব origin চলে |
AllowedMethods (বাধ্যতামূলক) | GET, PUT, POST, DELETE, HEAD-এর যেকোনোটা |
AllowedHeaders | Preflight-এ browser কোন header পাঠানোর অনুমতি চাইতে পারে (Access-Control-Request-Headers); প্রতিটা string-এ একটা *, তাই x-amz-* বা * |
ExposeHeaders | কোন response header script পড়তে পারবে, যেমন ETag (browser থেকে multipart upload-এ লাগে) বা x-amz-meta-* value |
MaxAgeSeconds | Preflight-এর উত্তর browser কতক্ষণ cache-এ রাখতে পারবে |
ID | Rule-এর একটা ঐচ্ছিক নাম |
S3 rule-গুলো ক্রমানুসারে দেখে, আর প্রথম যে rule-এ origin, method আর (preflight হলে) চাওয়া প্রতিটা header মেলে, সেটাই ব্যবহার করে। কোনোটাই না মিললে preflight পায় 403 Forbidden (“This CORS request is not allowed”, অথবা configuration একেবারে না থাকলে “CORS is not enabled for this bucket”), আর browser আসল request পাঠায়ই না।
[
{
"ID": "public-read-anywhere",
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": []
},
{
"ID": "admin-uploads",
"AllowedOrigins": ["https://www.example.com", "https://admin.example.com"],
"AllowedMethods": ["PUT", "POST", "DELETE"],
"AllowedHeaders": ["content-type", "x-amz-*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
Slide-এর S3 উদাহরণে একটা website bucket (my-bucket-html) থেকে আসা page আরেকটা website bucket (my-bucket-assets) থেকে coffee.jpg আনে। দ্বিতীয় bucket-কে origin হিসেবে প্রথম bucket-এর website endpoint http://my-bucket-html.s3-website.us-west-2.amazonaws.com-কে অনুমতি দিতে হবে, অথবা *-কে। http://-টা খেয়াল করো: https://-এর rule মিলবে না।
প্রতিটা মিল নিজে চেষ্টা করে দেখো, rule-এর ক্রমের ফাঁদ আর CORS পার হয়েও bucket policy আটকে দেওয়ার ঘটনা সহ:
CORS preflight simulator
একটা page-এর script S3 REST endpoint দিয়ে assets-example bucket-কে call করছে। Bucket-এর CORS configuration আর request বেছে নাও, তারপর পাঠিয়ে দেখো browser আর S3 কী করে।
assets-example-এর CORS configuration
[
{
"AllowedOrigins": [
"https://www.example.com"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedHeaders": []
}
]Method
বাড়তি request header
কী হয় দেখতে “Request পাঠাও” চাপো।
#CORS-এর যে ভুলগুলো পুরো বিকেল খেয়ে ফেলে
- Rule ভুল bucket-এ। এটা থাকতে হবে সেই bucket-এ, যেখান থেকে browser আনছে।
- Origin-এ scheme, port বা
www-এর তফাত।http://বনামhttps://,:8443,example.comবনামwww.example.com, সবই আলাদা origin। Rule-এ শেষে slash দিলে (https://www.example.com/) সেটাও কখনো মেলে না। AllowedHeaders-এ একটা header বাদ পড়েছে। AWS SDK-র PUT পাঠায়content-typeআর কয়েকটাx-amz-*header; এগুলো list করো বা*দাও।- Script
ETagপড়তে পারছে না। ওটাExposeHeaders-এ যোগ করো। - CDN ভুল উত্তর cache করেছে। সামনে CloudFront থাকলে
Origin,Access-Control-Request-MethodআরAccess-Control-Request-Headersheader forward করো আর cache key-তেOriginরাখো (managedCORS-S3Originorigin request policy এটাই করে), OPTIONS method-ও অনুমতি দাও; নইলে এক origin-এর cache করা response, CORS header ছাড়া, অন্য origin-এর কাছে চলে যায়। এই কারণেই S3Vary: Originজুড়ে দেয়। - ভাবা যে CORS data রক্ষা করে। করে না: browser ছাড়াই যে কেউ S3-কে call করতে পারে। Data রক্ষা করো policy দিয়ে; CORS ব্যবহার করো শুধু নিজের page-কে পড়তে দেওয়ার জন্য।
#System design-এ static site
- Serverless web front end। Static file S3-এ, OAC, HTTPS আর নিজের domain সহ CloudFront দিয়ে দেওয়া; dynamic call browser থেকে যায় API Gateway আর Lambda-তে, বা একটা ALB-তে। Patch করার মতো web server নেই, আর যেকোনো ভিড়ে নিজেই বাড়ে। Chapter ১৫-এর বেশিরভাগই এই কাঠামোর ওপর দাঁড়ানো।
- Page আর ভারী asset আলাদা করা। Page মূল domain-এ, বড় media আলাদা bucket-এ বা আলাদা path-এর পেছনে। Origin আলাদা হলে asset bucket-এ CORS দাও; একটা CloudFront distribution আর path behaviour (
/assets/*) দিয়ে করলে সবই same-origin, CORS লাগেই না। - Browser থেকে সরাসরি upload। Browser একটা pre-signed URL দিয়ে file সরাসরি S3-এ PUT করে (লেসন ১০.১২), তোমার server-কে byte-গুলো ছুঁতেই হয় না। এর জন্য PUT-এর CORS rule লাগে (আর multipart হলে
ETagexpose করা)। - Maintenance বা failover page। Route 53 failover policy-র secondary record হিসেবে ছোট্ট একটা S3 website (লেসন ৮.৩) মূল site বন্ধ থাকলে “একটু পরেই ফিরছি” দেখায়।
- সীমাগুলো জানো। শুধু HTTP, শুধু public, server-side code নেই, Regional (CloudFront cache না করলে একটা Region-এ একটাই কপি)। এর কোনোটা লাগলে সামনে CloudFront বসাও, বা অন্য service বেছে নাও।
#নিজে করে দেখো
AWS CLI দিয়ে, এমন একটা folder-এ, যেখানে index.html, 404.html আর site-এর বাকি file আছে:
# 1. Bucket বানাও আর site upload করো (content type extension দেখে আন্দাজ হয়)
aws s3 mb s3://mishti-menu-example --region ap-south-1
aws s3 sync ./site s3://mishti-menu-example/
# 2. Index আর error document সহ website hosting চালু করো
aws s3 website s3://mishti-menu-example/ --index-document index.html --error-document 404.html
# 3. সবার জন্য read: এই bucket-এর public access খোলো, তারপর policy দাও
aws s3api put-public-access-block --bucket mishti-menu-example \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=false,RestrictPublicBuckets=false
aws s3api put-bucket-policy --bucket mishti-menu-example --policy file://public-read.json
# 4. পরীক্ষা করো (ap-south-1-এর জন্য dot-Region endpoint)
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/menu # 302 → /menu/
curl -I http://mishti-menu-example.s3-website.ap-south-1.amazonaws.com/nothing # 404
curl -I https://mishti-menu-example.s3.ap-south-1.amazonaws.com/ # REST: 403 (list)
# 5. Routing rule আর একটা object redirect
aws s3api put-bucket-website --bucket mishti-menu-example --website-configuration file://website.json
aws s3api copy-object --bucket mishti-menu-example --copy-source mishti-menu-example/old-menu.html \
--key old-menu.html --website-redirect-location /menu/ --metadata-directive REPLACE
aws s3api get-bucket-website --bucket mishti-menu-example
৩ নম্বর ধাপে ACL-এর দুটো setting চালুই থাকে (ACL তো ব্যবহারই হচ্ছে না), বন্ধ হয় শুধু public policy আটকানোর দুটো। website.json-এ index, error আর routing rule একসাথে থাকে:
{
"IndexDocument": { "Suffix": "index.html" },
"ErrorDocument": { "Key": "404.html" },
"RoutingRules": [
{ "Condition": { "KeyPrefixEquals": "docs/" }, "Redirect": { "ReplaceKeyPrefixWith": "documents/" } }
]
}
Asset bucket-এ CORS, তারপর হাতে একটা preflight (curl browser সেজে browser-এর header-গুলো পাঠায়):
aws s3api put-bucket-cors --bucket assets-example --cors-configuration file://cors.json
aws s3api get-bucket-cors --bucket assets-example
curl -i -X OPTIONS https://assets-example.s3.ap-south-1.amazonaws.com/photos/roshogolla.jpg \
-H "Origin: https://admin.example.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: content-type"
# কোনো rule মিললে Access-Control-Allow-Origin সহ 200; না মিললে 403
aws s3api delete-bucket-cors --bucket assets-example
API-র জন্য file-টা একটা মোড়কে রাখতে হয়: {"CORSRules": [ …rule-গুলো… ]}; console নেয় শুধু list-টা।
#সারসংক্ষেপ
মূল কথা
- S3 static site চালায় (HTML, CSS, JavaScript, ছবি, React বা Angular দিয়ে বানানো SPA), কোনো server ছাড়াই; server-side code (PHP, Node.js, Python) চালাতে পারে না। Hosting নিজে বিনা পয়সার: দাম দাও storage, request আর transfer out-এর।
- সাজানো: hosting চালু → index document (বাধ্যতামূলক) আর error document (ঐচ্ছিক, শুধু 4xx) → Block Public Access বন্ধ (bucket আর account) →
arn:aws:s3:::bucket/*-এ publics3:GetObjectদেওয়া bucket policy। - Endpoint:
http://bucket.s3-website-region.amazonaws.com(পুরোনো Region) বাhttp://bucket.s3-website.region.amazonaws.com(বাকিগুলো)। শুধু HTTP। - 403 Forbidden → সবার পড়ার মতো না: Block Public Access, policy নেই বা ভুল, অন্য account-এর object, SSE-KMS, Requester Pays, স্পষ্ট একটা Deny। 404 → এমন key নেই বা index document নেই। 302 → শেষের slash ছাড়া folder।
- Website endpoint বনাম REST endpoint: শুধু public বনাম public আর private, শুধু GET/HEAD বনাম সব operation, index document, HTML বনাম XML error, redirect, HTTPS নেই বনাম HTTPS আছে।
- Redirect: পুরো bucket অন্য host-এ (www → apex), routing rule (prefix বা error code, সর্বোচ্চ ৫০টা), বা প্রতিটা object-এ
x-amz-website-redirect-location। - নিজের domain: domain-এর হুবহু নামে bucket + Route 53 Alias। HTTPS: CloudFront (সবচেয়ে ভালো private bucket আর OAC দিয়ে), বা Amplify Hosting।
- Origin = scheme + host + port। Browser same-origin policy মানায়; অন্য origin-এর CORS header সেটা শিথিল করে। Simple request-এ থাকে
Origin; বাকিগুলোর আগে OPTIONS দিয়ে preflight হয়। - S3-এর CORS configuration যায় resource দেওয়া bucket-এ: JSON rule,
AllowedOrigins(একটা*, বা শুধু*),AllowedMethods(GET, PUT, POST, DELETE, HEAD),AllowedHeaders,ExposeHeaders,MaxAgeSeconds; সর্বোচ্চ ১০০টা rule, প্রথম মিলটাই জেতে। CORS কখনো access দেয় না।
আগে নিজে উত্তর দাও, তারপর ব্যাখ্যাটা পড়ো। ঠিক হলেও পড়ো, কারণ ভুল Option-গুলো কেন ভুল, সেটাও ওখানে আছে।
নিজেকে যাচাই করোপ্রশ্ন
এর মধ্যে কোনটা S3 static website hosting একাই চালাতে পারে?
নিজেকে যাচাই করোপ্রশ্ন
Site host করলে (সব request redirect না করলে) static website hosting configuration-এ কোন জিনিসটা সবসময় থাকতেই হবে?
নিজেকে যাচাই করোপ্রশ্ন
কোন দুটো URL বৈধ S3 website endpoint?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা দল নতুন bucket-এ static website hosting চালু করল, index.html upload করল, তারপর website endpoint খুলল। Browser দেখাচ্ছে 403 Forbidden। সবচেয়ে সম্ভাব্য কারণ কী?
নিজেকে যাচাই করোপ্রশ্ন
Bucket-এ Block Public Access বন্ধ, আর bucket policy Principal *-কে arn:aws:s3:::shop-example-এ s3:GetObject action-এর অনুমতি দেয়। তবুও ক্রেতারা 403 পাচ্ছে। কেন?
নিজেকে যাচাই করোপ্রশ্ন
Bucket স্তরের Block Public Access setting বন্ধ, public-read policy-ও ঠিক আছে, কিন্তু console public policy নিয়ে access denied বলে policy save করতে দিচ্ছে না। কী দেখা উচিত?
নিজেকে যাচাই করোপ্রশ্ন
Website-এর সব page চলছে, শুধু download করার menu file-গুলো (.pdf) 403 দিচ্ছে। ওগুলো আলাদা encryption setting-ওয়ালা একটা script দিয়ে upload করা হয়েছিল। সবচেয়ে সম্ভাব্য কারণ কী?
নিজেকে যাচাই করোপ্রশ্ন
একটা public website bucket-এ ক্রেতা /menu চাইল (শেষে slash নেই)। menu নামের কোনো object নেই, কিন্তু menu/index.html আছে। S3 কী ফেরত দেয়?
নিজেকে যাচাই করোপ্রশ্ন
একটা S3 website-এ 404.html error document হিসেবে সেট করা। কোন response-এ S3 ওই document ফেরত দেয়?
নিজেকে যাচাই করোপ্রশ্ন
একটা React single-page app S3 website endpoint-এ চলছে। /orders/42-এ থাকা অবস্থায় page refresh করলে user app-এর বদলে error page দেখে। S3 website endpoint রেখেই সবচেয়ে সহজ সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
REST endpoint-এর তুলনায় S3 website endpoint নিয়ে কোন দুটো কথা ঠিক?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা site-এর JavaScript http://shop-example.s3-website-us-east-1.amazonaws.com-এ PUT দিয়ে file upload করছে আর 405 পাচ্ছে। এর বদলে কী করা উচিত?
নিজেকে যাচাই করোপ্রশ্ন
page1.html object-এর x-amz-website-redirect-location metadata-তে /page2.html দেওয়া। ক্রেতা website endpoint দিয়ে page1.html চাইলে কী হয়, আর REST endpoint দিয়ে চাইলে কী হয়?
নিজেকে যাচাই করোপ্রশ্ন
একটা কোম্পানি চায় http://example.com আর http://www.example.com দুটোতেই S3-এ রাখা একই site দেখাক, file-এর কপি একটাই রেখে। চেনা সাজানোটা কী?
নিজেকে যাচাই করোপ্রশ্ন
একটা website-এর ২০০-র বেশি আলাদা আলাদা page-এর নাম বদলেছে। কোন redirect পদ্ধতি সবচেয়ে মানানসই?
নিজেকে যাচাই করোপ্রশ্ন
Route 53-এ example.com host করা। Site আছে company-site-prod bucket-এ, website hosting চালু। example.com-এর Alias record বানাতে গেলে bucket-টা target হিসেবে দেখা যায় না। কোন দুটো সমাধান কাজ করবে?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
S3 website endpoint-এ চলা marketing site-টা www.example.com-এ HTTPS দিয়ে দিতে হবে, AWS-এ যত সস্তা আর সহজে সম্ভব। Architect কী করবে?
নিজেকে যাচাই করোপ্রশ্ন
Security review বলছে public website-এর S3 bucket নিজে publicly accessible হতে পারবে না, অথচ site HTTPS-এ সবার জন্য খোলা থাকতে হবে। কোন design এটা মেলায়?
নিজেকে যাচাই করোপ্রশ্ন
কোন জোড়া URL-এর origin একই?
নিজেকে যাচাই করোপ্রশ্ন
Page আসে https://www.example.com থেকে। Page-এর font আর একটা JSON দামের list আনা হয় https://assets-example.s3.us-east-1.amazonaws.com থেকে, আর browser console CORS error দেখাচ্ছে। CORS কোথায় configure করতে হবে?
নিজেকে যাচাই করোপ্রশ্ন
Developer curl দিয়ে bucket থেকে prices.json কোনো ঝামেলা ছাড়াই নামাতে পারছে, কিন্তু web page থেকে একই request CORS error দিচ্ছে। কেন?
নিজেকে যাচাই করোপ্রশ্ন
https://www.example.com-এর একটা page থেকে S3 bucket-এ কোন request CORS preflight ঘটায়?
নিজেকে যাচাই করোপ্রশ্ন
S3-এর CORS rule-এ কোন দুটো element থাকতে পারে?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা bucket-এর CORS configuration এমন: rule 1 https://www.example.com থেকে GET-এর অনুমতি দেয়, কোনো ExposeHeaders নেই; rule 2 https://www.example.com থেকে GET আর PUT-এর অনুমতি দেয় আর x-amz-meta-shop expose করে। www.example.com-এর একটা script simple GET পাঠিয়ে x-amz-meta-shop header পড়তে চায়। কী হয়?
নিজেকে যাচাই করোপ্রশ্ন
একটা CORS rule-এ AllowedOrigins [https://www.example.com] আর AllowedMethods [GET, PUT], কোনো AllowedHeaders নেই। Browser-এর SDK PUT দিয়ে upload করে, সাথে content-type আর x-amz-checksum-crc32 header পাঠায়। Preflight 403 দিয়ে ব্যর্থ হচ্ছে। সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
Website endpoint http://shop-html.s3-website-us-west-2.amazonaws.com থেকে আসা একটা site shop-assets bucket থেকে fetch() দিয়ে ছবি আনে। তার CORS rule origin হিসেবে https://shop-html.s3-website-us-west-2.amazonaws.com-কে অনুমতি দেয়। ছবি আটকে যাচ্ছে। কেন?
নিজেকে যাচাই করোপ্রশ্ন
ঠিকঠাক CORS configuration-ওয়ালা একটা S3 bucket-এর সামনে CloudFront, configuration দুটো origin-কে অনুমতি দেয়। দ্বিতীয় origin-এর user-রা মাঝে মাঝে CORS error পায়, আর cache invalidation-এর পর সেটা চলে যায়। কারণ আর সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
S3-এর CORS নিয়ে কোন দুটো কথা ঠিক?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
S3 website endpoint-এর একটা static site মাসে ২০,০০,০০০ বার দেখা হয়; প্রতিবার ১.৫ MB download হয়। Free tier বাদ দিয়ে, data transfer out প্রতি GB $0.09 ধরলে, মাসে transfer out-এর খরচ মোটামুটি কত?
নিজেকে যাচাই করোপ্রশ্ন
মূল notes-এর কোন দুটো কথা সংশোধন করতে হয়েছে?
বেছে নাও ২