Introduction
When trying to host Cantaloupe (an IIIF image server) on AWS App Runner with CloudFront placed in front of it, I encountered a problem where all requests returned 404 errors when accessed through CloudFront.
This article records the investigation of the cause, the solutions I tried, and the conclusion.
Environment
- Application: Cantaloupe 5.0.5 (IIIF image server)
- Hosting: AWS App Runner
- CDN: Amazon CloudFront
- Region: ap-northeast-1 (Tokyo)
Problem Overview
Symptoms
| Access Method | Result |
|---|---|
| Direct access to App Runner | 200 OK |
| Access via CloudFront | 404 Not Found |
What Was Confirmed
When 404 was returned via CloudFront, the response header contained server: envoy. This indicates that the request was reaching App Runner’s internal proxy (Envoy).
In other words, the communication from CloudFront to App Runner was successful, but the request was not being forwarded to the application (Cantaloupe) inside App Runner.
Cause
Host Header Issue
Inside App Runner, Envoy operates as a reverse proxy. Envoy routes requests based on the Host header.
Why the Host Header Becomes the CloudFront Domain
When CloudFront forwards requests to the origin (App Runner), by default the viewer’s Host header is forwarded as-is.
Solutions Tried
1. Origin Request Policy “AllViewerExceptHostHeader”
This is the policy recommended by AWS for App Runner. This policy excludes the Host header, and CloudFront should automatically set the origin’s domain name as the Host header.
Result: Failed (still 404)
2. Origin Request Policy “None”
A setting that doesn’t forward any viewer headers.
Result: Failed (still 404)
3. Custom Origin Request Policy
I created a custom policy that explicitly set “Headers: None”.
Result: Failed (still 404)
4. Rewriting Host Header with CloudFront Functions
I tried to rewrite the Host header to the App Runner domain during the viewer request.
Result: Failed
In CloudFront Functions, the Host header is read-only in the viewer request event and cannot be modified.
5. Rewriting Host Header with Lambda@Edge
Lambda@Edge can run on origin request events and can modify more headers.
Result: Failed
The Host header was also read-only in Lambda@Edge origin request events.
6. Creating New App Runner Service and CloudFront Distribution
Considering the possibility that the existing configuration had issues, I created entirely new resources.
- New App Runner service (no custom domain)
- New CloudFront distribution
- Origin request policy “AllViewerExceptHostHeader”
Result: Failed (still 404)
What the Investigation Revealed
App Runner Log Check
Logs when accessing App Runner directly:
When accessing via CloudFront:
- The request was not logged in Cantaloupe’s logs
- In other words, Envoy was returning 404 before forwarding the request to the application
Testing with curl by Changing the Host Header
When accessing App Runner directly, changing the Host header to the CloudFront domain:
The 404 was reproduced. This confirmed that the problem was with the Host header.
Conclusion
Root Cause
The 404 error with the CloudFront + App Runner combination is caused by App Runner’s Envoy routing based on the Host header.
If the “AllViewerExceptHostHeader” policy doesn’t resolve the issue, it is considered to be a problem on the App Runner side.
Recommended Actions
Contact AWS Support
- Report the issue as a 404 error occurring with App Runner + CloudFront
Consider Alternative Architectures
- ECS Fargate + ALB: Proven track record of integration with CloudFront
- EC2 + Nginx: Most flexible configuration
- Cloudflare: Easy Host header rewriting
Reference Information
Articles Reporting Similar Issues
- AWS App Runner - Placing a Cloudfront distribution in front of App Runner instance (Reddit)
- Solved! AWS App Runner and CloudFront 404 Error Problem (Zenn)
These articles report that “AllViewerExceptHostHeader” resolved the issue, but it did not work in this case.
AWS Documentation
Summary
| Method Tried | Result |
|---|---|
| AllViewerExceptHostHeader | Failed |
| Origin request policy “None” | Failed |
| Custom policy (no headers) | Failed |
| CloudFront Functions | Failed (Host is read-only) |
| Lambda@Edge | Failed (Host is read-only) |
| New resource creation | Failed |
Despite all settings being correct, it did not work, suggesting the problem is related to App Runner’s internal behavior. If you are considering the CloudFront + App Runner combination, please keep in mind that you may encounter this issue.