Specification: CloudFront Routing for WordPress Homepage Migration
Objective
Migrate the homepage and general content management for yoyacoo.jp to a WordPress system running on a new EC2 instance, while keeping the existing Next.js web application for the app routes (/user*, /customer*) served by the current ALB.
bourne-design.jp is dropped from the migration. The WP site will be served from a fresh EC2 origin accessed via CloudFront at the new hostname wp.yoyacoo.jp.
Current Architecture
Production
- Domain:
yoyacoo.jp - CDN: CloudFront Distribution
E1J6MUY16GJP2C - Primary Origin: Production ALB (
prod-yoyacoo-alb-...)
Staging
- Domain:
stg.yoyacoo.jp - CDN: CloudFront Distribution
E3QN4AIIY399LD - Primary Origin: Staging ALB (
stg-yoyacoo-alb-...)
Proposed Architecture
| Path | Origin | Notes |
|---|---|---|
yoyacoo.jp/user* |
Existing ALB | Unchanged |
yoyacoo.jp/customer* |
Existing ALB | Unchanged |
yoyacoo.jp/* (everything else) |
New EC2 origin via wp.yoyacoo.jp |
WordPress |
The new EC2 origin is a single Ubuntu 24.04 instance per environment running the full WordPress stack in Docker. Same hostname (wp.yoyacoo.jp) for both prod and stg, pointing to two separate EC2 instances. This means stg and prod share the same WordPress content; staging is used for deploys/infrastructure changes, not isolated content edits.
Technical Requirements
1. CloudFront Configuration Changes (apply to both distributions)
A. Add New Origin
| Field | Value |
|---|---|
| Origin domain | wp.yoyacoo.jp (prod) / wp-stg.yoyacoo.jp (stg) |
| Origin ID | wordpress-origin |
| Protocol | HTTPS only |
| Minimum origin SSL protocol | TLSv1.2 |
| Origin Custom Headers | Host: wp.yoyacoo.jp (prod) / Host: wp-stg.yoyacoo.jp (stg) |
| Origin Path | (empty) |
| Connection attempts | 3 |
| Connection timeout | 10s |
B. Update Cache Behaviors
| Path Pattern | Target Origin | Priority | Notes |
|---|---|---|---|
/user* |
Respective ALB | 0 | App routes for suppliers |
/customer* |
Respective ALB | 1 | App routes for customers |
Default (*) |
wordpress-origin |
Default | Changed: Homepage, blogs, and other content |
Note: separate /_next/* rules are not required. /user* and /customer* already cover their respective basePath-prefixed asset paths.
C. No CloudFront Functions
Body and path rewrites are not needed. The vendor’s clean WP install + correct siteurl / home handles URL consistency. The only first-party hostnames in HTML should be yoyacoo.jp and stg.yoyacoo.jp.
2. WordPress Origin (new EC2 per environment)
A. Instance
| Field | Value |
|---|---|
| Region | ap-northeast-1 (Tokyo) |
| AMI | Ubuntu 24.04 LTS (x86_64, official Canonical) |
| Instance type | t3.small (2 vCPU, 2 GB RAM) |
| Root volume | 30 GB gp3, encrypted (AWS-managed KMS key) |
| Elastic IP | Allocated and attached |
B. Networking
- Public IP at launch: enabled (needed to fetch the cert on first boot)
- Security group (initial):
22from vendor IP,80and443from0.0.0.0/0 - Security group (after CloudFront is wired up):
22from vendor IP,443from CloudFront managed prefix list (com.amazonaws.global.cloudfront.origin-facing) only,80closed
C. Stack (Docker, three services in /opt/yoyacoo-wp/docker-compose.yml)
| Service | Image | Purpose |
|---|---|---|
db |
mariadb:11 |
WordPress database |
wordpress |
wordpress:6-php8.3-fpm |
WordPress core (FPM only, no web server) |
nginx |
nginx:1.27-alpine |
HTTPS termination, static file serving, reverse proxy to FPM |
Shared named volume wp_html mounted read-write in wordpress and read-only in nginx, so nginx serves static assets (/wp-content/uploads/*, theme CSS/JS) directly without round-tripping to PHP.
D. Host nginx config (/opt/yoyacoo-wp/nginx.conf)
server {
listen 80;
server_name wp.yoyacoo.jp;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name wp.yoyacoo.jp;
ssl_certificate /etc/letsencrypt/live/wp.yoyacoo.jp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/wp.yoyacoo.jp/privkey.pem;
root /var/www/html;
index index.php;
client_max_body_size 64m;
location ~* \.(js|css|png|jpe?g|gif|webp|ico|svg|woff2?)$ {
expires 30d;
access_log off;
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass wordpress:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 60s;
}
}
E. WordPress configuration
Set via WORDPRESS_CONFIG_EXTRA env var in the wordpress container:
define('WP_HOME', 'https://yoyacoo.jp'); // viewer domain
define('WP_SITEURL', 'https://yoyacoo.jp');
Other WP settings:
- Site title:
yoyacoo - Admin email: ops contact
- Timezone:
Asia/Tokyo - Locale:
ja - Permalink structure:
/%postname%/
F. Database
| Field | Value |
|---|---|
| Engine | MariaDB 11 (the db container) |
| Database name | wordpress |
| Database user | wp |
| Database password | Random 24-char, stored in /opt/yoyacoo-wp/.env (mode 600) |
| Root password | Random (MARIADB_RANDOM_ROOT_PASSWORD: "yes") |
| Character set | utf8mb4, collation utf8mb4_unicode_ci |
G. SSL
- Let’s Encrypt via
certboton the host (not in a container). - Issued with
--standalonechallenge againstwp.yoyacoo.jp(orwp-stg.yoyacoo.jpfor stg). - Cert path:
/etc/letsencrypt/live/<hostname>/(mounted read-only into thenginxcontainer). - Auto-renewal cron:
0 3 * * *runningcertbot renew --quiet --deploy-hook "docker exec $(cd /opt/yoyacoo-wp && docker compose ps -q nginx) nginx -s reload".
3. DNS
| Env | Hostname | Type | Target |
|---|---|---|---|
| Prod | wp.yoyacoo.jp |
CNAME | <prod-elastic-ip> |
| Stg | wp-stg.yoyacoo.jp |
CNAME | <stg-elastic-ip> |
DNS must be propagated before certbot runs on first boot.
4. Required WordPress Plugins
- SEO: Rank Math or Yoast
- Security: Wordfence or equivalent
- Better Search Replace (for the URL migration step in §5)
- WP fail2ban or equivalent login throttling
- Disable: Hello Dolly, Akismet (unless requested)
5. Content Migration
- Export content from
https://bourne-design.jp/yoyacoo/using a migration tool (All-in-One WP Migration, Duplicator, or similar). - Import into the new EC2 WordPress install.
- Run Better Search Replace to convert:
http://bourne-design.jp/yoyacoo/→https://yoyacoo.jp/https://bourne-design.jp/yoyacoo/→https://yoyacoo.jp/
- Verify no
bourne-design.jpreferences remain in the DB. - Verify no
http://media URLs remain.
6. Hardening
- SSH: key-only auth, password login disabled, root login disabled, vendor IP allowlist
ufwenabled with the same rules as the security group- Fail2ban installed and running
- WordPress auto-updates enabled for minor versions
- Plugin/theme updates: weekly cadence
7. Backups
- Daily EBS snapshot of the root volume, retained 7 days
- Daily DB dump to S3, retained 7 days
- Daily
wp-contenttar to S3, retained 7 days
Vendor Scope
A third-party vendor handles the EC2 setup. The vendor is responsible for items 2–7 above. The internal team (us) is responsible for:
- CloudFront configuration changes
- DNS record creation
- Smoke testing the cutover
Rollback Plan
- To rollback, change the
Default (*)behavior back to the ALB Origin. The EC2 and WP install stay in place; nothing to clean up. Re-apply the change once the issue is understood.
Success Criteria
Infrastructure
https://yoyacoo.jp/returns 200 with WP content.https://yoyacoo.jp/price/returns 200 with WP content.https://yoyacoo.jp/wp-content/uploads/...returns 200 withimage/*content type.https://yoyacoo.jp/robots.txtreturns 200.https://yoyacoo.jp/sitemap.xmlreturns 200.
App routes preserved
https://yoyacoo.jp/user/logincontinues to serve the Next.js login page.https://yoyacoo.jp/customer/lp/...continues to serve the Next.js LP.
Content integrity
- HTML body of
https://yoyacoo.jp/contains nobourne-design.jpreferences. - HTML body of
https://yoyacoo.jp/contains nohttp://mixed-content URLs. - HTML body of
https://yoyacoo.jp/containsyoyacoo.jplinks.
Security
- Direct request to
https://wp.yoyacoo.jp/from a non-CloudFront IP is blocked (timeout or connection refused).
Out of Scope (for this migration)
- WAF rules on CloudFront (add later as a follow-up)
wp-adminIP allowlist- Cache policy optimization (start with default; WP
Cache-Controlheaders respected) - Migration of the existing ALB-backed
/userand/customerapps - Content editing or design changes (marketing team)
Pre-Cutover Checklist
- EC2s (stg + prod) provisioned per §2.
- DNS CNAMEs in place and propagated (
dig +short wp.yoyacoo.jpreturns the Elastic IP). - Vendor has migrated content, set
WP_HOME/WP_SITEURL, and scrubbed the DB per §5. - SEO plugin active;
robots.txtandsitemap.xmlaccessible. - Cert auto-renewal cron installed (
crontab -l | grep certbot). - Test the EC2 directly (with a temporary security group rule for the tester’s IP) —
/,/price/, sample media all return 200. - CloudFront origin added; cache behaviors configured; deployed to stg.
- Stg smoke test passes (all 11 criteria above, against
stg.yoyacoo.jp). - Same CloudFront deploy to prod distribution.
- Prod smoke test passes.
- Remove any temporary security group rules used for direct EC2 testing.