Chapter #5 of the AWS WordPress series — performance lessons after architecture experiments #1 through #4.
For years, WordPress performance discussions have revolved around the same topics:
- faster VPSes,
- caching plugins,
- Redis,
- LiteSpeed,
- Kubernetes,
- “headless” architectures,
- or simply “more CPU”.
And while those things matter, my experience running WordPress on AWS taught me something different:
Performance problems are often entropy problems before they become compute problems.
In my case, I didn’t dramatically increase the intrinsic performance of the infrastructure itself. Instead, I progressively removed unnecessary work from the EC2 instance.
The result was surprisingly effective not because the server became more powerful, but because it stopped wasting resources serving noise.
1. The Initial Goal
The original objective was relatively simple:
- host WordPress securely,
- keep costs minimal,
- survive traffic spikes,
- avoid overprovisioning,
- and experiment with AWS-native architectural patterns.
At the time, the stack progressively evolved into something like this:
The interesting part is that most optimizations were not focused on making WordPress itself faster, but they were focused on reducing the amount of useless work reaching WordPress.
2. The Myth of “WordPress Performance”
A common misconception is that WordPress performance is mostly about:
- PHP execution speed,
- database tuning,
- plugins,
- server power.
But in practice, a large amount of infrastructure load often comes from:
- bots,
- crawlers,
- brute-force attempts,
- image transformations,
- cache misses,
- malformed requests,
- scanners,
- admin probing,
- XML-RPC abuse,
- and other forms of “background internet noise”.
In other words:
The server is often busy serving things that should never have reached it in the first place.
That realization changed the way I approached optimization.
3. The Image Optimization Rabbit Hole
Like many people, I initially wanted to optimize images dynamically.
The idea was elegant:
- store original images in S3,
- generate WebP variants on demand,
- cache the result,
- serve optimized assets through CloudFront.
The architecture looked roughly like this:
Technically, it worked, but in practice, things became more complicated.
WordPress URL Hell
One unexpected challenge was WordPress itself.
WordPress generates image URLs dynamically through multiple mechanisms:
- src
- srcset
- thumbnails
- responsive image variants
- plugins
- editor blocks
- featured images
So I ended up writing a custom WordPress plugin responsible for rewriting image URLs toward the optimization pipeline.
/wp-content/uploads/image.jpg
Became:
https://cdn.example.com/image.jpg?w=800&format=webp
The problem is that WordPress loves generating variants automatically.
If only src is rewritten but not srcset, browsers may still fetch the original JPEGs.
That led to a long series of experiments involving:
- URL normalization,
- cache key strategies,
- query string handling,
- responsive image variants,
- CDN cache fragmentation.
Ironically, image optimization quickly became one of the most complex parts of the stack.
When CloudFront and CloudFlare Made Things Worse
One particularly interesting moment was when performance unexpectedly degraded after introducing CloudFront into the image processing flow.
This sounds counterintuitive.
CDNs are supposed to improve performance, and they usually do.
But architectures are systems, not magic.
What likely happened was a combination of:
- cache miss amplification,
- query-string fragmentation,
- Lambda cold starts,
- too many generated variants,
- origin fetch latency,
- Sharp initialization overhead,
- inconsistent cache keys.
The result:
the infrastructure spent more time generating and synchronizing image variants than actually serving users efficiently.
This was an important lesson:
Serverless architectures can move complexity around rather than eliminate it.
And it was even worse with CloudFlare, once outside AWS ecosystem.
4. The Invisible Enemy: Bots
The biggest realization came later: A huge amount of traffic was not legitimate human traffic. The EC2 instance was constantly receiving:
- WordPress scans,
- /wp-login.php brute force attempts,
- XML-RPC abuse,
- SEO crawlers,
- vulnerability scanners,
- fake bots,
- random internet noise.
And every request reaching PHP has a cost:
- CPU wakeups,
- PHP workers,
- MySQL queries,
- memory pressure,
- filesystem access,
- network overhead.
At some point I realized:
I didn’t need a stronger server first, but I needed fewer useless requests.
5. WAF as a Performance Tool
This is where AWS WAF became surprisingly effective.
Most discussions present WAFs purely as security controls, but in practice, a WAF can also act as a performance optimization layer by filtering malicious or useless traffic at the edge:
- fewer requests reached PHP,
- fewer workers woke up,
- fewer database queries were executed,
- less bandwidth was consumed,
- and the EC2 instance remained responsive for actual users.
The architecture progressively shifted toward this philosophy:
The EC2 itself did not suddenly become more powerful, it simply stopped wasting resources.
6. The Cognito “Honeypot”
One of the more unusual experiments involved protecting the WordPress administration area behind AWS Cognito.
Not because Cognito magically secured WordPress, but because it reduced the visibility and accessibility of the admin surface itself.
Conceptually:
The interesting effect was not just authentication but it was traffic reduction.
Most automated attacks never even reached WordPress anymore as the admin endpoint became effectively “less visible”.
Again:
the optimization came from reducing useless work before it reached compute resources.
Conclusion: The Real Lesson
Over time, the architecture evolved less around “making WordPress faster” and more around:
- reducing entropy,
- filtering noise,
- externalizing expensive operations,
- minimizing unnecessary PHP execution,
- and protecting compute resources.
This changed the economics dramatically.
A relatively small EC2 instance could survive workloads that would otherwise require far larger infrastructure.
Not because the application became intrinsically faster.
But because the infrastructure became more selective about what deserved compute time.
And in hindsight, the biggest optimization was not caching, it was understanding that:
Performance is often about protecting systems from useless work before optimizing the work itself.



