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.