It seems like you're on the right track, but there are a couple of things worth refining for better maintainability and to avoid unnecessary complexity.
Bookmarks and Cache-Control: Yes, you're correct—browsers like Chrome and Edge can sometimes serve an outdated index.html from the cache if the proper Cache-Control headers are not set. This is especially problematic with SPAs where the HTML can change, but assets like JS/CSS are cached with long expiry times.
Error Response Handling: The custom error response (403 → 200) can indeed affect caching behavior if you're not controlling the headers explicitly in CloudFront. Since you're using Lambda@Edge, consider placing the Cache-Control header logic in the origin (for index.html) or Lambda functions for both viewer request and response to ensure that index.html is always revalidated while other assets can be cached longer.
Best Caching Strategy: For index.html, the recommended approach is to always set Cache-Control: no-cache, must-revalidate to ensure browsers always check for updates. For static assets, version your files (e.g., main.abc123.js) and use Cache-Control: public, max-age=31536000, immutable for long-term caching. You can automate invalidation with CloudFront when index.html changes to prevent serving stale content.
A more robust approach would be:
Use CloudFront to manage caching as you have, but ensure that the specific headers are set for each file type (HTML vs assets). Utilize Lambda@Edge for cache control logic specifically for index.html and assets, but try to avoid the complexity of custom error handling unless necessary.