| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /etc/apache2/sites-available/ |
Upload File : |
# Plan 294 §Stage 0 — Catch-all Apache vhost for tenant renders.
# Drop this file in /etc/apache2/sites-available/ and `a2ensite` it.
#
# Loaded AFTER portal.scopeforged.com + admin.scopeforged.com vhosts so
# their exact-match ServerName wins on those hosts. The wildcard
# ServerAlias here catches every `{slug}.{parent}` request, which the
# RouteWebsiteTenant middleware then resolves to a Website row via the
# Redis active_hosts cache (Plan 270 §Phase 5).
#
# Parent-domain ServerAlias list MUST match config('websites.parent_domains')
# in the Laravel codebase. Adding a parent is two edits:
# 1. config/websites.php parent_domains array
# 2. ServerAlias line below + reload Apache
#
# Wildcard cert is one ACM-or-Certbot cert per parent stored at
# /etc/letsencrypt/live/{parent}-wildcard/. The Certbot DNS-01 renewal
# cron handles renewal; the Stage 0 deploy guide covers the initial issue.
<VirtualHost *:443>
ServerName _tenant_catchall_
# Plan 294 — Parent domains. Add a ServerAlias line per parent.
ServerAlias *.scopeforged.com
# ServerAlias *.your-second-parent.com
# ServerAlias *.your-third-parent.com
UseCanonicalName Off
DocumentRoot /var/www/client-portal-laravel/current/public
SSLEngine on
# One wildcard cert per parent — the first matching cert is served via SNI.
SSLCertificateFile /etc/letsencrypt/live/scopeforged.com-wildcard/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/scopeforged.com-wildcard/privkey.pem
# SNI must accept hosts not in this vhost's literal ServerAlias list
# (BYO custom-domain hosts arrive here too once cert-prov has issued
# them — they have their own per-domain cert in ssl-domains.conf).
SSLStrictSNIVHostCheck off
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
# Reserved tenant paths — Certbot HTTP-01 challenge dir served as
# static before reaching the renderer (Plan 294 §Phase B-2).
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory /var/www/letsencrypt/.well-known/acme-challenge/>
Require all granted
</Directory>
# Scanner-trap paths — fast 404 from Apache without hitting FPM
# (Plan 294 §Phase C reserved-path list).
<LocationMatch "^/(wp-login\.php|xmlrpc\.php|\.env|admin/config\.php|\.well-known/security\.txt)$">
Require all denied
</LocationMatch>
<Directory /var/www/client-portal-laravel/current/public>
AllowOverride None
Require all granted
# Front controller via mod_rewrite — same as the portal vhost.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</Directory>
# Plan 294 §Stage 0 — Tenant traffic logs go to their own files so
# the portal/admin logs stay clean. Format is JSON for CloudWatch
# Logs ingestion (Lock #15).
LogFormat "{ \"time\":\"%{%FT%T%z}t\", \"host\":\"%V\", \"src_ip\":\"%a\", \"method\":\"%m\", \"uri\":\"%U%q\", \"status\":%>s, \"bytes\":%B, \"dur_us\":%D, \"ua\":\"%{User-Agent}i\" }" tenant_json
ErrorLog ${APACHE_LOG_DIR}/tenant-sites-error.log
CustomLog ${APACHE_LOG_DIR}/tenant-sites-access.log tenant_json
</VirtualHost>
# Pre-cert HTTP gap (Plan 294 §Phase B-2) — serve the "issuing certificate"
# view over plain HTTP for BYO domains before their cert is bound.
# 301-to-HTTPS is enabled only after RegenerateSSLDomainsConfJob writes
# the per-domain cert + reloads.
<VirtualHost *:80>
ServerName _tenant_catchall_http_
ServerAlias *.scopeforged.com
DocumentRoot /var/www/client-portal-laravel/current/public
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory /var/www/letsencrypt/.well-known/acme-challenge/>
Require all granted
</Directory>
# Subdomain customers (already covered by parent wildcard) → 301 to HTTPS.
# BYO customers without a per-domain cert yet → fall through to Laravel
# which serves the "issuing cert" view with X-Robots-Tag: noindex.
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} \.scopeforged\.com$
RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L]
<Directory /var/www/client-portal-laravel/current/public>
AllowOverride None
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</Directory>
ErrorLog ${APACHE_LOG_DIR}/tenant-sites-http-error.log
CustomLog ${APACHE_LOG_DIR}/tenant-sites-http-access.log combined
</VirtualHost>