CloudFront 404 Error Page Configuration
Problem
When accessing a non-existent URL like https://docs.sendsquared.com/docs/bad-url
, CloudFront returns an ugly XML error or default error page instead of the Docusaurus-generated 404.html page.
Solution
Step 1: Configure S3 Static Website Hosting
- Go to your S3 bucket in AWS Console
- Navigate to Properties tab
- Scroll to Static website hosting
- Click Edit
- Configure:
- Static website hosting: Enable
- Hosting type: Host a static website
- Index document:
index.html
- Error document:
404.html
- Click Save changes
Step 2: Configure CloudFront Error Pages
- Go to CloudFront in AWS Console
- Select your distribution
- Navigate to Error pages tab
- Click Create custom error response
For 404 Errors:
- HTTP error code:
404: Not Found
- Customize error response: Yes
- Response page path:
/404.html
- HTTP response code:
404: Not Found
- Error caching minimum TTL:
10
(seconds)
For 403 Errors (Important for SPAs):
- HTTP error code:
403: Forbidden
- Customize error response: Yes
- Response page path:
/404.html
- HTTP response code:
404: Not Found
- Error caching minimum TTL:
10
(seconds)
Step 3: Invalidate CloudFront Cache
After making changes:
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/*"
Or in AWS Console:
- Select your distribution
- Go to Invalidations tab
- Click Create invalidation
- Enter
/*
in the object paths - Click Create invalidation
Alternative: Using CloudFront Functions
For more advanced routing, you can use CloudFront Functions:
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check if the URI is for a docs page that doesn't have an extension
if (uri.startsWith('/docs/') && !uri.includes('.')) {
// Try to append .html
request.uri = uri + '.html';
}
return request;
}
Testing
After configuration, test these scenarios:
- Valid page:
https://docs.sendsquared.com/docs/getting-started
✅ - Invalid page:
https://docs.sendsquared.com/docs/bad-url
→ Should show custom 404 - Root 404:
https://docs.sendsquared.com/non-existent
→ Should show custom 404 - Deep 404:
https://docs.sendsquared.com/docs/category/fake/page
→ Should show custom 404
Troubleshooting
404 Page Not Showing
-
Verify 404.html exists in S3:
aws s3 ls s3://your-bucket/404.html
-
Check CloudFront Origin Settings:
- Origin should point to S3 bucket website endpoint, not the S3 API endpoint
- Example:
your-bucket.s3-website-us-east-1.amazonaws.com
- NOT:
your-bucket.s3.amazonaws.com
-
Clear Browser Cache:
- Hard refresh:
Cmd+Shift+R
(Mac) orCtrl+Shift+R
(Windows) - Or test in incognito/private mode
- Hard refresh:
Still Seeing XML Errors
If you're still seeing XML errors like:
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
</Error>
This means CloudFront is hitting S3's REST API directly. Solutions:
-
Use S3 Website Endpoint as Origin:
- Change origin from
bucket.s3.amazonaws.com
- To:
bucket.s3-website-region.amazonaws.com
- Change origin from
-
Or Use Origin Access Identity (OAI) with Lambda@Edge:
- Keep S3 private
- Use Lambda@Edge to handle 404s
- More complex but more secure
Docusaurus-Specific Considerations
Build Configuration
Ensure your docusaurus.config.js
includes:
module.exports = {
// ... other config
onBrokenLinks: 'warn', // or 'ignore' for production
onBrokenMarkdownLinks: 'warn',
// Ensure trailing slashes are handled consistently
trailingSlash: false, // or true, but be consistent
};
Custom 404 Page (Optional)
While Docusaurus provides a default 404, you can customize it:
- Create
src/theme/NotFound.js
:
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function NotFound() {
return (
<Layout title="Page Not Found">
<main className="container margin-vert--lg">
<div className="row">
<div className="col col--6 col--offset-3">
<h1 className="hero__title">404 - Page Not Found</h1>
<p>We couldn't find what you're looking for.</p>
<p>
<Link to="/">Return to homepage</Link>
</p>
</div>
</div>
</main>
</Layout>
);
}
- Build and deploy:
npm run build
aws s3 sync ./build s3://your-bucket --delete
aws cloudfront create-invalidation --distribution-id YOUR_ID --paths "/*"