Git & deploy·5 min read

Deploying a static site or Next.js with git on DirectAdmin

Your site lives in a repository and has to go live on your hosting. The three approaches that work, what you give up with a static export of Next.js, and the pitfall with deleted files.

Connect the repository of my website to this domain, put the built folder
in the web root and make sure it updates itself after every push.

You build a site with Next.js, Astro, Eleventy or plain Vite, and the result is a folder with HTML, CSS and JavaScript. That folder has to end up on your hosting. The question is not whether that works, but what exactly you put in your repository and who does the building.

The choice that decides everything

A shared hosting environment does not run a build step and does not run your project as a Node server. It serves files. That means the built files have to come from somewhere. There are three approaches.

Approach What you connect For whom
Build on your own machine and commit the output The folder with the built site, to the web root A site you update yourself, one person
Build in a job at your git provider and push the output to a separate branch That branch, to the web root A team, or when you do not want built files in your main branch
Put the source outside the website and build and run it on your own server The source code, outside the web root A project that genuinely needs a Node server

The first is honestly the quickest way to start. The second is where you grow to as soon as more than one person pushes. How to make the connection itself is in connecting a GitHub repository to your website; the automatic update after every push you switch on with a webhook.

Next.js as a static export

Next.js can write its whole site out as separate HTML files. You set this in next.config.ts:

const nextConfig = {
  output: "export",
  trailingSlash: true,
};

After next build the result sits in the out folder. That folder is what you connect to the web root.

trailingSlash: true is not a detail. Without that line Next writes a file about.html for the route /about, and then your web server has to know that it should pick up that file for an address without .html. With that line you get about/index.html, and every web server serves that without any further configuration.

What you give up with an export

This is the part that surprises people. A static export has no server, so everything that has to decide something per visitor falls away:

  • Route handlers that do something with the request, and server actions.
  • Reading cookies on the server.
  • redirects, rewrites and headers from your configuration. You arrange those in the web server, in .htaccess for instance.
  • Image optimization with the default loader of next/image. You plug in your own loader, or you optimize your images up front.
  • Dynamic routes without generateStaticParams, and anything that has to be regenerated after the build.

For a company site, a portfolio or a knowledge base that is no problem: those already know at build time what is on every page. If you have a login screen, a search function that needs the server or a page that differs per visitor, a static export is not the right choice and you want a server where Node runs as a service.

Two settings that make the difference

Cache. The files under _next/static have a hash in their name: change the contents and the name changes. So you can cache those for a long time. Your HTML you cannot, because it has to be fresh right after a new deploy. You set that distinction in .htaccess, or you let the assistant do it.

Your 404. A static site has no server picking a tidy error page. Point .htaccess at the page Next built for you:

ErrorDocument 404 /404/index.html

The pitfall: deleted files stay

A deploy fetches the new version and puts it down. What it does not do is clean up. Take a page out of your site and the old file stays on your hosting, and that page stays reachable.

On a handmade site you notice that. With a static export you do not, because it is dozens of files with names you never typed yourself. The result is a site where an old page is still alive and still found by Google.

Two ways to solve it. Have your build job empty the target folder first, or clean up by hand after a deploy in which you removed something. Just ask the assistant:

Compare the files in the web root with the files in the latest deploy.
Name what is in the web root but not in the repository, and show it to me
before you delete anything.

Common mistakes

"I put my whole project in public_html." Then your package.json, your configuration and possibly a .env end up in the web root. Pick outside the website as the target for source code and connect only the built folder to the web root.

"My .git folder is public now." It is not: the web server blocks that folder. But a .env or a backup file in the web root is public, so keep an eye on what you commit.

"I build locally and push, but the site does not change." Check that you committed the folder with the built site and did not accidentally exclude it in .gitignore. An empty deploy is almost always an ignored folder.

"I use next start on my hosting plan." That is not shared hosting but a server. On your own server it works, as a service with a restart policy, never as a loose command you start in a terminal.

"After a deploy I still see the old page." That is cache, not the deploy. Clear your site's cache and reload without cache in your browser before you go digging through files.

MK
Maarten Keizer

Founder of Invoker. Over twenty years in hosting, system administration and web development; builds the Claude hosting himself and tests everything on our own servers first.

about maarten