<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Meta on Alex's E-Portfolio</title><link>https://alexswitzer.com/categories/meta/</link><description>Recent content in Meta on Alex's E-Portfolio</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 25 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://alexswitzer.com/categories/meta/index.xml" rel="self" type="application/rss+xml"/><item><title>3 years of bot traffic analyzed</title><link>https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/</link><pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate><guid>https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/</guid><description>&lt;p&gt;In 2023 I moved this site to AWS. As part of the move, I set up request logging, which means I can see every single request ever made to the site. Three years on, it feels like a good time to look back over the data.&lt;/p&gt;&#10;&lt;p&gt;The site is effectively dead. I haven’t published any new content in that time, so any traffic it gets isn’t coming from people following a new post or a fresh link. That makes it a useful little laboratory: whatever shows up in the logs is background noise of the internet, with nothing to drown it out.&lt;/p&gt;&#10;&lt;h3 id="how-the-data-is-stored"&gt;How the data is stored&#13;&#10;&lt;/h3&gt;&lt;p&gt;The logs are written as compressed files into an Amazon S3 bucket. From there I pull them into Amazon Athena, which lets me run SQL queries over the full data set without having to download or unpack anything. Every question I had about the site turned into a query. Scaning the full data set tasks about 11 seconds to proses the full 200MB of logs.&lt;/p&gt;&#10;&lt;h3 id="requests-per-month"&gt;Requests per month&#13;&#10;&lt;/h3&gt;&lt;p&gt;To get an overview, I started with the simplest question: how many requests did the site receive each month?&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Requests per month" class="gallery-image" data-flex-basis="480px" data-flex-grow="200" height="750" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_per_month_hu_d02f51a0cb076607.webp" srcset="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_per_month_hu_fb1d9bb8580444d4.webp 800w, https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_per_month_hu_d02f51a0cb076607.webp 1500w" width="1500"&gt;&lt;/p&gt;&#10;&lt;p&gt;What stands out is the growth. Requests have climbed over the three years, even though there has been no new content. That means essentially 100% of that growth is automated. Nobody is discovering the site and telling their friends. The bots are just getting busier.&lt;/p&gt;&#10;&lt;h3 id="the-top-25-urls"&gt;The top 25 URLs&#13;&#10;&lt;/h3&gt;&lt;p&gt;Next I looked at the 25 most requested URLs.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Top 25 URLs" class="gallery-image" data-flex-basis="266px" data-flex-grow="111" height="1350" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/top_urls_hu_3d1f35b9b2e08efa.webp" srcset="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/top_urls_hu_5ec3ffac624bbf88.webp 800w, https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/top_urls_hu_3d1f35b9b2e08efa.webp 1500w" width="1500"&gt;&lt;/p&gt;&#10;&lt;p&gt;This list shows clear signs of automated scanning. Two entries in particular caught my eye:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;/wp-login.php&lt;/code&gt; a check to see whether the site is a WordPress install. It isn’t, but the scanners ask anyway, because on the internet at large it is a very good bet.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;/.env&lt;/code&gt; a file that often holds API keys, database passwords and other secrets. If a site accidentally serves it, the attacker gets the keys to everything. I’m curious how often that request actually succeeds on the wider internet.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="user-agents"&gt;User agents&#13;&#10;&lt;/h3&gt;&lt;p&gt;Then I looked at what the visitors claimed to be, using the user agent string sent with each request.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Top User agents" class="gallery-image" data-flex-basis="342px" data-flex-grow="142" height="1050" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/user_agents_hu_eb95c472f75f021f.webp" srcset="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/user_agents_hu_84a9b9f56dcb0d20.webp 800w, https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/user_agents_hu_eb95c472f75f021f.webp 1500w" width="1500"&gt;&lt;/p&gt;&#10;&lt;p&gt;The headline number: over 30% of all requests had no user agent at all. A real browser always identifies itself, so a missing user agent is a strong sign of a script.&lt;/p&gt;&#10;&lt;p&gt;Among the ones that did identify themselves, the tooling is revealing:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;curl/8.7.1&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Go-http-client/1.1&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Go-http-client/2.0&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;python-requests/2.31.0&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;These are all command-line tools and programming libraries, the sort of thing you use to write a scanner in an afternoon. The remaining user agents looked like ordinary browsers. That doesn’t mean they were real people, since faking a browser string is trivial, but they were at least plausible.&lt;/p&gt;&#10;&lt;h3 id="where-is-the-traffic-coming-from"&gt;Where is the traffic coming from?&#13;&#10;&lt;/h3&gt;&lt;p&gt;To get a rough idea of geography, I grouped requests by the edge location (PoP) that served them. This isn’t a perfect measure of where a request originated, but it’s a decent proxy for where in the world the traffic entered the network.&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Requests by pop" class="gallery-image" data-flex-basis="400px" data-flex-grow="166" height="900" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_by_pop_hu_1766dfadf15c3094.webp" srcset="https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_by_pop_hu_97ad107b2dac9668.webp 800w, https://alexswitzer.com/p/3-years-of-bot-traffic-analyzed/requests_by_pop_hu_1766dfadf15c3094.webp 1500w" width="1500"&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;CDG52-P4 is Charles de Gaulle International Airport in France&lt;/li&gt;&#10;&lt;li&gt;YTO53-P1 is the Greater Toronto Area in Canada&lt;/li&gt;&#10;&lt;li&gt;AMS58-P5 is Amsterdam Airport Schiphol in Netherlands&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Beyond those, the traffic is spread across a wide range of locations around the world.&lt;/p&gt;&#10;&lt;p&gt;It’s also worth noting where the traffic isn’t. If real people were looking at this site, I’d expect to see the locations closest to its audience near the top. Those locations sit well down the list:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Rank&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;PoP&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Location&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Share of traffic&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;11&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;SYD3-P3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Sydney&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;2.7%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;19&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;SYD62-P3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Sydney&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;1.5%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;130&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;AKL53-P1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Auckland&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;0.04%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;132&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;AKL50-C1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Auckland&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;0.04%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Auckland barely registers. The traffic follows where the data centres and scanning infrastructure are, not where the human audience is.&lt;/p&gt;&#10;&lt;h3 id="the-most-persistent-visitor"&gt;The most persistent visitor&#13;&#10;&lt;/h3&gt;&lt;p&gt;The single busiest IP address was in France, responsible for a little over 0.7% of all requests. That sounds small, but for one address among everything the internet threw at this site, it’s a lot.&lt;/p&gt;&#10;&lt;p&gt;First seen: 2026-04-12 &lt;br&gt;&#10;Last seen: 2026-09-16&lt;/p&gt;&#10;&lt;p&gt;In that time it completed four full scans of the site, working methodically through a long list of paths. Some of what it asked for:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;/etc/passwd&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;/id_rsa&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;/.git/hooks&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;/opt/mailcow-dockerized/mailcow.conf&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;/sendgrid.env&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;That list is a snapshot of what attackers hope to find lying around: system password files, private SSH keys, source control data, and configuration files for mail servers and email services. This is credential fishing, plain and simple. The scanner isn’t targeting me in particular. It is casting a wide net across the internet and hoping some server is misconfigured enough to hand something over.&lt;/p&gt;&#10;&lt;h3 id="what-this-tells-me"&gt;What this tells me&#13;&#10;&lt;/h3&gt;&lt;p&gt;Overall, this is a large data set of almost purely automated activity. A site with no content, no audience and no promotion still attracts a steadily growing stream of scanners, scrapers and credential fishers, and the volume is rising year on year.&lt;/p&gt;&#10;&lt;p&gt;If you run a website of any kind, this is a good reminder that someone is already checking it for leftover secrets, whether or not you’ve ever told anyone it exists.&lt;/p&gt;&#10;</description></item><item><title>Website Move to AWS</title><link>https://alexswitzer.com/p/website-move-to-aws/</link><pubDate>Wed, 20 Sep 2023 00:00:00 +0000</pubDate><guid>https://alexswitzer.com/p/website-move-to-aws/</guid><description>&lt;p&gt;I have moved my website hosting to AWS. This was done for two reasons. Improve load times and lower running costs.&lt;/p&gt;&#10;&lt;p&gt;The old stack: Vultr + Epic + OpenBSD + httpd&lt;/p&gt;&#10;&lt;p&gt;In the server, I was running OpenBSD. I started off on this as I wanted to self-host an email server. In the OpenBSD VPS, I was using httpd, openSMTPD, and dovecot. This stack made hosting an email server not as painful as using Postfix. As with all email servers I had lots of delivery problems. In the end, I moved to Protonmail for my email hosting, letting me keep my custom domain without the maintenance and delivery problems.&lt;/p&gt;&#10;&lt;p&gt;The VPS was in Sydney. This gave Oceania good response times. The single point of presence did limit response times in NA and EU. As I was on the cheapest VPS with an ipv4 address I was paying $5 per month. Working out to $60 per year. This is quite a lot for a website that gets less than 10 views a month.&lt;/p&gt;&#10;&lt;p&gt;The new stack: AWS Route53 + AWS S3 + AWS Cloud Front&lt;/p&gt;&#10;&lt;p&gt;I have now moved to AWS. To do this I am using 3 AWS services. Route53 for DNS. S3 for storing the website. And Cloud Front as a CDN. Cloud Front does all the SSL and cacheing.&lt;/p&gt;&#10;&lt;p&gt;This set of tools gives worldwide CDN coverage, less downtime, and costs less.&lt;/p&gt;&#10;&lt;p&gt;As my traffic is so low, I am in the free tier for most of the services that I use. Costs have settled to $0.50 per month. This makes the total cost $6 per year. That is almost what I paid a month with the old stack.&lt;/p&gt;&#10;&lt;p&gt;Once I moved from Epic to AWS Route53 for my DNS I had problems linking my domain’s apex to Cloud Front. It would always give pops that were in the EU. After months of research, I found that I had not changed the name server to the ones my hosted zone used. After I made this change the system is now working with 100% efficiency.&lt;/p&gt;&#10;</description></item></channel></rss>