Experiment #1 - AWS + EC2 + CloudFront + AWS WAF
1. Objective of the Experiment
This first experiment evaluates a fully AWS-native approach to hosting WordPress with three core constraints:
- maximum security,
- minimum cost,
- minimum maintenance.
The goal was to see whether a simple EC2-based design, protected by CloudFront and AWS WAF, could realistically meet these requirements for a small website.
2. Context and Technical Constraints
Security Requirements
Security considerations were split into two distinct layers:
Infrastructure security (AWS architecture)
Protect the EC2 origin, reduce attack surface, enforce strict edge filtering, and isolate the application behind CloudFront and WAF.Application security (WordPress itself)
Protect WordPress and its inherently vulnerable endpoints through ZTNA solutions, access control wrappers, and selective authentication.
Functional Requirements
- CDN-based acceleration (CloudFront caching).
- Consistent TLS termination with ACM.
- Ability to handle modest traffic without scaling.
- Keep the stack small and comprehensible.
Cost Requirements
- Keep monthly costs as low as possible.
- Avoid unpredictable charges such as WAF request fees or CloudFront log ingestion.
- Stay close to free-tier or near-free-tier limits where realistic.
Maintenance Requirements
- Prefer managed certificates over self-managed ones.
- Avoid complex automation or orchestration.
- Limit the number of moving parts requiring regular patching.
Architecture Diagram
flowchart TD
User["User / Browser"] --> CF["CloudFront\nACM TLS"]
CF --> WAF["AWS WAF\nManaged & Custom Rules"]
WAF --> EC2["EC2 Instance\nWordPress + PHP + Nginx/Apache"]
EC2 --> EBS["EBS Volume\nWordPress Files"]
Admin["Admin / Ops"] --> SSM["SSM Session Manager"]
SSM --> EC2
classDef edge fill:#f3f4ff,stroke:#4f46e5,stroke-width:1px,color:#111;
classDef origin fill:#ecfeff,stroke:#0891b2,stroke-width:1px,color:#111;
classDef mgmt fill:#fef9c3,stroke:#eab308,stroke-width:1px,color:#111;
class CF,WAF edge;
class EC2,EBS origin;
class SSM,Admin mgmt;
Figure A — Wordpress with Cloudfront and WAF at the edge.
3. Architecture Tested
3.1. AWS Infrastructure Components
EC2 instance
Runs WordPress (PHP + Nginx/Apache) on a lightweight instance with EBS storage.CloudFront
- Primary edge layer.
- TLS termination using ACM.
- Caches static content and acts as a global distribution layer.
AWS WAF
- Attached to CloudFront.
- Used AWS Managed Rules + basic custom rules.
Security Groups
- Only CloudFront allowed to reach the origin.
- SSH disabled; SSM Session Manager for access.
IAM
- Minimal permissions for instance roles, following least privilege.
3.2 Infrastructure Security (AWS Architecture)
Key security controls at this layer:
- Edge filtering via AWS WAF (managed rules + rate limiting).
- Origin lockdown so the EC2 instance is not publicly reachable.
- TLS coverage using ACM with automatic renewal.
- Strict header forwarding to prevent origin bypass.
- No SSH exposed; all administrative access goes through SSM.
3.3. Application Security (WordPress Layer)
WordPress exposes several high-risk endpoints (/wp-login.php, /wp-admin/, xmlrpc.php).
Admin Access Security Flow
flowchart TD
Admin["Admin User"] --> Gateway{"Admin Access<br/>Protection Choice"}
Gateway --> Twingate["Twingate ZTNA\nPrivate Connector"]
Gateway --> CFZTNA["Cloudflare ZTNA\nAccess + Tunnel"]
Gateway --> WAFH["CloudFront + AWS WAF\nLogin Hardening"]
Gateway --> Cognito["Amazon Cognito\nFront Gate (PoC)"]
Twingate --> WPAdmin["WordPress Admin\n/wp-admin"]
CFZTNA --> WPAdmin
WAFH --> WPAdmin
Cognito --> WPAdmin
%% Styles
classDef ztna fill:#ecfeff,stroke:#0369a1,stroke-width:1px,color:#111;
classDef hardening fill:#fef9c3,stroke:#d97706,stroke-width:1px,color:#111;
classDef target fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#111;
class Twingate,CFZTNA,Cognito ztna;
class WAFH hardening;
class WPAdmin target;
1. Twingate ZTNA
- Only authenticated users on trusted devices could reach the WordPress admin.
- Traffic routed through a private Twingate connector.
- Very strong security (identity + device posture).
- Downside: breaks the normal “public admin panel” workflow; creates friction; unsuitable for anonymous visitors if misconfigured.
2. Cloudflare ZTNA (Access + Tunnel)
- A Cloudflare Tunnel exposed the EC2 origin privately — no public origin IP.
- Cloudflare Access required user authentication (email, OAuth, etc.) before reaching
/wp-admin. - Very convenient and extremely secure.
- But introduces Cloudflare dependency and additional moving parts.
3. CloudFront + AWS WAF hardening
- Specific WAF rules to protect
wp-login.php:- rate limiting,
- CAPTCHA challenges,
- country restrictions,
- bot mitigation.
- Blocked XML-RPC entirely.
- Worked well, but adds cost and required continuous tuning.
4. Amazon Cognito Authentication (PoC)
- Tested as a “front gate” before WordPress admin.
- Technically possible with CloudFront functions or Lambda@Edge.
- Adds strong security, but:
- increases architectural complexity,
- complicates login workflows,
- problematic for plugins expecting native WordPress auth.
3.4. High-Level Traffic Flow
- Visitor connects to CloudFront over HTTPS.
- CloudFront enforces WAF rules, headers, and TLS policies.
- CloudFront forwards valid requests to the EC2 origin.
- WordPress processes dynamic requests; static assets are cached at the edge.
3.5. Technical Observations
- CloudFront requires careful cache policy design to avoid breaking WordPress admin pages.
- WAF rules are powerful but can become expensive quickly.
- Debugging issues involving caching, cookies, and forwarded headers is non-trivial.
- Application-level security solutions (ZTNA, Cognito) add strength but increase complexity.
4. Results and Analysis
4.1. Pros
- Fully AWS-native architecture with excellent integration.
- Strong infrastructure security via CloudFront + WAF.
- Global performance is very good with CloudFront caching.
- ACM certificates simplify TLS management.
- SSM Session Manager removes the need for SSH exposure.
- Multiple ZTNA options tested (Twingate, Cloudflare) showed that admin access can be hardened effectively.
4.2. Cons
- Significant configuration complexity, even for a small site.
- WAF costs can escalate rapidly depending on traffic volume.
- CloudFront + WordPress requires non-trivial adjustments to caching logic.
- ZTNA + WordPress can break workflows and complicate plugin behavior.
- EC2 maintenance remains your responsibility (OS, PHP, WordPress updates).
- Cognito integration is difficult and adds significant friction.
- Error visibility is poor when something breaks between CloudFront → origin → WordPress.
4.3. Estimated Costs
Approximate monthly cost (low-traffic scenario):
- EC2 instance: CHF 8–12
- EBS storage: CHF 1–3
- CloudFront: CHF 1–15
- AWS WAF: CHF 20–25 minimum
- Monitoring / logs: CHF 1–5
Total: ~CHF 30–50 per month.
This is higher than expected for a simple WordPress site and fails the “very low cost” objective.
5. Conclusion
Experiment #1 delivers strong edge security and robust AWS-native protections, but it also reveals clear limitations:
- ZTNA solutions (Twingate, Cloudflare Access) are powerful but add operational friction.
- WAF adds meaningful protection but with unpredictable cost.
- Cognito introduces heavy architectural complexity.
- The EC2 instance still requires regular maintenance and patching.
While the setup is secure and performant, it does not meet the goal of “minimal cost and minimal maintenance” for a small WordPress installation.
The next article explores a different approach: simplifying the security layer by using Cloudflare (CDN + WAF + Access) instead of CloudFront + AWS WAF, potentially reducing complexity and lowering the overall bill.