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): 22 from vendor IP, 80 and 443 from 0.0.0.0/0
  • Security group (after CloudFront is wired up): 22 from vendor IP, 443 from CloudFront managed prefix list (com.amazonaws.global.cloudfront.origin-facing) only, 80 closed

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 certbot on the host (not in a container).
  • Issued with --standalone challenge against wp.yoyacoo.jp (or wp-stg.yoyacoo.jp for stg).
  • Cert path: /etc/letsencrypt/live/<hostname>/ (mounted read-only into the nginx container).
  • Auto-renewal cron: 0 3 * * * running certbot 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.jp references 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
  • ufw enabled 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-content tar 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

  1. https://yoyacoo.jp/ returns 200 with WP content.
  2. https://yoyacoo.jp/price/ returns 200 with WP content.
  3. https://yoyacoo.jp/wp-content/uploads/... returns 200 with image/* content type.
  4. https://yoyacoo.jp/robots.txt returns 200.
  5. https://yoyacoo.jp/sitemap.xml returns 200.

App routes preserved

  1. https://yoyacoo.jp/user/login continues to serve the Next.js login page.
  2. https://yoyacoo.jp/customer/lp/... continues to serve the Next.js LP.

Content integrity

  1. HTML body of https://yoyacoo.jp/ contains no bourne-design.jp references.
  2. HTML body of https://yoyacoo.jp/ contains no http:// mixed-content URLs.
  3. HTML body of https://yoyacoo.jp/ contains yoyacoo.jp links.

Security

  1. 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-admin IP allowlist
  • Cache policy optimization (start with default; WP Cache-Control headers respected)
  • Migration of the existing ALB-backed /user and /customer apps
  • Content editing or design changes (marketing team)

Pre-Cutover Checklist

  1. EC2s (stg + prod) provisioned per §2.
  2. DNS CNAMEs in place and propagated (dig +short wp.yoyacoo.jp returns the Elastic IP).
  3. Vendor has migrated content, set WP_HOME/WP_SITEURL, and scrubbed the DB per §5.
  4. SEO plugin active; robots.txt and sitemap.xml accessible.
  5. Cert auto-renewal cron installed (crontab -l | grep certbot).
  6. Test the EC2 directly (with a temporary security group rule for the tester’s IP) — /, /price/, sample media all return 200.
  7. CloudFront origin added; cache behaviors configured; deployed to stg.
  8. Stg smoke test passes (all 11 criteria above, against stg.yoyacoo.jp).
  9. Same CloudFront deploy to prod distribution.
  10. Prod smoke test passes.
  11. Remove any temporary security group rules used for direct EC2 testing.