Bug description
On Statamic 6 (confirmed on v6.16.0), a site that doesn't have a resources/sites.yaml file will treat a direct request to /index.php as a valid request for the homepage and respond with 200 instead of 404. All URLs generated on that response are prefixed with /index.php/.... When full static caching is enabled, that response is written to disk and served as 200 to every subsequent /index.php request indefinitely.
Impact: crawlers spider the /index.php/{slug} link graph, every cached page on those paths contains the same broken prefix, and the control panel becomes inaccessible via the affected route. Statamic 5 sites correctly return a styled 404 in this scenario.
Root cause appears to be:
- Sites::getFallbackConfig() (src/Sites/Sites.php:130) defaults the site URL to '/' (relative) when resources/sites.yaml is absent.
- Site::absoluteUrl() (src/Sites/Site.php:78) calls URL::makeAbsolute() on that relative URL.
- URL::makeAbsolute() prepends the result of URL::getRequestRootUrl() (src/Facades/Endpoint/URL.php:468), which uses Laravel's url()->to('/'). On a direct /index.php request, Laravel/Symfony resolves the request base URL to /index.php, so the "absolute site URL" becomes https://host/index.php for that request.
- Data::findByRequestUrl() (src/Data/DataRepository.php:44) then strips the inflated site URL from the request URL, leaving '', which URL::makeRelative() normalises to '/'. That matches the homepage entry, and Statamic serves it as 200.
Regression source
Introduced in PR #11840 ("[6.x] Normalize and configure trailing slashes in URLs"), commit 1fbc7f2, first shipped in v6.0.0-alpha.7. That PR refactored Site::absoluteUrl() from request()->getSchemeAndHttpHost() . $url to URL::makeAbsolute($url). The previous form pinned the site base to the request's scheme + host only; the new form delegates to URL::makeAbsolute(), which prepends url()->to('/') — and Laravel's request root includes the base URL (/index.php) on direct front-controller requests, so the site's "absolute URL" is no longer invariant across request entry points.
Workaround:
Create resources/sites.yaml with an explicit absolute URL:
default:
name: "{{ config:app:name }}"
url: "{{ config:app:url }}"
locale: "{{ config:app:locale }}"
This makes Site::url() return an already-absolute URL, so URL::makeAbsolute() short-circuits and never calls getRequestRootUrl(). After applying the fix, the static cache must be cleared (php artisan statamic:static:clear plus rm -rf public/static/*) to evict the poisoned entries.
Suggested fix:
Either default the fallback url in Sites::getFallbackConfig() to config('app.url') instead of '/', or have URL::getRequestRootUrl() strip a trailing /index.php so the site base is invariant to whether the request hit the front controller directly.
How to reproduce
- Install a fresh Statamic 6 site (or use any existing site that doesn't have resources/sites.yaml).
- Ensure APP_URL is set to an absolute URL in .env.
- Confirm resources/sites.yaml does not exist: ls resources/sites.yaml → no such file.
- Start the app (php artisan serve or any webserver that doesn't itself block /index.php).
- Run:
curl -I http://localhost:8000/index.php
- Expected: HTTP/1.1 404 Not Found
Actual: HTTP/1.1 200 OK with the homepage body.
- Inspect the body — every generated link uses /index.php/... as its base.
- Optional: enable full static caching (STATAMIC_STATIC_CACHING_STRATEGY=full), hit /index.php once, and observe that public/static/index.php_.html is created. Every subsequent request to /index.php is now served from disk with the prefix baked in.
Logs
Environment
Environment
Laravel Version: 13.7.0
PHP Version: 8.4.20
Composer Version: 2.9.7
Environment: production
Debug Mode: OFF
Maintenance Mode: OFF
Timezone: UTC
Locale: en
Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: redis
Database: sqlite
Logs: stack / single
Mail: mailgun
Queue: sync
Session: file
Storage
public/storage: NOT LINKED
Statamic
Addons: 0
License Key: Set
Sites: 1
Stache Watcher: Disabled (auto)
Static Caching: full
Version: 6.16.0 PRO
Installation
Fresh statamic/statamic site via CLI
Additional details
No response
Bug description
On Statamic 6 (confirmed on v6.16.0), a site that doesn't have a resources/sites.yaml file will treat a direct request to /index.php as a valid request for the homepage and respond with 200 instead of 404. All URLs generated on that response are prefixed with /index.php/.... When full static caching is enabled, that response is written to disk and served as 200 to every subsequent /index.php request indefinitely.
Impact: crawlers spider the /index.php/{slug} link graph, every cached page on those paths contains the same broken prefix, and the control panel becomes inaccessible via the affected route. Statamic 5 sites correctly return a styled 404 in this scenario.
Root cause appears to be:
Regression source
Introduced in PR #11840 ("[6.x] Normalize and configure trailing slashes in URLs"), commit 1fbc7f2, first shipped in v6.0.0-alpha.7. That PR refactored Site::absoluteUrl() from request()->getSchemeAndHttpHost() . $url to URL::makeAbsolute($url). The previous form pinned the site base to the request's scheme + host only; the new form delegates to URL::makeAbsolute(), which prepends url()->to('/') — and Laravel's request root includes the base URL (/index.php) on direct front-controller requests, so the site's "absolute URL" is no longer invariant across request entry points.
Workaround:
Create resources/sites.yaml with an explicit absolute URL:
This makes Site::url() return an already-absolute URL, so URL::makeAbsolute() short-circuits and never calls getRequestRootUrl(). After applying the fix, the static cache must be cleared (php artisan statamic:static:clear plus rm -rf public/static/*) to evict the poisoned entries.
Suggested fix:
Either default the fallback url in Sites::getFallbackConfig() to config('app.url') instead of '/', or have URL::getRequestRootUrl() strip a trailing /index.php so the site base is invariant to whether the request hit the front controller directly.
How to reproduce
curl -I http://localhost:8000/index.php
Actual: HTTP/1.1 200 OK with the homepage body.
Logs
Environment
Installation
Fresh statamic/statamic site via CLI
Additional details
No response