Options -Indexes
DirectoryIndex index.php index.html

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

# Consolidate public organic-search URLs on the www hostname configured in
# SITE_URL. Avoid touching authentication, payment, webhook or installer routes.
# Redirect GET/HEAD only; never change the destination of a form POST.
RewriteCond %{HTTP_HOST} ^coriable\.com$ [NC]
RewriteCond %{REQUEST_METHOD} ^(?:GET|HEAD)$
RewriteCond %{REQUEST_URI} !^/(?:admin/|portal/|api/|webhooks/|install(?:\.php)?|update\.php|pay\.php|payment-callback\.php) [NC]
RewriteRule ^(.*)$ https://www.coriable.com/$1 [R=301,L,NE]

# Deny private SQL, CI and test assets at the web server boundary.
RewriteRule ^(?:tests|scripts|database|\.github|private|backups|backup|tmp|logs)(?:/|$) - [F,L,NC]

# Client Hub subdomain. Point clients.coriable.com to this same document root.
RewriteCond %{HTTP_HOST} ^clients\.coriable\.com$ [NC]
RewriteRule ^$ portal/index.php [L,QSA]

# Machine-readable discovery endpoints are dynamic so crawler access can be measured.
RewriteRule ^robots\.txt$ robots.php [L,QSA,NC]
RewriteCond %{HTTP_HOST} !^clients\.coriable\.com$ [NC]
RewriteRule ^llms-full\.txt$ llms.php?full=1 [L,QSA,NC]

# Keep the Client Hub hostname dedicated to the portal. Only shared public
# assets and the CSP reporting endpoint bypass the portal router.
RewriteCond %{HTTP_HOST} ^clients\.coriable\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/(?:img|favicon|uploads/media)(?:/|$) [NC]
RewriteCond %{REQUEST_URI} !^/(?:robots\.txt|csp-report\.php)$ [NC]
RewriteCond %{REQUEST_URI} !^/portal(?:/|$) [NC]
RewriteRule ^(.+)$ portal/$1 [L,QSA]

# The one-time installer must work on cPanel even when the generic PHP
# extensionless rewrite or DOCUMENT_ROOT filesystem check is unavailable.
# The PHP installer still requires an empty install lock and server-side
# authorization before any database change. Direct /install.php also works.
RewriteRule ^install/?$ install.php [L,QSA,NC]

# Preserve existing files and directories.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# Public clean URLs.
RewriteRule ^solutions/([A-Za-z0-9-]+)/?$ service-single.php?slug=$1 [L,QSA,NC]
RewriteRule ^industries/([A-Za-z0-9-]+)/?$ industry-single.php?slug=$1 [L,QSA,NC]
RewriteRule ^blog/([A-Za-z0-9-]+)/?$ blog-single.php?slug=$1 [L,QSA,NC]
RewriteRule ^guides/([A-Za-z0-9-]+)/?$ guide.php?slug=$1 [L,QSA,NC]
RewriteRule ^work/([A-Za-z0-9-]+)/?$ work-single.php?slug=$1 [L,QSA,NC]

# Machine-readable endpoints advertised in the document head.
RewriteRule ^sitemap\.xml$ sitemap.php [L,QSA,NC]
RewriteRule ^feed\.xml$ feed.php [L,QSA,NC]
RewriteRule ^llms\.txt$ llms.php [L,QSA,NC]
RewriteRule ^indexnow-key\.txt$ indexnow-key.php [L,QSA,NC]

# Extensionless fallback for existing PHP pages.
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([A-Za-z0-9-]+)/?$ $1.php [L,QSA,NC]

# Everything else uses the custom 404 page.
RewriteRule ^ 404.php [L]
</IfModule>

<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(self), usb=()"
Header always set X-Permitted-Cross-Domain-Policies "none"
</IfModule>

<FilesMatch "(^\.|\.env$|composer\.(json|lock)$|.*\.(sql|log|md|bak|ini|sh|py|zip|tar|gz|db|sqlite)$|install\.lock$)">
Require all denied
</FilesMatch>

# V15.2 public performance. Static assets only; dynamic portal/admin/payment
# pages MUST NOT receive shared cache headers or cross-user HTML caching.
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json image/svg+xml application/xml
</IfModule>
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType text/css "access plus 7 days"
  ExpiresByType application/javascript "access plus 7 days"
  ExpiresByType text/javascript "access plus 7 days"
  ExpiresByType image/png "access plus 30 days"
  ExpiresByType image/jpeg "access plus 30 days"
  ExpiresByType image/webp "access plus 30 days"
  ExpiresByType image/svg+xml "access plus 30 days"
  ExpiresByType image/x-icon "access plus 30 days"
  ExpiresByType video/mp4 "access plus 30 days"
  ExpiresByType video/webm "access plus 30 days"
</IfModule>
<IfModule mod_headers.c>
  <FilesMatch "\.(css|js|png|jpe?g|webp|svg|ico|mp4|webm)$">
    Header set Vary "Accept-Encoding"
    Header set X-Content-Type-Options "nosniff"
  </FilesMatch>
</IfModule>

# V15.9 MP4 MIME and range requests for mobile inline playback.
<IfModule mod_mime.c>
  AddType video/mp4 .mp4 .m4v
  AddType video/webm .webm
</IfModule>
