Getting a real domain and real HTTPS
![]()
Picking up from where the honeypot post left off — today's goal was the "next up" from that post: stop serving this site off a bare IP address and get it behind a real domain with real HTTPS.
Free domain, the lazy way
I didn't want to pay for a domain just to find out if I even like doing this, so I went with DuckDNS (duckdns.org) — free dynamic DNS, sign in with an existing account, type a subdomain, done. Mine ended up mileshuynh.duckdns.org. The only gotcha: when you're on the "current ip" field, DuckDNS auto-fills the IP of whatever machine is looking at the page — which was my office IP, not my EC2 box. Had to manually overwrite it with the server's public IP before the domain actually pointed anywhere useful.
Pointing Nginx at the new domain
The server was already running Nginx + PHP-FPM (I'd forgotten I set that up), serving Bludit off the default catch-all server block — server_name _; meaning "answer to any hostname." I changed that to my actual domain so Certbot would have something concrete to attach a certificate to:
server_name mileshuynh.duckdns.org;
Certbot, and it just worked
I ran:sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d mileshuynh.duckdns.org
Answered a couple of prompts, picked "redirect HTTP to HTTPS," and that was it — cert issued, deployed, renewal scheduled automatically. First time I've set up a real Let's Encrypt cert myself instead of it being invisible platform magic, and it's a genuinely small amount of work for what it buys you.
The site loaded... with no styling
Switched over to https:// and the page came back as plain unstyled HTML — no fonts, no layout, nothing. Classic mixed content: the browser console pointed straight at it —
Mixed Content: The page at https://mileshuynh.duckdns.org/ was loaded over HTTPS, but requested an insecure resource http://18.208.211.75/bl-themes/.../favicon.png. This request has been blocked.
Bludit had the site's base URL saved as the old http://<ip> from before I touched DNS or TLS, so every asset it generated a link for was still pointing at plain HTTP on the raw IP. Browsers refuse to load insecure sub-resources on an otherwise-secure page, so the CSS/JS/images all got silently blocked. Updated the URL setting to the real https:// domain and the page came back looking like an actual website again.
A brief detour into panicking about AWS billing
Checked Cost Explorer out of habit and watched the number tick from $0.16 to $0.21 over the course of the day, which is a great way to briefly convince yourself you've done something expensive. Turned out to be nothing dramatic — I'm on AWS's newer credit-based free plan, not the old 12-month free tier, so everything (compute hours, EBS storage, and — new since Feb 2024 — a per-hour charge on every public IPv4 address, even the free-tier default one) draws down from a $100 credit balance instead of hitting a card. At the current burn rate the credit comfortably outlasts the free plan's 184-day window. Worth knowing before I go add an Elastic IP: it costs exactly the same as the IP I already have, as long as it stays attached to a running instance.
What I actually learned
A "free" domain and "free" HTTPS are each about 10 minutes of work once you know the two gotchas — DuckDNS grabs your IP, not the server's, and Bludit caches its own base URL and won't update it for you.
Mixed content errors are one of the most self-explanatory error messages a browser gives you — the console told me exactly what was broken and where, no guessing required.
AWS's billing dashboard is intimidating mostly because it's spread across four different pages (Cost Explorer, Bills, Free Tier, Billing summary) that each show a different slice of the same number. Once you find the per-usage-type breakdown, it stops being scary.
Next up: back to the actual point of the box — let the Cowrie honeypot run for a few days and write up what it caught.