Generally speaking, Heroku is not the best platform to host static files. It's much better to host them on a CDN instead. But it depends what we mean by static files.
If what we want to achieve is to deploy a JavaScript frontend app, like a React or Angular, then it may still be a good idea to use Heroku because it provides good tools to build and deploy this app. It also simplifies the continuous delivery process and, let's not forget: free dynos can be great for non-prod environments.
Solution: Node.js & Express
Heroku supports Nodejs and Express is a great facility to serve our static files. We can write a server app quickly using Express to serve our files:
const express = require('express');
const serveStatic = require('serve-static');
const compression = require('compression');
// the port is arbitrary in Heroku and supplied via
// environment variable PORT, so we need to read it and use it
const port = process.env.PORT || 3000;
const app = express();
app.use(compression());
// default to .html (you can omit the extension in the URL)
// serves the static files from the dist folder
app.use(serveStatic(`${__dirname}/dist`, {
'extensions': ['html'],
etag: true
}));
app.listen(port, () => {
console.log(`Server running on port ${port}...`);
});
To ensure the server runs when deployed to Heroku, save the code in file called server.js
and add this to your package.json
"scripts": {
...
"start": "node server.js"
...
}
So when Heroku deploys our code, it detects it's a NodeJs application and executes npm install
.
This will build the web application and put the code in the dist
folder. After that, Heroku
will run npm start
and this will simple execute the code in server.js
file.
Push state (for deep links)
Some js web frameworks have routing facilities. Some even offer the possiblity to use the routes as browser urls, but this requires push state support from the server.
Push state is a fancy term to describe that the server will return the index.html
file
regardless of which html file is requested by the browser.
We can amend server.js
to add support for this simply by adding this
(right before the app.listen(port, () => {
line):
// always return index.html to support push state
app.get('*', function(req, res){
res.sendFile(`${__dirname}/dist/index.html`);
});