When deploying a Vue.js Single Page Application (SPA) on AWS, developers often encounter a common issue: the 403 access error. This typically happens when refreshing a page or accessing a non-root URL after the application has already loaded. Here’s my experience troubleshooting and resolving this issue using S3 for static website hosting and CloudFront for distribution.
The Problem: 403 Access Error After Refreshing Routes
While working on deploying my Vue.js application on AWS, I stored the bundled JavaScript files in a S3 bucket and used it for static website hosting. I then created a CloudFront distribution and linked it to my S3 bucket.
The default route pointed to the index.html
page, where my app was bootstrapped. Everything worked well when loading the app using the CloudFront domain. However, once the app was loaded, a path would be appended to the domain, like cloud-domain.com/dashboard
.
The problem occurred when I refreshed the /dashboard
route or navigated to another path directly—it returned a 403 Access Denied error.
Why It Happened: CloudFront's Path Request Mechanism
Here’s how CloudFront works: when you request a URL with a path, CloudFront treats it as a request for a specific file or object. It checks whether the file exists in its cache or in the linked S3 bucket. In this case, since my S3 bucket didn’t contain an actual dashboard
file (or any other path-specific file), CloudFront returned the 403 error.
The Solution: Redirect All Requests to index.html
The solution to this issue is straightforward: I needed to redirect all requests to index.html
, as my Vue.js app handles routing internally. After some research, I discovered that I needed to update configurations in both my S3 bucket and CloudFront distribution.
Here’s what I did:
Step 1: Update S3 Bucket Settings
In the S3 bucket, under the Static Website Hosting section, I had already set index.html
as the default document. However, I also needed to set index.html
as the Error Document. This way, when any request came in that didn’t match an existing file (like /dashboard
), it would return index.html
instead of throwing a 403 error.
Here’s how the S3 settings looked after I made the change:
Step 2: Configure CloudFront Custom Error Responses
Next, I needed to configure my CloudFront distribution to handle these requests properly. In the Error Pages section of CloudFront, I created a custom error response for 403 errors.
- I set the response code to
200
(OK). - I specified
/index.html
as the page to serve in the case of a 403 error. - This ensured that when CloudFront couldn’t find the requested path (like
/dashboard
), it would serve theindex.html
file instead.
Here’s how I configured CloudFront:
Final Configuration in CloudFront:
After configuring these settings, the issue was resolved. Now, any request to a specific path is properly redirected to the index.html
page, allowing Vue.js to handle the routing internally.
Voila! Issue Resolved
By making these two key changes—updating the S3 error document settings and configuring CloudFront's custom error responses—the 403 error was eliminated. Now, my application routes work perfectly, even when refreshing or navigating directly to different URLs.
Conclusion
Deploying a Vue.js Single Page Application on AWS using S3 and CloudFront can result in 403 errors when refreshing or navigating to non-root URLs. However, by redirecting all paths to the index.html
page, you allow Vue.js to manage routing internally, solving the issue.
Top comments (1)
This article is great! It helped me understand why this happens, and the solution here was also what AWS recommended when I faced a similar issue.
The only annoying thing is that I configured both S3 and an ALB as my two Cloudfront origins, and the error pages can not be limited per origin. I could not allow a 403 response code from my ALB to get redirected to 200 and return the index.html :(