← All projects

This website

Personal project, 2026

A static portfolio I built to present my profile for an internship abroad in summer 2027, and run like a production service: infrastructure as code, automated deployment, no long-lived credentials, a few euros a month.

Read the article: Terraform, OIDC and a strict CSP →

At a glance

  • A+Mozilla Observatory, 12/12 tests
  • ≥ 95Lighthouse, enforced in CI
  • 0 keysstored: deploys via OIDC
  • 2 USDmonthly budget alert

Architecture

Architecture of this websiteGitLab CI builds the site and uploads it to a private S3 bucket using OIDC. CloudFront reads the bucket through Origin Access Control and serves visitors over HTTPS with security headers. Terraform defines the AWS resources.VisitorbrowserHTTP/3CloudFrontHTTPS, headersTLS 1.2+CSPHSTSS3private bucketPrivateOAC onlyGitLab CIbuild, scansOIDCSASTCheckovrequestOACOIDC syncTerraformS3, CloudFront, ACM,IAM, Budgets, alarmsstate in S3lockedArchitecture of this websiteGitLab CI builds the site and uploads it to a private S3 bucket using OIDC. CloudFront reads the bucket through Origin Access Control and serves visitors over HTTPS with security headers. Terraform defines the AWS resources.GitLab CIbuild, scansOIDCSASTCheckovS3private bucketPrivateOAC onlyCloudFrontHTTPS, headersTLS 1.2+CSPHSTSVisitorbrowserHTTP/3OIDC syncOACrequestTerraformS3, CloudFront, ACM, IAM, Budgets, alarmsstate in S3locked

Deployment pipeline

  1. 1BuildAstro static site
  2. 2Scan codeSAST, secret detection
  3. 3Scan infraCheckov on Terraform
  4. 4LighthouseEvery category ≥ 95
  5. 5HTML & linksValid, no broken links
  6. 6Deploymain, via OIDC
  7. 7InvalidateCloudFront cache
  • Merge requests run every check, never deploy
  • One deployment at a time
  • Terraform state in S3, locked
  • Renovate weekly, releases ≥ 14 days old

Security

  • Private S3 bucket

    Public access blocked; CloudFront only, via OAC.

  • Strict CSP and headers

    default-src 'none', HSTS for 2 years, no Server header.

  • No inline code

    No inline script or style, so no CSP exceptions.

  • HTTPS only

    TLS 1.2 minimum, ACM certificate.

  • Deploys without keys

    OIDC role limited to this project and main.

  • SAST and secret detection

    GitLab SAST and secret scan on every pipeline.

  • Mozilla Observatory A+

    130 out of 100, all 12 tests passed.

  • Checkov on Terraform

    Skipped rules documented; new findings fail.

Cost

Hosting
Fully static site: no server, no database, nothing that bills while idle.
CDN
CloudFront PriceClass_100, the cheapest edge locations (Europe and North America).
Routing
A CloudFront Function serves index.html for sub-pages, well within the free tier.
Monitoring
Everything stays within the AWS free tier.
Guardrail
An AWS Budgets alert emails me at 80% of the limit, and when the forecast exceeds it.
Budget limit
2 USD / month

Monitoring

  • every 5 min
    Uptime check

    A small Python Lambda checks that the site answers 200 with the expected content.

  • daily
    Browser canary

    A CloudWatch Synthetics canary opens the home page and this case study in a real browser and fails on any console error, including CSP violations.

  • on failure
    Email alarms

    Each check has a CloudWatch alarm that emails me through SNS, and again when the site recovers.

Visit statistics come from CloudFront's built-in reports: no tracking script, no cookies, no consent banner.

Quality

  • Lighthouse CI

    Audits every page before deployment and fails the pipeline if any category drops below 95.

  • Valid HTML

    Every page is checked for valid, accessible HTML and for broken links.

  • Dark mode

    Follows the system setting, remembers the visitor's choice without a flash, and keeps every colour pair above WCAG AA contrast.

  • Print stylesheet

    A clean PDF for recruiters: no navigation, light colours, contact details written out.

All technical details

Goal

  • Present my profile to recruiters for an internship abroad in summer 2027.
  • Run it the way I would run a production service: infrastructure as code, automated deployment, no long-lived credentials, and a monthly budget of a few euros.

Security

  • Private S3 bucket: all public access is blocked and only my CloudFront distribution can read it, through Origin Access Control.
  • Strict Content Security Policy (default-src 'none', scripts, styles and fonts from 'self' only), HSTS for two years, X-Frame-Options DENY, a restrictive Permissions-Policy, and the Server header removed.
  • The site ships no inline script or style, so the CSP needs no exceptions.
  • HTTPS only, TLS 1.2 minimum, with an ACM certificate for the domain and www.
  • GitLab CI deploys through OIDC: no AWS key is stored anywhere. The role is limited to this project and the main branch, and can only write to this bucket and invalidate this distribution.
  • GitLab's SAST and secret detection run on every pipeline.
  • Mozilla Observatory grades the security headers A+ (130 out of 100, all 12 tests passed).
  • Checkov scans the Terraform code on every pipeline. Each rule I skip is documented with its reason, usually cost (WAF, KMS) or not applicable to a static site; any new finding fails the build.

Cost

  • Fully static site: no server, no database, nothing that bills while idle.
  • CloudFront PriceClass_100, the cheapest edge locations (Europe and North America).
  • An AWS Budgets alert emails me at 80% of a 2 USD monthly limit, and when the forecast exceeds it.
  • A CloudFront Function serves index.html for sub-pages, well within the free tier.

Deployment

  • All AWS resources are defined in Terraform.
  • Every push to main builds the site, runs the security scans, then syncs it to S3 and invalidates the CloudFront cache.
  • Hashed assets are cached for a year; HTML is revalidated on every visit, so a new version is live right after the deployment.
  • Only one deployment can run at a time.
  • The Terraform state lives in S3: versioned, encrypted, with native locking so two applies can never run at once.
  • Merge requests run every check but never deploy; only main can assume the deployment role.
  • Renovate opens a merge request every week for outdated dependencies (npm, CI images, Terraform providers), only for releases at least 14 days old.

Quality

  • Lighthouse CI audits every page before deployment and fails the pipeline if any category drops below 95.
  • Every page is checked for valid, accessible HTML and for broken links.
  • Dark mode follows the system setting, remembers the visitor's choice without a flash, and keeps every colour pair above WCAG AA contrast.
  • A print stylesheet gives recruiters a clean PDF: no navigation, light colours, contact details written out.

Monitoring

  • Every 5 minutes, a small Python Lambda checks that the site answers 200 with the expected content.
  • Once a day, a CloudWatch Synthetics canary opens the home page and this case study in a real browser and fails on any console error, including CSP violations.
  • Each check has a CloudWatch alarm that emails me through SNS, and again when the site recovers.
  • Visit statistics come from CloudFront's built-in reports: no tracking script, no cookies, no consent banner.
  • Everything stays within the AWS free tier.

What I'd improve next

  • Quote a LinkedIn recommendation from my EDF supervisor, once I have one.

Contact

Email meLinkedIn

Based in Lyon, France

Last updated

Download CVContact