Description
The <*> → /index.html (200) redirect rule — which Amplify's own "Single page app" wizard inserts — intercepts requests for deployed static files (e.g. /assets/index-[hash].js) and returns index.html with content-type: text/html. CloudFront then caches this HTML response for the asset URL.
This contradicts the documented behaviour:
"Static files in the deployment are always served before custom rules are applied. When a path matches a static file in the deployment, the custom rule is not applied to that path."
— Amplify Hosting Redirects and Rewrites docs
Environment
- Amplify Hosting platform: WEB
- Region: eu-west-2
- Deployment type: Amplify Gen 2 (Amplify Console, static SPA)
- Frontend framework: Vite + Vue 3
Steps to reproduce
- Deploy a Vite/Vue (or any SPA) to Amplify Hosting
- In the Amplify Console → App settings → Rewrites and redirects, use the "Single page app" preset — this inserts
source: <*>, target: /index.html, status: 200
- Run:
curl -sI https://<your-app>.amplifyapp.com/assets/index-[hash].js
Expected behaviour
The static file /assets/index-[hash].js is served from S3 with content-type: text/javascript, consistent with the documented static-files-first priority.
Actual behaviour
HTTP/2 200
content-type: text/html
content-length: 1188 ← size of index.html, not the JS bundle
x-cache: Hit from cloudfront
server: AmazonS3
The response body is index.html. The JS file is in the S3 deployment but the <*> rule intercepts the request before S3 is consulted. CloudFront then caches this text/html response against the asset URL with s-maxage=31536000 (1 year).
Impact
The browser throws:
Failed to load module script: Expected a JavaScript-or-Wasm module script
but the server responded with a MIME type of "text/html". Strict MIME type
checking is enforced for module scripts per HTML spec.
The application is completely broken in production for all users. The failure mode is especially hard to diagnose because it returns HTTP 200 with wrong content rather than a 404, and the Amplify Console deployment status shows SUCCEED throughout.
Why this is particularly problematic
Amplify creates the broken rule itself via the "Single page app" wizard. Users following Amplify's own guidance end up with an app that cannot load JavaScript in production, with no warning in the console.
Workaround
Replace <*> with a regex that excludes paths containing known static file extensions:
</^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json|webp|webmanifest)$)([^.]+$)/>
This can be applied via the AWS CLI:
aws amplify update-app \
--app-id <your-app-id> \
--region <your-region> \
--custom-rules '[{
"source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json|webp|webmanifest)$)([^.]+$)/>",
"target": "/index.html",
"status": "200"
}]'
Suspected root cause
The rule evaluation does not honour the documented "static files first" priority when the catch-all <*> pattern is used. The pattern matches and short-circuits before the S3 origin is consulted, regardless of whether the requested file exists in the deployment.
Suggested fixes
- Fix the implementation so that
<*> (and all redirect rules) genuinely evaluate after static file lookup, as documented.
- Fix the wizard — either replace the
<*> preset with the safe regex, or add a warning that <*> may intercept static asset requests.
- Update the docs to clarify the actual evaluation order, or remove the claim that static files always take priority.
Description
The
<*>→/index.html(200) redirect rule — which Amplify's own "Single page app" wizard inserts — intercepts requests for deployed static files (e.g./assets/index-[hash].js) and returnsindex.htmlwithcontent-type: text/html. CloudFront then caches this HTML response for the asset URL.This contradicts the documented behaviour:
Environment
Steps to reproduce
source: <*>,target: /index.html,status: 200Expected behaviour
The static file
/assets/index-[hash].jsis served from S3 withcontent-type: text/javascript, consistent with the documented static-files-first priority.Actual behaviour
The response body is
index.html. The JS file is in the S3 deployment but the<*>rule intercepts the request before S3 is consulted. CloudFront then caches thistext/htmlresponse against the asset URL withs-maxage=31536000(1 year).Impact
The browser throws:
The application is completely broken in production for all users. The failure mode is especially hard to diagnose because it returns HTTP 200 with wrong content rather than a 404, and the Amplify Console deployment status shows SUCCEED throughout.
Why this is particularly problematic
Amplify creates the broken rule itself via the "Single page app" wizard. Users following Amplify's own guidance end up with an app that cannot load JavaScript in production, with no warning in the console.
Workaround
Replace
<*>with a regex that excludes paths containing known static file extensions:This can be applied via the AWS CLI:
Suspected root cause
The rule evaluation does not honour the documented "static files first" priority when the catch-all
<*>pattern is used. The pattern matches and short-circuits before the S3 origin is consulted, regardless of whether the requested file exists in the deployment.Suggested fixes
<*>(and all redirect rules) genuinely evaluate after static file lookup, as documented.<*>preset with the safe regex, or add a warning that<*>may intercept static asset requests.