Jak utworzyć najtańszą stronę WWW?

Jak zbudować statyczną stronę WWW na AWS S3 + CloudFront za kilka groszy miesięcznie?

Jak utworzyć najtańszą stronę WWW?
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.

← Wróć do Receptury