<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Visual Harmony</title>
	<atom:link href="https://visualharmonydesign.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://visualharmonydesign.com/</link>
	<description>Websites, Apps, Design, Development - Ocala, FL</description>
	<lastBuildDate>Thu, 30 Jul 2026 21:58:19 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Prevent AI-Bots from Hammering Your Calendars 24/7</title>
		<link>https://visualharmonydesign.com/2026/07/prevent-ai-bots-from-hammering-your-calendars-24-7/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Thu, 30 Jul 2026 21:58:19 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://visualharmonydesign.com/?p=910</guid>

					<description><![CDATA[<p>I&#8217;ve been hosting and managing WordPress websites for a long time, and every so often a problem comes along that doesn&#8217;t match any of the usual patterns. Earlier this week, every site on one of our servers slowed to a crawl. Some pages went completely blank. Others loaded without their stylesheets, that broken half-rendered look &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2026/07/prevent-ai-bots-from-hammering-your-calendars-24-7/" class="more-link">Continue reading<span class="screen-reader-text"> "Prevent AI-Bots from Hammering Your Calendars 24/7"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2026/07/prevent-ai-bots-from-hammering-your-calendars-24-7/">Prevent AI-Bots from Hammering Your Calendars 24/7</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I&#8217;ve been hosting and managing WordPress websites for a long time, and every so often a problem comes along that doesn&#8217;t match any of the usual patterns. Earlier this week, every site on one of our servers slowed to a crawl. Some pages went completely blank. Others loaded without their stylesheets, that broken half-rendered look where the content shows up but the design is gone. And the strange part? The server had plenty of resources available.</p>
<p>If you run a server with WordPress sites on it, especially any site using an events calendar plugin, what I found might save you a very frustrating afternoon.</p>
<h2>Everything Looked Fine on the Surface, but Problems Lurked Below</h2>
<p>The specs on this server are respectable: 8 cores and 12GB of RAM. During the slowdown, the load average was hovering around 3 to 5. On an 8-core machine, that&#8217;s not even close to maxed out. Memory was mostly free. Swap was untouched. Disk I/O was quiet.</p>
<p>By every dashboard metric I&#8217;d normally check, the server should have been fast. It wasn&#8217;t.</p>
<p>I did what most of us do when a server misbehaves and nothing obvious explains it: a graceful reboot. It didn&#8217;t help. Within minutes, everything was slow again. That turned out to be an important clue. A reboot clears stuck processes and runaway memory. It does nothing against a problem that walks right back in the front door.</p>
<h2>The Suspect That Couldn&#8217;t Have Done It</h2>
<p>Resource monitoring showed one account consistently at the top of the usage charts. But here&#8217;s the thing: that account was limited to just 3 PHP-FPM workers. Three workers on an 8-core machine. How could that possibly drag down an entire server?</p>
<p>That question sent me down the wrong path for a bit, because the intuitive answer is &#8220;it can&#8217;t.&#8221; A worker cap feels like a safety fence. What I eventually understood is that a worker cap limits <em>concurrency</em>, not <em>damage</em>. Three workers pinned at 100% CPU around the clock is still nearly 3 full cores gone. And more importantly, those three workers were generating a flood of heavy database queries.</p>
<p>That&#8217;s the cascade: the busy site&#8217;s workers hammer MySQL, MySQL slows down for everyone, every other site&#8217;s PHP requests start stalling while they wait on their queries, stalled requests pile up and occupy all of Apache&#8217;s connection slots, and once those slots are full, even requests for plain static files like CSS get queued or dropped. That&#8217;s where the blank pages and missing stylesheets came from, on sites that had absolutely nothing to do with the problem.</p>
<h2>Finding the Real Culprit</h2>
<p>When I looked at the process list, the story got interesting. The account&#8217;s PHP workers each showed over 5 minutes of accumulated CPU time on a server that had only been up for half an hour. A normal PHP request finishes in milliseconds. Workers grinding at 100% for minutes at a time mean something is very wrong: an infinite loop, a stampede of expensive requests, or malware.</p>
<p>So I went to the access logs. First gotcha: on a cPanel server, the regular domain log only shows plain HTTP traffic, which for most modern sites is just redirected to HTTPS. The real traffic lives in the separate <code>-ssl_log</code> file. If you only check the regular log, a site under heavy fire can look almost idle.</p>
<p>The SSL log told a different story. Out of the last 5,000 requests, one single IP accounted for about 3,400 of them, a sustained rate of roughly 200 requests per minute. The user agent?</p>
<pre><code>Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +&#99;&#108;aud&#101;b&#111;&#116;&#64;an&#116;h&#114;&#111;p&#105;c&#46;c&#111;m)</code></pre>
<p>ClaudeBot, Anthropic&#8217;s AI training crawler. And right behind it, OpenAI&#8217;s GPTBot doing the same thing. These weren&#8217;t malicious actors. They were well-known crawlers politely identifying themselves while accidentally running a denial-of-service attack.</p>
<h2>The Infinite Calendar Problem</h2>
<p>Here&#8217;s where it gets useful for anyone running WordPress. The site being hammered uses The Events Calendar plugin. Look at the kinds of URLs the bots were requesting:</p>
<pre><code>/calendar/list/page/46/?ical=1&amp;tribe-bar-date=2023-11-15
/calendar/list/page/13/?tribe-bar-date=2020-02-26
/?eventDate=2018-10-23&amp;eventDisplay=day&amp;paged=17&amp;post_type=tribe_events</code></pre>
<p>Every date, every view type (day, list, month), every page number, every combination of query parameters generates a unique URL. Date pickers go back years and forward forever. The crawlable URL space is effectively infinite, and AI crawlers are thorough. They will happily walk that space all day, every day.</p>
<p>Two properties make this so destructive:</p>
<ol>
<li><strong>Every URL is unique</strong>, so page caching never helps. Each request is a full WordPress bootstrap.</li>
<li><strong>Calendar queries are expensive</strong>, joining posts and meta tables in ways that make the database work hard for every single hit.</li>
</ol>
<p>Multiply that by 200 requests per minute, around the clock, and a modest events site becomes a server killer. This isn&#8217;t specific to one plugin either. Any faceted URL structure works the same way: layered navigation on WooCommerce stores, filterable directories, search pages with combinable parameters. If your URLs can be recombined infinitely, AI crawlers will find them.</p>
<h2>Why I Didn&#8217;t Just Block Everything</h2>
<p>My first instinct was to block AI crawlers site-wide, and as an emergency measure, that&#8217;s exactly what I did. Load dropped within minutes. But it&#8217;s worth pausing before making that permanent, because there&#8217;s a real tradeoff.</p>
<p>AI assistants increasingly answer people&#8217;s questions directly and cite their sources. When someone asks a chatbot about services in your area, you want your site to be in the running for that citation. Block every AI-related user agent and you&#8217;ve opted out of that channel entirely. Traditional SEO isn&#8217;t affected either way, since Google and Bing crawlers are separate, but AI visibility is becoming its own thing worth protecting.</p>
<p>The crawlers also come in two distinct flavors, and they deserve different treatment:</p>
<ul>
<li><strong>Bulk training crawlers</strong> (GPTBot, ClaudeBot, CCBot, Bytespider) systematically download everything. These are the ones causing the load.</li>
<li><strong>Live user-fetch agents</strong> (ChatGPT-User, OAI-SearchBot, Claude-User, PerplexityBot) only fire when a real person asks a question and the assistant fetches a page to answer it. Low volume, high value. These are the ones you want to keep.</li>
</ul>
<p>So I landed on a tiered approach.</p>
<h2>The Fix, In Three Tiers</h2>
<p><strong>Tier 1: junk scrapers, blocked everywhere.</strong> Bots like Bytespider that offer nothing in return and have a reputation for ignoring robots.txt. These get a 403 on every URL.</p>
<p><strong>Tier 2: major AI crawlers, blocked only from the calendar.</strong> ClaudeBot, GPTBot, and friends can still read normal pages, so the site stays visible to AI assistants. They just can&#8217;t touch the infinite URL space that was causing the damage.</p>
<p><strong>Tier 3: user-fetch agents and search engines, untouched.</strong></p>
<p>The enforcement lives in two places. First, <code>robots.txt</code> politely asks compliant bots to stay away from the faceted URLs and slow down:</p>
<pre><code>User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Disallow: /*?*eventDate=
Disallow: /*?*tribe-bar-date=
Disallow: /*?*eventDisplay=
Disallow: /*?*ical=1
Disallow: /calendar/list/page/

User-agent: ClaudeBot
User-agent: GPTBot
User-agent: Amazonbot
User-agent: meta-externalagent
User-agent: Applebot-Extended
Crawl-delay: 10
Disallow: /wp-admin/
Disallow: /*?*eventDate=
Disallow: /*?*tribe-bar-date=
Disallow: /*?*eventDisplay=
Disallow: /*?*ical=1
Disallow: /calendar/list/page/</code></pre>
<p>Two details matter here. When a bot matches a named group, it ignores the <code>*</code> group entirely, so you have to repeat your Disallow rules in the named group. And <code>Crawl-delay: 10</code> caps compliant bots at roughly 6 requests per minute instead of 200. ClaudeBot documents that it honors both.</p>
<p>One more robots.txt tip: make it a real, physical file. WordPress generates a virtual robots.txt through PHP, which means that when your server is overloaded, the very file that could call off the bots starts returning 500 errors. A static file always serves.</p>
<p>Second, <code>.htaccess</code> enforces the rules for bots that don&#8217;t ask permission:</p>
<pre><code>ErrorDocument 403 "Forbidden"
&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On

# Tier 1: bulk scrapers with no user-facing value - blocked site-wide
RewriteCond %{HTTP_USER_AGENT} (Bytespider|CCBot|omgili|Diffbot|ImagesiftBot|Timpibot) [NC]
RewriteRule .* - [F,L]

# Tier 2: major AI crawlers - blocked only from calendar URLs
RewriteCond %{HTTP_USER_AGENT} (ClaudeBot|GPTBot|Amazonbot|meta-externalagent|Applebot-Extended) [NC]
RewriteCond %{QUERY_STRING} (eventDate=|tribe-bar-date=|eventDisplay=|ical=1) [NC]
RewriteRule .* - [F,L]

RewriteCond %{HTTP_USER_AGENT} (ClaudeBot|GPTBot|Amazonbot|meta-externalagent|Applebot-Extended) [NC]
RewriteCond %{REQUEST_URI} ^/(calendar|events)(/[^/]+)*/page/ [NC]
RewriteRule .* - [F,L]
&lt;/IfModule&gt;</code></pre>
<p>A 403 served by Apache costs almost nothing. The request dies before WordPress or the database ever get involved, which is the whole point.</p>
<h2>The Trap That Almost Fooled Me</h2>
<p>Now for the part I couldn&#8217;t find documented anywhere, and the reason this post exists.</p>
<p>After deploying the block rules, I tested them. Bot user agent on a calendar URL: 403.</p>
<p>Except it wasn&#8217;t my 403.</p>
<p>When I tested more carefully, using URLs that didn&#8217;t exist as real pages, the &#8220;blocked&#8221; requests were sailing straight through to WordPress. My rules weren&#8217;t firing at all in the way I thought. The 403s I was seeing came from the calendar plugin&#8217;s own PHP-level bot handling, which still costs a full WordPress bootstrap per request. The load problem would have quietly continued.</p>
<p>Here&#8217;s the mechanism, and it&#8217;s sneaky. See that first line in the code above, <code>ErrorDocument 403 "Forbidden"</code>? Without it, this happens:</p>
<ol>
<li>Your rewrite rule matches and Apache generates a 403.</li>
<li>To render the error page, Apache makes an <strong>internal subrequest</strong> for the error document.</li>
<li>That subrequest has no query string and doesn&#8217;t match your original URL, so it falls through to WordPress&#8217;s catch-all rewrite rule.</li>
<li>WordPress boots up, doesn&#8217;t recognize the error document URL, and issues a 301 redirect to the homepage.</li>
</ol>
<p>The result: the bot receives a friendly redirect instead of a 403, PHP runs anyway, and your block accomplishes nothing while appearing to work. Defining the error document inline as a plain string breaks the chain. No subrequest, no WordPress, just seven bytes of &#8220;Forbidden&#8221; and a closed connection.</p>
<p>The lesson that goes with it: <strong>test your blocks with URLs that don&#8217;t exist.</strong> A real page might be getting 403&#8217;d by a security plugin or the theme itself, which proves nothing about your Apache rules. Only an Apache-level block can 403 a URL that WordPress would otherwise 404. That distinction is the difference between a block that saves your server and one that just redecorates the problem.</p>
<h2>The Aftermath</h2>
<p>Load dropped from around 5 to under 2 within minutes of the final rules going in, and it stayed there. Real visitors never noticed a thing, which is the best kind of fix. The client&#8217;s site is still visible to AI assistants for normal content, search rankings are untouched, and the calendar now belongs to actual humans looking for actual events.</p>
<p>A few takeaways if you manage WordPress servers:</p>
<ul>
<li><strong>Low load average doesn&#8217;t mean healthy.</strong> A shared database bottleneck can strangle every site while your CPU graph looks fine.</li>
<li><strong>A reboot that doesn&#8217;t fix it is information.</strong> It means the cause is external and recurring, not a stuck process.</li>
<li><strong>Check the SSL access logs</strong>, not just the default ones. That&#8217;s where the real traffic is.</li>
<li><strong>Worker limits cap concurrency, not collateral damage.</strong> Three workers can absolutely take down a server if they&#8217;re feeding an expensive shared resource.</li>
<li><strong>Faceted URLs plus AI crawlers is a fire waiting to happen.</strong> Calendars, filtered shops, directories. If URL combinations are infinite, someone&#8217;s crawler will eventually try to visit all of them.</li>
<li><strong>Verify blocks at the Apache level with nonexistent URLs</strong>, and always pair deny rules with an inline ErrorDocument on WordPress sites.</li>
</ul>
<p>AI crawler traffic has exploded over the past couple of years, and from what I saw in my logs, it&#8217;s not slowing down. If you&#8217;ve run into something similar, whether it was a calendar, a store filter, or some other infinite URL generator, I&#8217;d like to hear how it showed up for you and what you did about it.</p>
<p>The post <a href="https://visualharmonydesign.com/2026/07/prevent-ai-bots-from-hammering-your-calendars-24-7/">Prevent AI-Bots from Hammering Your Calendars 24/7</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How a 95,000-IP Botnet Used tel: Links to Cripple a Client&#8217;s WordPress Site</title>
		<link>https://visualharmonydesign.com/2026/02/how-a-95000-ip-botnet-used-tel-links-to-cripple-a-clients-wordpress-site/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Sun, 01 Mar 2026 03:28:11 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://visualharmonydesign.com/?p=725</guid>

					<description><![CDATA[<p>I&#8217;ve been hosting and managing WordPress websites for over 20 years. Brute force attacks, XML-RPC abuse, spam injections, DDoS attempts&#8230; I&#8217;ve seen all of it. But last weekend I ran into something I&#8217;d never encountered before, and after searching around I couldn&#8217;t find a single article documenting this type of attack. So I&#8217;m writing one. &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2026/02/how-a-95000-ip-botnet-used-tel-links-to-cripple-a-clients-wordpress-site/" class="more-link">Continue reading<span class="screen-reader-text"> "How a 95,000-IP Botnet Used tel: Links to Cripple a Client&#8217;s WordPress Site"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2026/02/how-a-95000-ip-botnet-used-tel-links-to-cripple-a-clients-wordpress-site/">How a 95,000-IP Botnet Used tel: Links to Cripple a Client&#8217;s WordPress Site</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">I&#8217;ve been hosting and managing WordPress websites for over 20 years. Brute force attacks, XML-RPC abuse, spam injections, DDoS attempts&#8230; I&#8217;ve seen all of it. But last weekend I ran into something I&#8217;d never encountered before, and after searching around I couldn&#8217;t find a single article documenting this type of attack. So I&#8217;m writing one.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">Something Was Wrong, But Nothing Looked Wrong</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">One of the WordPress sites on our cPanel server had been sluggish for about a week. It wasn&#8217;t just that site feeling it, either. The whole server was slower than usual, and other client sites that normally loaded instantly were dragging.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">We went through the usual checklist. Cleared caches. Optimized the database. Checked for bloated error logs and reviewed plugin performance. Nothing jumped out. We ran a Wordfence scan, which kept failing due to memory exhaustion. We found and fixed a wp-cron misconfiguration that had been generating tens of thousands of unnecessary loopback requests. Load would drop for a bit and then climb right back up.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">After hours of chasing our tail, we finally stopped guessing and looked at what was actually consuming resources in real time.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">There It Was</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Running <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">ps aux --sort=-%cpu</code> showed six PHP-FPM processes for this one site, each consuming 75-79% CPU. A single website was consuming the equivalent of nearly five full CPU cores.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">We pulled the Apache access logs and the problem was staring right at us:</p>
<div class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg">
<div class="sticky opacity-0 group-hover/copy:opacity-100 top-2 py-2 h-12 w-0 float-right">
<div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">
<div class="relative">
<div class="transition-all opacity-100 scale-100"></div>
<div class="absolute inset-0 flex items-center justify-center">
<div class="transition-all opacity-0 scale-50"></div>
</div>
</div>
</div>
</div>
<div class="overflow-x-auto">
<pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code>187.244.118.5 "GET /directory-page/categories/escape-room/tel:5551234567/tel:5559876543/tel:5555551234/tel:5558675309" 301
168.181.58.2 "GET /directory-page/categories/escape-room/tel:5551234567/tel:5553456789/tel:5557891234/tel:5554567890" 301
27.252.198.69 "GET /directory-page/categories/escape-room/tel:5551234567/tel:5552345678/tel:5558901234/tel:5556789012" 301</code></pre>
</div>
</div>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Hundreds of requests per second. All hitting the same URL pattern. All from different IPs. And all with fake user agents like Windows 98, Opera 8, MSIE 5.0, and Android 2.0. Nobody is running those browsers in 2026.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">How the Attack Works</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">The site had a business directory page built with Elementor that listed local businesses with clickable phone numbers. Somewhere in the template, the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code> URIs were being rendered as relative links rather than proper <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">&lt;a href="tel:5551234567"&gt;</code> protocol links.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">That distinction matters more than you&#8217;d think. When a <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code> link is coded correctly, the browser handles it by opening the phone dialer. No request ever hits your server. But when it&#8217;s rendered as a relative path, a crawler treats it like a page on the site. A request to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/directory-page/categories/escape-room/tel:5551234567</code> doesn&#8217;t return a 404 in WordPress. WordPress just loads the parent page, which contains more phone links, which generates more URL paths, which creates an infinite recursive crawl loop.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">A botnet found this loop and went to town. Every request had a unique URL because the phone number combinations were randomized, which meant caching was completely useless. Every single request was a cache miss that spun up a fresh PHP process. With 95,000 unique IPs hammering the site, the server was trying to handle hundreds of concurrent PHP requests, each one rendering a full WordPress page with Elementor.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">That&#8217;s what had been dragging down the server for a week.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">Why This Was So Hard to Catch</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">This attack is different from what most WordPress admins are used to dealing with, and that&#8217;s what makes it dangerous.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">With 95,000 unique source IPs, there&#8217;s nobody to block. Traditional firewall deny lists don&#8217;t help when the IPs are disposable and constantly rotating.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Because the phone number combinations are randomized, every URL is different. Page caching, the thing that normally saves WordPress sites from traffic spikes, does nothing here. Every request is a cache miss.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">In this case, WordPress didn&#8217;t reject these URLs. When a request came in for <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/valid-page/garbage/more-garbage/</code>, instead of returning a 404 it loaded the valid parent page. This behavior can vary depending on your permalink structure, theme, and plugins, but the result here was that every junk URL the botnet generated got a full 200 response with a complete PHP execution cycle.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Standard security monitoring won&#8217;t catch it. If you&#8217;re watching for failed logins, xmlrpc abuse, or malware signatures, this looks like normal page traffic. Wordfence didn&#8217;t flag a single request.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">And on any server hosting multiple sites, one site under this attack slows down every other site on the machine. We spent hours optimizing the wrong things because we didn&#8217;t realize the traffic pattern was the actual problem.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">The Fix</h2>
<h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">Immediate Mitigation</h3>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">We needed to kill this fast. After trying several approaches that didn&#8217;t work (other redirect rules in the .htaccess were firing before our block could catch the requests), this is what finally did the job. Add it to the very top of your <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">.htaccess</code>, before any cache plugin or WordPress rewrite rules:</p>
<div class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg">
<div class="sticky opacity-0 group-hover/copy:opacity-100 top-2 py-2 h-12 w-0 float-right">
<div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">
<div class="relative">
<div class="transition-all opacity-100 scale-100"></div>
<div class="absolute inset-0 flex items-center justify-center">
<div class="transition-all opacity-0 scale-50"></div>
</div>
</div>
</div>
</div>
<div class="text-text-500 font-small p-3.5 pb-0">apache</div>
<div class="overflow-x-auto">
<pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-apache"># Block botnet tel: URI resource exhaustion attacks
&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On
RewriteCond %{THE_REQUEST} /tel: [NC]
RewriteRule ^ - [F]
&lt;/IfModule&gt;</code></pre>
</div>
</div>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">The important detail is using <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">%{THE_REQUEST}</code> instead of <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">%{REQUEST_URI}</code>. The former matches against the original raw request line before any other rewrites process it. We tried <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">%{REQUEST_URI}</code> first and it flat out didn&#8217;t work because trailing slash redirects and www normalization rules were issuing 301s before our block ever had a chance to evaluate.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Server load dropped to under 2 within minutes.</p>
<h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">This Won&#8217;t Break Your Phone Links</h3>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">If you&#8217;re wondering whether this rule will break legitimate <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code> links on the front end, it won&#8217;t. When a visitor clicks <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">&lt;a href="tel:5551234567"&gt;Call Us&lt;/a&gt;</code>, the browser opens the phone dialer directly. No HTTP request goes to your server. The only time <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/tel:</code> shows up in a URL path hitting your server is when a bot is crawling it like a page, and that&#8217;s exactly what we want to block.</p>
<h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">Fixing the Root Cause</h3>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">The real fix is finding where the template renders <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code> URIs as relative links and correcting the HTML. Phone links should always be output as proper protocol links:</p>
<div class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg">
<div class="sticky opacity-0 group-hover/copy:opacity-100 top-2 py-2 h-12 w-0 float-right">
<div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">
<div class="relative">
<div class="transition-all opacity-100 scale-100"></div>
<div class="absolute inset-0 flex items-center justify-center">
<div class="transition-all opacity-0 scale-50"></div>
</div>
</div>
</div>
</div>
<div class="text-text-500 font-small p-3.5 pb-0">html</div>
<div class="overflow-x-auto">
<pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-html"><span class="token token">&lt;</span><span class="token token">a</span> <span class="token token">href</span><span class="token token attr-equals">=</span><span class="token token">"</span><span class="token token">tel:5551234567</span><span class="token token">"</span><span class="token token">&gt;</span>Call Us<span class="token token">&lt;/</span><span class="token token">a</span><span class="token token">&gt;</span></code></pre>
</div>
</div>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">When they&#8217;re coded correctly, the browser handles them natively and they never generate server requests. This closes the infinite crawl loop permanently.</p>
<h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">Nginx Alternative</h3>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">If you&#8217;re running nginx as a reverse proxy or standalone, you can block this at the nginx level instead, which is even more efficient since it drops the connection before anything else processes:</p>
<div class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg">
<div class="sticky opacity-0 group-hover/copy:opacity-100 top-2 py-2 h-12 w-0 float-right">
<div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">
<div class="relative">
<div class="transition-all opacity-100 scale-100"></div>
<div class="absolute inset-0 flex items-center justify-center">
<div class="transition-all opacity-0 scale-50"></div>
</div>
</div>
</div>
</div>
<div class="text-text-500 font-small p-3.5 pb-0">nginx</div>
<div class="overflow-x-auto">
<pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-nginx"><span class="token token directive">location</span><span class="token token directive"> ~* /tel:</span> <span class="token token">{</span>
    <span class="token token directive">return</span> <span class="token token directive">444</span><span class="token token">;</span>
<span class="token token">}</span></code></pre>
</div>
</div>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">The 444 response is an nginx-specific status code that simply closes the connection with zero response body. Extremely lightweight.</p>
<h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">Long-Term Protection</h3>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Putting Cloudflare in front of the domain is probably the best single thing you can do going forward. Even their free tier with Bot Fight Mode would catch this traffic before it reaches your server. The fake user agents alone would trigger their bot detection.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Beyond that, get in the habit of periodically reviewing raw access logs for unusual patterns. Security plugins are great at detecting malware and brute force attacks, but they have blind spots. This type of resource exhaustion doesn&#8217;t trip any of the usual alarms.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">Was This an AI-Driven Attack?</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">I wondered about this too, given how much AI is being used for offensive security these days. Based on what I observed, I don&#8217;t think so. The fake user agents are classic botnet signatures that have been in use for years. Windows 98, impossible OS and browser combinations, randomized version numbers. An AI-driven attack would more likely use realistic, modern user agent strings to blend in with legitimate traffic.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">My best guess is that this is a traditional scraper botnet that stumbled into the infinite URL space created by the malformed <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code> links and just kept spawning crawlers because it kept finding &#8220;new&#8221; pages. Whether the resource exhaustion was intentional or just a side effect of the botnet getting stuck in a crawl loop is hard to say, but the result was the same.</p>
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">What I Took Away From This</h2>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">The biggest lesson was embarrassingly simple: when a server is slow, look at what&#8217;s actually hitting it before you start optimizing things. We spent hours tweaking caches, databases, and cron jobs while the answer was sitting right there in the access logs.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">WordPress&#8217;s permissive URL handling is also worth thinking about. The fact that it serves content for URLs with arbitrary path segments appended to valid pages creates real opportunities for this kind of resource exhaustion. It&#8217;s not a bug exactly, but it&#8217;s a behavior that can be exploited.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Security plugins have blind spots too. Wordfence is excellent at what it does, but it&#8217;s designed to detect malware, brute force attempts, and known vulnerability exploits. It&#8217;s not analyzing traffic patterns for resource exhaustion.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">And finally, check your templates for relative protocol links. Any <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">tel:</code>, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">mailto:</code>, or similar protocol URI that gets rendered as a relative path instead of a proper protocol link creates a potential infinite crawl loop that a bot can exploit.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">If you&#8217;ve seen something similar or have questions about implementing these fixes, I&#8217;d like to hear about it. And if you&#8217;re a hosting provider or security researcher, I&#8217;m curious whether this attack pattern has shown up elsewhere. I couldn&#8217;t find any documentation when I went looking.</p>
<p>The post <a href="https://visualharmonydesign.com/2026/02/how-a-95000-ip-botnet-used-tel-links-to-cripple-a-clients-wordpress-site/">How a 95,000-IP Botnet Used tel: Links to Cripple a Client&#8217;s WordPress Site</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Canon Cinema RAW Light Workflow</title>
		<link>https://visualharmonydesign.com/2020/12/canon-cinema-raw-light-workflow/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Mon, 21 Dec 2020 19:09:12 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://visualharmonydesign.com/?p=637</guid>

					<description><![CDATA[<p>Here&#8217;s a Cinema RAW Light workflow I discovered over the weekend. This approach uses Davinci Resolve but you could use a similar process in other NLEs. In terms of practicality and for our needs, this is superior to using the Canon CRD software. I shot about an hour of RAW footage for a client last &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2020/12/canon-cinema-raw-light-workflow/" class="more-link">Continue reading<span class="screen-reader-text"> "Canon Cinema RAW Light Workflow"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2020/12/canon-cinema-raw-light-workflow/">Canon Cinema RAW Light Workflow</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Here&#8217;s a Cinema RAW Light workflow I discovered over the weekend.  This approach uses Davinci Resolve but you could use a similar process in other NLEs.  In terms of practicality and for our needs, this is superior to using the Canon CRD software.</p>
<p>I shot about an hour of RAW footage for a client last week.  Even though our main editing workstation is very powerful, it chokes on RAW clips when editing natively in an NLE once you start keyframing and adding effects.  So, I needed to widdle them down to something more manageable that can be edited without hiccups.  Also, I think it defeats the purpose of shooting in RAW if you are going to transcode before adjusting RAW settings. </p>
<p>Here&#8217;s the approach I came up with:</p>
<ol>
<li>Open RAW clip in Davinci Resolve</li>
<li>Trim clip to only the good stuff.  This will need to be done at some point during the project, so you might as well do it now.  Trim off everything that won&#8217;t be usable in your final edit.  Feel free to leave handles if you aren&#8217;t sure of the best place to cut.  This will save a lot of time because it makes no sense to render/transcode extra footage that will never be used.</li>
<li>Adjust RAW settings</li>
<li>Adjust exposure (if needed)</li>
<li>Remove noise (if present)</li>
<li>Stabilize (if needed)</li>
<li>Crop (if desired)</li>
<li>Render clip to DNxHR HQX 10-bit (or other format if preferred for your situation).  The settings I used are: Format: Quicktime, Codec: DNxHR, Type: DNxHR HQX 10-bit.  The new file will be rendered with the adjusted RAW settings baked-in, and it will be far less likely to cause frame-stutter in an NLE when editing and adding effects.</li>
<li>Repeat steps 1-8 for each RAW clip you have.</li>
</ol>
<p>Now, instead of sending my editing partner a bunch of unadjusted RAW clips (doing so would not spark joy), I can now send her manageable .MOV files that have already been pre-trimmed, are exposed properly, and are ready to drop on the timeline and color grade.</p>
<p>Optionally, you can delete the RAW files afterwards since you already have the good stuff rendered at the size and format you need it to be in.  I&#8217;ll still keep the RAW clips until the project has been delivered, but I will certainly be deleting them and only storing the 4:2:2 clips after final approval.</p>
<p>I hope this is helpful for fellow Canon shooters.  Some of you may already be doing it this way, but it&#8217;s new to me.  This workflow is easy and takes all the pressure out of shooting RAW for me.  And since we spend the bulk of our project time in the editing room, this will save a ton of time on each of our projects going forward without having to sacrifice RAW capabilities.</p>
<p>The post <a href="https://visualharmonydesign.com/2020/12/canon-cinema-raw-light-workflow/">Canon Cinema RAW Light Workflow</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Defining Your Digital Marketing Strategy</title>
		<link>https://visualharmonydesign.com/2018/10/defining-your-digital-marketing-strategy/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Wed, 17 Oct 2018 21:41:08 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://visualharmonydesign.com/?p=612</guid>

					<description><![CDATA[<p>One of the keys to a successful digital marketing campaign is to have a well-defined strategy. At Visual Harmony, we work with our clients to develop and implement a custom digital marketing strategy that will help them reach the top of the search results for their desired keyword phrases. Search Engine Optimization Overview First, we &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2018/10/defining-your-digital-marketing-strategy/" class="more-link">Continue reading<span class="screen-reader-text"> "Defining Your Digital Marketing Strategy"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2018/10/defining-your-digital-marketing-strategy/">Defining Your Digital Marketing Strategy</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>One of the keys to a successful digital marketing campaign is to have a well-defined strategy. At Visual Harmony, we work with our clients to develop and implement a custom digital marketing strategy that will help them reach the top of the search results for their desired keyword phrases.</p>
<h2>Search Engine Optimization Overview</h2>
<p>First, we review their website and prepare a comprehensive report of the current state of their search profile. Next, we identify key competitors and assess what is and isn&#8217;t working for them. <span id="more-612"></span> We then use this data to increase the amount and quality of traffic to the site.  All this is done while staying within their monthly budget. Then, we go through and perform a laundry-list of on-site and off-site search engine optimization (SEO) tasks. Some of these include making sure their XML sitemap is set up and includes all important pieces of content on the site and handling the process of improving page load speed and speed scores.</p>
<h2>Pay-Per-Click Campaign Setup and Managment</h2>
<p>We also set up and provide ongoing management of pay-per-click advertising campaigns. We are able to identify keywords that will perform well and monitor and track their performance. Adjustments are made as needed so that clients are always receiving the highest return from their advertising budget. We are also able to identify keyword phrases which underperform so that these are avoided and do not drain resources.</p>
<h2>Accepting New Digital Marketing Projects in January 2019</h2>
<p>We just wanted let everyone know that we are offering these services again. We have many SEO experts in our network and can provide unmatched service and results when it comes to developing and implementing a successful digital marketing strategy for clients in Ocala, Belleview, and all of Central Florida. Currently, our schedule is fully booked through the end of 2018 but we are currently accepting appointments for new digital marketing projects starting in January 2019. <a href="https://visualharmonydesign.com/contact/">Contact us to schedule an appointment in early 2019</a>.</p>
<p>The post <a href="https://visualharmonydesign.com/2018/10/defining-your-digital-marketing-strategy/">Defining Your Digital Marketing Strategy</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Visual Harmony is Now Offering Video Production Services and is No Longer Developing Apps!</title>
		<link>https://visualharmonydesign.com/2018/03/visual-harmony-is-now-offering-video-production-services-and-is-no-longer-developing-apps/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Mon, 19 Mar 2018 16:09:13 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://visualharmonydesign.com/?p=586</guid>

					<description><![CDATA[<p>We are very excited to announce that we are now offering video production services! We have acquired a cinema camera and have partnered with another local videographer to bring Ocala the quality video production services it needs. Our first productions are underway and we will be ready to book new commercial video productions beginning in &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2018/03/visual-harmony-is-now-offering-video-production-services-and-is-no-longer-developing-apps/" class="more-link">Continue reading<span class="screen-reader-text"> "Visual Harmony is Now Offering Video Production Services and is No Longer Developing Apps!"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2018/03/visual-harmony-is-now-offering-video-production-services-and-is-no-longer-developing-apps/">Visual Harmony is Now Offering Video Production Services and is No Longer Developing Apps!</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>We are very excited to announce that we are now offering video production services!  We have acquired a cinema camera and have partnered with another local videographer to bring Ocala the quality video production services it needs.  Our first productions are underway and we will be ready to book new commercial video productions beginning in July.  More information and examples of our work will be added to the website as they are ready.</p>
<p>Also, after much consideration, we are no longer offering app development or app maintenance services.  We wanted apps to be a staple part of our business.  We really did.  However, more often than not, they were more trouble than they were worth.  I&#8217;ve lost track of how many times we discussed an app with prospective clients and had them walk away from sticker shock due to the extensive amount of time that goes in to planning, developing, launching and maintaining an app.  We developed some great apps over the years but the required ongoing maintenance is too costly for most clients in this area.  So, rather than continue to support an area of business that hasn&#8217;t been fruitful, I am eliminating this service offering so that we can put 100% of our energy into continuing to build the best websites in the area and to make room for our new video production services!</p>
<p>The post <a href="https://visualharmonydesign.com/2018/03/visual-harmony-is-now-offering-video-production-services-and-is-no-longer-developing-apps/">Visual Harmony is Now Offering Video Production Services and is No Longer Developing Apps!</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Recently Completed Web Projects</title>
		<link>https://visualharmonydesign.com/2018/01/recently-completed-web-projects/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Thu, 25 Jan 2018 20:14:31 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">http://visualharmonydesign.com/?p=492</guid>

					<description><![CDATA[<p>We&#8217;ve been really busy at Visual Harmony over the past several months. We built and launched new websites for the ELC of Marion County, Ocala Oral Surgery, Owen Construction, Managed Care Consultants, LTC Systems and J. Lowe&#8217;s Guide Service and each of these projects is now on display in our projects area. Stay tuned, we &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2018/01/recently-completed-web-projects/" class="more-link">Continue reading<span class="screen-reader-text"> "Recently Completed Web Projects"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2018/01/recently-completed-web-projects/">Recently Completed Web Projects</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>We&#8217;ve been really busy at Visual Harmony over the past several months.  We built and launched new websites for the ELC of Marion County, Ocala Oral Surgery, Owen Construction, Managed Care Consultants, LTC Systems and J. Lowe&#8217;s Guide Service and each of these projects is now on display in our <a href="http://visualharmonydesign.com/projects/">projects area</a>.</p>
<p>Stay tuned, we have some amazing projects in the works that will be added to the projects section after they launch.</p>
<p>The post <a href="https://visualharmonydesign.com/2018/01/recently-completed-web-projects/">Recently Completed Web Projects</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Celebrating 10 Years of Web Development</title>
		<link>https://visualharmonydesign.com/2017/02/celebrating-10-years-of-web-development/</link>
		
		<dc:creator><![CDATA[VisualHarmony]]></dc:creator>
		<pubDate>Mon, 20 Feb 2017 21:05:41 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">http://visualharmonydesign.com/new/?p=220</guid>

					<description><![CDATA[<p>We are very happy to be celebrating 10 years of providing quality web development services! It seems like it was only yesterday when Visual Harmony was founded to serve the web development needs of the Central Florida community.  It&#8217;s hard to believe that was all the way back in 2007! Many thanks to all of &#8230; </p>
<p class="link-more"><a href="https://visualharmonydesign.com/2017/02/celebrating-10-years-of-web-development/" class="more-link">Continue reading<span class="screen-reader-text"> "Celebrating 10 Years of Web Development"</span></a></p>
<p>The post <a href="https://visualharmonydesign.com/2017/02/celebrating-10-years-of-web-development/">Celebrating 10 Years of Web Development</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>We are very happy to be celebrating 10 years of providing quality web development services!</p>
<p>It seems like it was only yesterday when Visual Harmony was founded to serve the web development needs of the Central Florida community.  It&#8217;s hard to believe that was all the way back in 2007!</p>
<p>Many thanks to all of the wonderful people and businesses we have been fortunate to work with over the years.  Here&#8217;s to the next 10!</p>
<p>The post <a href="https://visualharmonydesign.com/2017/02/celebrating-10-years-of-web-development/">Celebrating 10 Years of Web Development</a> appeared first on <a href="https://visualharmonydesign.com">Visual Harmony</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
