aws-amplify / aws-amplify/amplify-hosting

SPA redirect rule <*> intercepts static asset requests despite docs claiming static files take priority

Open
#4,099 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dockerfile
Stars
481
Forks
123
PR merge metrics
No merged PRs in 30d

Description

## 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](https://docs.aws.amazon.com/amplify/latest/userguide/redirects.html)

## 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

1. Deploy a Vite/Vue (or any SPA) to Amplify Hosting
2. In the Amplify Console → App settings → Rewrites and redirects, use the **\"Single page app\"** preset — this inserts `source: <*>`, `target: /index.html`, `status: 200`
3. Run:
```
curl -sI https://.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:

```

```

This can be applied via the AWS CLI:

```bash
aws amplify update-app \
--app-id \
--region \
--custom-rules '[{
"source": "",
"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

1. **Fix the implementation** so that `<*>` (and all redirect rules) genuinely evaluate after static file lookup, as documented.
2. **Fix the wizard** — either replace the `<*>` preset with the safe regex, or add a warning that `<*>` may intercept static asset requests.
3. **Update the docs** to clarify the actual evaluation order, or remove the claim that static files always take priority.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the documented behavior with the provided curl request against an Amplify-hosted SPA, then compare the Single page app wizard rule with the redirects documentation. Done means deployed static assets are served with their correct content type instead of index.html, while SPA routes still resolve to /index.html.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, javascript, vite
Domain
cloud, devops, web-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.