Routing for a Static Exported Next JS site on S3 and Cloudfront

Viewed 656

I'm using Next JS to export a static HTML site that is uploaded on S3 and hosted on Cloudfront. I don't want to upload it onto a node server and just want it fully static. I don't know if I'm doing something incorrectly or missing out on something but the hosted site on cloudfront does not seem to have any routes. Hence, although I can access the site on the main page (www.mysite.com), if I try to reach a relative path (www.mysite.com/page1), I can't reach it unless I do (www.mysite.com/page1.html). That's when I realized that Cloudfront just points to the S3 bucket and does not have any way to generate routes. Is there a good way to provide Cloudfront with those routes?

I've looked online for solutions and tried the below: -Implemented exportPathMaps: I might have done this incorrectly. An example would help if you have one -turned on trailingSlash -used Lambda edge to reroute the user to www.mysite.com/page1.html while preserving the URL to www.mysite.com/page1. <- This method is too costly as it used lambda so I don't like it

For now, I have a redirect script that does what the lambda edge function does (append the .HTML onto the request) but I'm sure there is a better way to do this.

2 Answers

You can use a lambda trigger to copy any new file in the s3 bucket which has a key like key/index.html as key or key/. This is what I use :

exports.handler = (event, context, callback) => {
    var AWS = require('aws-sdk');
    var s3 = new AWS.S3();
    var params = {
        Bucket: event.Records[0].s3.bucket.name,
        CopySource: "/" + event.Records[0].s3.bucket.name + "/" + event.Records[0].s3.object.key,
        Key: event.Records[0].s3.object.key.replace(/index.html$/g, '')
    };
    s3.copyObject(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);
        }
    });
    
    var params2 = {
        Bucket: event.Records[0].s3.bucket.name,
        CopySource: "/" + event.Records[0].s3.bucket.name + "/" + event.Records[0].s3.object.key,
        Key: event.Records[0].s3.object.key.replace(/\/index.html$/g, '')
    };
    s3.copyObject(params2, function(err, data) {
        if (err) {
            console.log(err, err.stack);
        }
    });
    
    
};

What we found is nextjs router pathname is not populated with the expected route on first hit if you use next export. Until this issue is fixed within nextjs, you can use a provider in _app.js that wraps your components and adjusts the route or put this before the return statement inside you _app.js default function:

import { useRouter } from 'next/router'

const { asPath, push, pathname } = useRouter()
if (asPath.split('?')[0] != pathname.split('?')[0] && !pathname.includes('[')) {
    // Work around for next export breaking SPA routing on first hit
    console.log('Browser route ' + asPath + ' did not match nextjs router route ' + pathname)
    push(asPath)
    return <div>Loading...</div>
}

Related