Go · engineered from scratch

About this site / Technical colophon

Small on purpose.

I built this portfolio from scratch as one Go binary with explicit routes, embedded content and very little infrastructure to surprise me later.

Code

Go · chi · Go templates

Runtime

One binary · one container · one droplet

Application integration

Brevo · contact delivery

A deliberately small architecture

A personal site does not need a miniature platform.

This is a personal portfolio, not a miniature platform. It does not need a CMS, database, queue, or reverse-proxy tier, so it has none. That makes it easier for me to understand, deploy, and keep secure.

Page copy is compile-time Go data. Templates, the gallery catalog, and asset hashes are prepared once at startup; generated images and compressed assets are embedded in the binary. The first render of each eligible page is retained in memory.

Apart from ACME certificate issuance and renewal, the only application-level outbound call is the contact form’s transactional email request to Brevo. Everything else is the server returning local bytes.

Implementation notes

Under the hood

The short version is above. These four sections cover the implementation details without turning the page into a wall of infrastructure notes.

From request to responseOne process, explicit routes
  • One process handles routing, TLS, templates, static files, caching, and the contact endpoint. There is no proxy tier in front of it.
  • Production traffic has two deliberately narrow entry points:
    • :80 handles Let’s Encrypt HTTP-01, serves /ping, and redirects everything else to HTTPS.
    • :443 serves the application over autocert-managed TLS 1.3.
  • Startup does the reusable work: templates and the email template are parsed, the gallery is discovered and sorted, assets are hashed, and compressed variants are indexed before the listeners open.
  • Cacheable page routes are retained after their first render. The cache key follows the router’s cleaned path and can only grow to fixed pages plus validated case-study and album IDs.
  • The code stays separated: models own content and validation, controllers coordinate requests, views render templates, and infrastructure owns transport and deployment.
  • Canonical URLs always point at milosmijic.com; configured aliases such as www redirect permanently.
Security choicesStrict defaults, narrow exceptions
  • Transport: Let’s Encrypt certificates, TLS 1.3 as the minimum, and two-year HSTS with subdomains and preload enabled.
  • The browser starts from no permissions: the CSP begins with default-src 'none' and opens only the sources this site uses. The small inline theme bootstrap is allowed by its exact SHA-256 hash.
    • Trusted Types is required for scripts; inline script and style attributes are blocked.
    • Framing is denied, MIME sniffing is disabled, referrers are limited, and camera, microphone, and geolocation permissions are turned off.
    • COOP and CORP stay same-origin; COEP remains off because this site does not need cross-origin isolation.
  • The contact form gets the strictest path: same-origin checks, scoped CSRF tokens, a bounded request body, field validation, a honeypot, and both per-client and site-wide rate limits.
  • Hosts and paths are normalized early. Unknown hosts are rejected, configured aliases redirect to the canonical host, panics are recovered, and handler time is capped at 20 seconds.
  • Gallery media has a separate boundary: album paths are normalized and hotlink checks use Sec-Fetch-Site with a same-site Referer fallback.
Fast by defaultBuild once, reuse often
  • Static work happens before requests: responsive cover variants and WebP gallery images are generated ahead of time; CSS and JavaScript receive Brotli and gzip versions during the build.
  • HTML is cheap to revisit: cacheable pages are retained in a bounded in-memory cache, use weak content ETags, and return 304 Not Modified when the browser already has the current representation.
  • Static assets use exact validators: strong ETags describe the bytes actually served, including separate Brotli, gzip, and identity representations.
  • Versioned URLs are immutable for a year. Templates add content hashes to UI and gallery URLs; stable unversioned URLs revalidate instead of going stale.
  • The gallery sends the right-sized image: responsive cover srcset, lightweight thumbnails, larger lightbox files, reserved dimensions, lazy loading below the fold, and no runtime resizing.
Keeping it runningHealth, deploys, observability
  • The runtime is intentionally plain: one static Linux binary in a distroless Debian container on a DigitalOcean Droplet. The container has no shell or package manager.
  • Health is checked from both sides: Docker runs the binary’s loopback healthcheck, while an external monitor checks public HTTPS health, the HSTS header, Brotli delivery, certificate lifetime, and reports HSTS preload status.
  • Deploys keep a way back: the release script snapshots the current runtime and image, checks the replacement publicly, and automatically restores the previous release if the new one fails health checks.
  • Logs are useful without collecting messages: requests receive IDs and single-line logs; contact names, addresses, and message bodies stay out of application logs.
  • Production configuration is environment-driven. Docker injects the server-side values; the application does not open a dotenv file in production.

More engineering work

Production systems, integrations and infrastructure.

Explore the systems