Jak utworzyć najtańszą stronę WWW?
Jak zbudować statyczną stronę WWW na AWS S3 + CloudFront za kilka groszy miesięcznie?
Najtańsza, zautomatyzowana strona WWW?
PoznajAWS.pl — ta strona — został zbudowany w sposób, w którym nie mamy fizycznego serwera, świadczącego usługę, a jedynie w momencie czasu wykorzystujemy konteneryzację, by zbudować nową wersję strony i umieścić ją w S3. Ktoś mógłby to nazwać podejściem #devops, ja wolę słowo “pragmatyzm”.
Architektura
GitHub → GitHub Actions → AWS S3 (static hosting) → CloudFront (CDN)
Koszt? Przy małym ruchu to dosłownie grosze miesięcznie:
- S3: opłata za przechowywanie (poniżej 1 GB to mniej niż $0.023/miesiąc)
- CloudFront: pierwsze 1 TB transferu darmowe (Free Tier)
- Route53: ~$0.50/miesiąc za hosted zone + $12/rok za domenę
Krok 1: Utwórz bucket S3
aws s3 mb s3://moja-strona.pl --region eu-west-1
Włącz hosting statycznej strony:
aws s3 website s3://moja-strona.pl \
--index-document index.html \
--error-document 404.html
Krok 2: Bucket Policy — publiczny odczyt
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::moja-strona.pl/*"
}
]
}
Uwaga: Zamiast publicznego bucketa, lepiej użyć CloudFront z OAC (Origin Access Control) — bucket pozostaje prywatny, a CloudFront jest jedynym dostępem.
Krok 3: CloudFront Distribution
Utwórz dystrybucję CloudFront wskazującą na Twój bucket S3. Kluczowe ustawienia:
- Origin: Twój bucket S3
- Default Root Object:
index.html - Viewer Protocol Policy: Redirect HTTP to HTTPS
- Custom Error Response: 404 →
/404.html - OAC (Origin Access Control) zamiast publicznego bucketa
Krok 4: GitHub Actions — automatyczny deployment
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build site
run: npm ci && npm run build
- name: Deploy to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: eu-west-1
run: |
aws s3 sync dist/ s3://moja-strona.pl/ --delete
- name: Invalidate CloudFront
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: eu-west-1
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} \
--paths "/*"
Minimalne uprawnienia IAM
Klucz AWS użyty w GitHub Actions powinien mieć minimalne uprawnienia:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::moja-strona.pl",
"arn:aws:s3:::moja-strona.pl/*"
]
},
{
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
}
]
}
Podsumowanie
Za kilka złotych miesięcznie masz:
- Statyczną stronę hostowaną na AWS S3
- CDN CloudFront — globalny, szybki
- SSL/TLS — darmowy przez AWS Certificate Manager
- Automatyczny deployment przez GitHub Actions
- Skalowanie do milionów odwiedzin bez konfiguracji
To podejście jest używane przez tę samą stronę, którą właśnie czytasz.